/*
 * @(#)PlotMark.java    2.6.0   13 September 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 java.awt.*;
import java.awt.geom.*;
import java.io.*;

/**
 * <p>Class <code>PlotMark</code> is a class for drawing a geometric
 * shape or mark at a specified point in a graphics context or for
 * painting a <code>Paintable</code> at such a point.</p>
 *
 * <p>In 2.4.0, the class was completely refactored to use the class
 * <code>PlotMarkAlgorithm</code> so that it is possible for the
 * user to define a new plot mark.</code>
 *
 * <p>In 2.4.0, the alternative to use a <code>Paintable</code> as a
 * mark was also introduced.</p>
 *
 * <p>When a plot mark is defined via a plot mark algorithm then the
 * paint used for drawing is that determined by the graphics context.
 * When a plot mark is defined by a paintable, then the paintable
 * controls the entire drawing process.</p>
 *
 * <p>In 2.4.0, it was decided to remove options that would permit a
 * <code>PlotMark</code> to be modified after construction. In other
 * words, a <code>PlotMark</code> is immutable.  Instead, there are
 * now constructors available that permit a <code>PlotMark</code> to
 * be fully or partially cloned.</p>
 *
 * <p>This class provides several static instances of itself. The
 * constant names are chosen to promote compatibility with earlier
 * versions of the class although the actual objects referenced by
 * these constants are quite different.</p>
 * 
 * <p>In 2.6.0, the <code>mark</code> method was changed to always
 * stroke the boundary of a plot mark defined by a plot mark algorithm
 * whether or not the internal <code>fill</code> is set to true.  This
 * was done since a pure fill may draw an asymmetric boundary. As a
 * consequence of this decision, a mark has the same boundary whether
 * filled or unfilled.  Also, anti-aliasing is turned off when using a
 * plot mark algorithm because that will destroy the desired symmetry.</p>
 * 
 * <p>Also, in 2.6.0, we cache the <code>Shape</code> that corresponds
 * to the internal <code>PlotMarkAlgorithm</code>, the size, and the
 * standard location (0,0).  This makes painting a bit faster.</p>
 * 
 * <p>Finally, in 2.6.0, we allow the plot mark size to be as small as
 * 0 to enable the base case for certain plot mark shapes.</p>
 * 
 * @author  Richard Rasala
 * @version 2.6.0
 * @since   1.0
 */
public class PlotMark 
{
    /** The horizontal bar plot mark. */
    public static final PlotMark H_BAR =
        new PlotMark(PlotMarkAlgorithm.HBar, false);
    
    /** The vertical bar plot mark. */
    public static final PlotMark V_BAR =
        new PlotMark(PlotMarkAlgorithm.VBar, false);
    
    /** The plus plot mark. */
    public static final PlotMark PLUS =
        new PlotMark(PlotMarkAlgorithm.Plus, false);
    
    /** The cross plot mark. */
    public static final PlotMark CROSS =
        new PlotMark(PlotMarkAlgorithm.Cross, false);
    
    /** The asterisk plot mark. */
    public static final PlotMark ASTERISK =
        new PlotMark(PlotMarkAlgorithm.Asterisk, false);
    

    /** The square plot mark. */
    public static final PlotMark SQUARE =
        new PlotMark(PlotMarkAlgorithm.Square, false);
    
    /** The diamond plot mark. */
    public static final PlotMark DIAMOND =
        new PlotMark(PlotMarkAlgorithm.Diamond, false);
    
    
    /**
     * <p>The circle plot mark.</p>
     * 
     * <p>See comments in <code>PlotMarkAlgorithm.BetterCircle</code>.</p>
     */
    public static final PlotMark CIRCLE =
        new PlotMark(PlotMarkAlgorithm.BetterCircle, false);
    
    
    /** The wedge-facing-north plot mark. */
    public static final PlotMark WEDGE_N =
        new PlotMark(PlotMarkAlgorithm.WedgeN, false);
    
    /** The wedge-facing-east plot mark. */
    public static final PlotMark WEDGE_E =
        new PlotMark(PlotMarkAlgorithm.WedgeE, false);
    
    /** The wedge-facing-south plot mark. */
    public static final PlotMark WEDGE_S =
        new PlotMark(PlotMarkAlgorithm.WedgeS, false);
    
    /** The wedge-facing-west plot mark. */
    public static final PlotMark WEDGE_W =
        new PlotMark(PlotMarkAlgorithm.WedgeW, false);
    
    
    /** The blunt-wedge-facing-north plot mark. */
    public static final PlotMark BLUNT_WEDGE_N =
        new PlotMark(PlotMarkAlgorithm.BluntWedgeN, false);
    
    /** The blunt-wedge-facing-east plot mark. */
    public static final PlotMark BLUNT_WEDGE_E =
        new PlotMark(PlotMarkAlgorithm.BluntWedgeE, false);
    
    /** The blunt-wedge-facing-south plot mark. */
    public static final PlotMark BLUNT_WEDGE_S =
        new PlotMark(PlotMarkAlgorithm.BluntWedgeS, false);
    
    /** The blunt-wedge-facing-west plot mark. */
    public static final PlotMark BLUNT_WEDGE_W =
        new PlotMark(PlotMarkAlgorithm.BluntWedgeW, false);
    
    
    /** The sharp-wedge-facing-north plot mark. */
    public static final PlotMark SHARP_WEDGE_N =
        new PlotMark(PlotMarkAlgorithm.SharpWedgeN, false);
    
    /** The sharp-wedge-facing-east plot mark. */
    public static final PlotMark SHARP_WEDGE_E =
        new PlotMark(PlotMarkAlgorithm.SharpWedgeE, false);
    
    /** The sharp-wedge-facing-south plot mark. */
    public static final PlotMark SHARP_WEDGE_S =
        new PlotMark(PlotMarkAlgorithm.SharpWedgeS, false);
    
    /** The sharp-wedge-facing-west plot mark. */
    public static final PlotMark SHARP_WEDGE_W =
        new PlotMark(PlotMarkAlgorithm.SharpWedgeW, false);
    
    
    /** The thumb-facing-north plot mark. */
    public static final PlotMark THUMB_N =
        new PlotMark(PlotMarkAlgorithm.ThumbN, false);
    
    /** The thumb-facing-east plot mark. */
    public static final PlotMark THUMB_E =
        new PlotMark(PlotMarkAlgorithm.ThumbE, false);
    
    /** The thumb-facing-south plot mark. */
    public static final PlotMark THUMB_S =
        new PlotMark(PlotMarkAlgorithm.ThumbS, false);
    
    /** The thumb-facing-west plot mark. */
    public static final PlotMark THUMB_W =
        new PlotMark(PlotMarkAlgorithm.ThumbW, false);
    
    
    /** The filled square plot mark. */
    public static final PlotMark FILLED_SQUARE =
        new PlotMark(PlotMarkAlgorithm.Square, true);
    
    /** The filled diamond plot mark. */
    public static final PlotMark FILLED_DIAMOND =
        new PlotMark(PlotMarkAlgorithm.Diamond, true);
    
    
    /**
     * <p>The filled circle plot mark.</p>
     * 
     * <p>See comments in <code>PlotMarkAlgorithm.BetterCircle</code>.</p>
     */
    public static final PlotMark FILLED_CIRCLE =
        new PlotMark(PlotMarkAlgorithm.BetterCircle, true);
    
    
    /** The filled wedge-facing-north plot mark. */
    public static final PlotMark FILLED_WEDGE_N =
        new PlotMark(PlotMarkAlgorithm.WedgeN, true);
    
    /** The filled wedge-facing-east plot mark. */
    public static final PlotMark FILLED_WEDGE_E =
        new PlotMark(PlotMarkAlgorithm.WedgeE, true);
    
    /** The filled wedge-facing-south plot mark. */
    public static final PlotMark FILLED_WEDGE_S =
        new PlotMark(PlotMarkAlgorithm.WedgeS, true);
    
    /** The filled wedge-facing-west plot mark. */
    public static final PlotMark FILLED_WEDGE_W =
        new PlotMark(PlotMarkAlgorithm.WedgeW, true);
    
    
    /** The filled blunt-wedge-facing-north plot mark. */
    public static final PlotMark FILLED_BLUNT_WEDGE_N =
        new PlotMark(PlotMarkAlgorithm.BluntWedgeN, true);
    
    /** The filled blunt-wedge-facing-east plot mark. */
    public static final PlotMark FILLED_BLUNT_WEDGE_E =
        new PlotMark(PlotMarkAlgorithm.BluntWedgeE, true);
    
    /** The filled blunt-wedge-facing-south plot mark. */
    public static final PlotMark FILLED_BLUNT_WEDGE_S =
        new PlotMark(PlotMarkAlgorithm.BluntWedgeS, true);
    
    /** The filled blunt-wedge-facing-west plot mark. */
    public static final PlotMark FILLED_BLUNT_WEDGE_W =
        new PlotMark(PlotMarkAlgorithm.BluntWedgeW, true);
    
    
    /** The filled sharp-wedge-facing-north plot mark. */
    public static final PlotMark FILLED_SHARP_WEDGE_N =
        new PlotMark(PlotMarkAlgorithm.SharpWedgeN, true);
    
    /** The filled sharp-wedge-facing-east plot mark. */
    public static final PlotMark FILLED_SHARP_WEDGE_E =
        new PlotMark(PlotMarkAlgorithm.SharpWedgeE, true);
    
    /** The filled sharp-wedge-facing-south plot mark. */
    public static final PlotMark FILLED_SHARP_WEDGE_S =
        new PlotMark(PlotMarkAlgorithm.SharpWedgeS, true);
    
    /** The filled sharp-wedge-facing-west plot mark. */
    public static final PlotMark FILLED_SHARP_WEDGE_W =
        new PlotMark(PlotMarkAlgorithm.SharpWedgeW, true);
    
    
    /** The filled thumb-facing-north plot mark. */
    public static final PlotMark FILLED_THUMB_N =
        new PlotMark(PlotMarkAlgorithm.ThumbN, true);
    
    /** The filled thumb-facing-east plot mark. */
    public static final PlotMark FILLED_THUMB_E =
        new PlotMark(PlotMarkAlgorithm.ThumbE, true);
    
    /** The filled thumb-facing-south plot mark. */
    public static final PlotMark FILLED_THUMB_S =
        new PlotMark(PlotMarkAlgorithm.ThumbS, true);
    
    /** The filled thumb-facing-west plot mark. */
    public static final PlotMark FILLED_THUMB_W =
        new PlotMark(PlotMarkAlgorithm.ThumbW, true);
    
    
    // private constants
    
    /** Default plot mark algorithm: <code>PlotMarkAlgorithm.Square</code>. */
    private static final PlotMarkAlgorithm DEFAULT_ALGORITHM =
        PlotMarkAlgorithm.Square;
    
    /** Default plot mark size: <code>3</code>. */
    private static final int DEFAULT_SIZE = 3;

    /** Default plot mark fill: <code>true</code>. */
    private static final boolean DEFAULT_FILL = true;
    
    /** The standard stroke: <code>BasicStroke(1)</code>. */
    private static final Stroke standardStroke = new BasicStroke(1);
    
    
    // member data
    
    /** The plot mark algorithm. */
    private PlotMarkAlgorithm algorithm = DEFAULT_ALGORITHM;
    
    /** The plot mark size. */
    private int size = DEFAULT_SIZE;
    
    /** The plot mark fill. */
    private boolean fill = DEFAULT_FILL;
    
    /** The plot mark shape. */
    private Shape shape = null;
    
    /** The paintable if used instead of an algorithm. */
    private Paintable paintable = null;
    

    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * <p>Constructs a plot mark using the defaults.</p>
     *
     * <ul>
     *   <li><pre>Algorithm: PlotMarkAlgorithm.Square</pre></li>
     *   <li><pre>Size:      3</pre></li>
     *   <li><pre>Fill:      true</pre></li>
     * </ul>
     *
     * <p>The default plot mark is a copy of the static constant
     * plot mark <code>FILLED_SQUARE</code>.</p>
     */
    public PlotMark() {}
    
    
    /**
     * <p>Constructs a plot mark using the given algorithm and
     * the defaults listed below.</p>
     *
     * <ul>
     *   <li><pre>Size:      3</pre></li>
     *   <li><pre>Fill:      true</pre></li>
     * </ul>
     *
     * <p>If the algorithm is <code>null</code>, it is set to
     * <code>PlotMarkAlgorithm.Square</code>.</p>
     *
     * @param algorithm the plot mark algorithm
     */
    public PlotMark(PlotMarkAlgorithm algorithm) { 
        initializePlotMark(
            algorithm,
            DEFAULT_SIZE,
            DEFAULT_FILL,
            null);
    }
    
    
    /**
     * <p>Constructs a plot mark using the given algorithm, the
     * given size, and the default listed below.</p>
     *
     * <ul>
     *   <li><pre>Fill:      true</pre></li>
     * </ul>
     *
     * <p>If the algorithm is <code>null</code>, it is set to
     * <code>PlotMarkAlgorithm.Square</code>.</p>
     *
     * <p>If the size is less than 0, it is set to 0.</p>
     *
     * @param algorithm the plot mark algorithm
     * @param size the plot mark size setting
     */
    public PlotMark(PlotMarkAlgorithm algorithm, int size) { 
        initializePlotMark(
            algorithm,
            size,
            DEFAULT_FILL,
            null);
    }
    
    
    
    /**
     * <p>Constructs a plot mark using the given algorithm, the
     * given fill setting, and the default listed below.</p>
     *
     * <ul>
     *   <li><pre>Size:      3</pre></li>
     * </ul>
     *
     * <p>If the algorithm is <code>null</code>, it is set to
     * <code>PlotMarkAlgorithm.Square</code>.</p>
     *
     * @param algorithm the plot mark algorithm
     * @param fill the plot mark fill setting
     */
    public PlotMark(PlotMarkAlgorithm algorithm, boolean fill) { 
        initializePlotMark(
            algorithm,
            DEFAULT_SIZE,
            fill,
            null);
    }
    
    
    
    /**
     * <p>Constructs a plot mark using the given algorithm, the
     * given size, and the given fill setting.</p>
     *
     * <p>If the algorithm is <code>null</code>, it is set to
     * <code>PlotMarkAlgorithm.Square</code>.</p>
     *
     * <p>If the size is less than 0, it is set to 0.</p>
     *
     * @param algorithm the plot mark algorithm
     * @param size the plot mark size setting
     * @param fill the plot mark fill setting
     */
    public PlotMark(PlotMarkAlgorithm algorithm, int size, boolean fill) { 
        initializePlotMark(
            algorithm,
            size,
            fill,
            null);
    }
    
    
    /**
     * <p>Constructs a plot mark by cloning the given plot mark.</p>
     *
     * <p>If the plot mark is <code>null</code>, it is set to
     * <code>PlotMark.FILLED_SQUARE</code>.</p>
     *
     * <p>If the plot mark is set by a paintable then the new
     * plot mark is set by the same paintable.</p>
     *
     * @param plotmark the plot mark to clone
     */
    public PlotMark(PlotMark plotmark) { 
        if (plotmark == null)
            plotmark = FILLED_SQUARE;
        
        initializePlotMark
            (plotmark.algorithm, plotmark.size, plotmark.fill, plotmark.paintable);
    }
    
    
    
    /**
     * <p>Constructs a plot mark using the given plot mark but
     * changing the specified setting.</p>
     *
     * <p>If the plot mark is <code>null</code>, it is set to
     * <code>PlotMark.FILLED_SQUARE</code>.</p>
     *
     * <p>If the plot mark is set by a paintable then the new
     * plot mark is set by the same paintable.</p>
     *
     * <p>If the size is less than 0, it is set to 0.</p>
     *
     * @param plotmark the plot mark to partially clone
     * @param size the plot mark size setting
     */
    public PlotMark(PlotMark plotmark, int size) { 
        if (plotmark == null)
            plotmark = FILLED_SQUARE;
        
        initializePlotMark
            (plotmark.algorithm, size, plotmark.fill, plotmark.paintable);
    }
    
    
    
    /**
     * <p>Constructs a plot mark using the given plot mark but
     * changing the specified setting.</p>
     *
     * <p>If the plot mark is <code>null</code>, it is set to
     * <code>PlotMark.FILLED_SQUARE</code>.</p>
     *
     * <p>If the plot mark is set by a paintable then the new
     * plot mark is set by the same paintable.</p>
     *
     * @param plotmark the plot mark to partially clone
     * @param fill the plot mark fill setting
     */
    public PlotMark(PlotMark plotmark, boolean fill) { 
        if (plotmark == null)
            plotmark = FILLED_SQUARE;
        
        initializePlotMark
            (plotmark.algorithm, plotmark.size, fill, plotmark.paintable);
    }
    
    
    
    /**
     * <p>Constructs a plot mark using the given plot mark but
     * changing the specified settings.</p>
     *
     * <p>If the plot mark is <code>null</code>, it is set to
     * <code>PlotMark.FILLED_SQUARE</code>.</p>
     *
     * <p>If the plot mark is set by a paintable then the new
     * plot mark is set by the same paintable.</p>
     *
     * <p>If the size is less than 0, it is set to 0.</p>
     *
     * @param plotmark the plot mark to partially clone
     * @param size the plot mark size setting
     * @param fill the plot mark fill setting
     */
    public PlotMark(PlotMark plotmark, int size, boolean fill) { 
        if (plotmark == null)
            plotmark = FILLED_SQUARE;
        
        initializePlotMark
            (plotmark.algorithm, size, fill, plotmark.paintable);
    }
    
    
    /**
     * <p>Constructs a plot mark using the given paintable.</p>
     *
     * <p>Uses the default plot mark algorithm and settings
     * if the given paintable is <code>null</code>.</p>
     *
     * @param paintable the paintable to use for the plot mark
     */
    public PlotMark(Paintable paintable) {
        initializePlotMark(
            DEFAULT_ALGORITHM,
            DEFAULT_SIZE,
            DEFAULT_FILL,
            paintable);
    }
    
    
    /**
     * <p>The common initialization code for a plot mark.</p>
     *
     * @param algorithm the plot mark algorithm if used
     * @param size the plot mark size setting
     * @param fill the plot mark fill setting
     * @param paintable the plot mark paintable if used
     */
    private void initializePlotMark
        (PlotMarkAlgorithm algorithm, int size, boolean fill, Paintable paintable)
    {
        if (paintable != null) {
            this.algorithm = null;
            this.paintable = paintable;
            return;
        }
        
        if (algorithm == null)
            algorithm = PlotMarkAlgorithm.Square;
        
        this.algorithm = algorithm;
        
        if (size < 0)
            size = 0;
        
        this.size = size;
        
        this.fill = fill;
    }
    
    
    /**
     * <p>Draws, fills, or paints a mark at the given plot point.</p>
     *
     * <p>If this plot mark is defined by a plot mark algorithm, then
     * proceed as follows:</p>
     *
     * <ul>
     *   <li>Use the plot mark algorithm together with the plot point
     *       and the size to define a <code>Shape</code>.</li>
     *   <li>Paint the shape if the fill setting is <code>true</code>.</li>
     *   <li>Always stroke the boundary of the shape using
     *       <code>BasicStroke(1)</code> as the stroke setting.</li>
     *   <li>Use other graphics context settings such as the current
     *       <code>Paint</code> as-is.</li>
     * </ul>
     *
     * <p>Otherwise, if the plot mark is defined by a paintable, then
     * make the call:</p>
     *
     * <ul>
     *   <li><code>paintable.paintAt(g, the-plot-point)</code></li>
     * </ul>
     *
     * <p>Does nothing if the given plot point is <code>null</code>.
     *
     * @param g the graphics context
     * @param p the plot point
     */
    public final void mark(Graphics g, Point2D p) {
        if (g == null)
            return;
        
        if (p == null)
            return;
        
        mark(g, p.getX(), p.getY());
    }
    
    
    /**
     * <p>Draws, fills, or paints a mark at the given plot point.</p>
     *
     * <p>If this plot mark is defined by a plot mark algorithm, then
     * proceed as follows:</p>
     *
     * <ul>
     *   <li>Use the plot mark algorithm together with the plot point
     *       and the size to define a <code>Shape</code>.</li>
     *   <li>Paint the shape if the fill setting is <code>true</code>.</li>
     *   <li>Always stroke the boundary of the shape using
     *       <code>BasicStroke(1)</code> as the stroke setting.</li>
     *   <li>Use other graphics context settings such as the current
     *       <code>Paint</code> as-is.</li>
     * </ul>
     *
     * <p>Otherwise, if the plot mark is defined by a paintable, then
     * make the call:</p>
     *
     * <ul>
     *   <li><code>paintable.paintAt(g, the-plot-point)</code></li>
     * </ul>
     *
     * @param g the graphics context
     * @param x the x-coordinate of the plot point
     * @param y the y-coordinate of the plot point
     */
    public final void mark(Graphics g, double x, double y) {
        if (g == null)
            return;
        
        if (algorithm != null) {
            if (shape == null)
                shape = algorithm.makeShape(0, 0, size);
            
            Graphics2D h = (Graphics2D) g.create();
            
            h.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_OFF);
            
            h.translate( x,  y);
            
            if (fill) {
                h.fill(shape);
            }
            
            // always stroke
            h.setStroke(standardStroke);
            h.draw(shape);
        }
        else
        if (paintable != null) {
            paintable.paintAt(g, x, y);
        }
    }
    
    
    /**
     * <p>Draws, fills, or paints a mark at the plot point which is
     * obtained by applying the transform to the given point.</p>
     *
     * <p>In all cases, find the plot point by applying the transform
     * to the given point.</p>
     *
     * <p>If this plot mark is defined by a plot mark algorithm, then
     * proceed as follows:</p>
     *
     * <ul>
     *   <li>Use the plot mark algorithm together with the plot point
     *       and the size to define a <code>Shape</code>.</li>
     *   <li>Paint the shape if the fill setting is <code>true</code>.</li>
     *   <li>Always stroke the boundary of the shape using
     *       <code>BasicStroke(1)</code> as the stroke setting.</li>
     *   <li>Use other graphics context settings such as the current
     *       <code>Paint</code> as-is.</li>
     * </ul>
     *
     * <p>Otherwise, if the plot mark is defined by a paintable, then
     * make the call:</p>
     *
     * <ul>
     *   <li><code>paintable.paintAt(g, the-plot-point)</code></li>
     * </ul>
     *
     * <p>Does nothing if the original point is <code>null</code>.
     *
     * @param g the graphics context
     * @param T the affine transform to apply to the original point
     * @param p the original point
     */
    public final void mark(Graphics g, AffineTransform T, Point2D p) {
        if (g == null)
            return;
        
        if (p == null)
            return;
        
        mark(g, T, p.getX(), p.getY());
    }
    
    
    /**
     * <p>Draws, fills, or paints a mark at the plot point which is
     * obtained by applying the transform to the given point.</p>
     *
     * <p>In all cases, find the plot point by applying the transform
     * to the given point.</p>
     *
     * <p>If this plot mark is defined by a plot mark algorithm, then
     * proceed as follows:</p>
     *
     * <ul>
     *   <li>Use the plot mark algorithm together with the plot point
     *       and the size to define a <code>Shape</code>.</li>
     *   <li>Paint the shape if the fill setting is <code>true</code>.</li>
     *   <li>Always stroke the boundary of the shape using
     *       <code>BasicStroke(1)</code> as the stroke setting.</li>
     *   <li>Use other graphics context settings such as the current
     *       <code>Paint</code> as-is.</li>
     * </ul>
     *
     * <p>Otherwise, if the plot mark is defined by a paintable, then
     * make the call:</p>
     *
     * <ul>
     *   <li><code>paintable.paintAt(g, the-plot-point)</code></li>
     * </ul>
     *
     * @param g the graphics context
     * @param T the affine transform to apply to the original point
     * @param x the x-coordinate of the original point
     * @param y the y-coordinate of the original point
     */
    public final void mark(Graphics g, AffineTransform T, double x, double y) {
        if (g == null)
            return;
        
        if ((T == null) || (T.isIdentity()))
            mark(g, x, y);
        else {
            Point2D.Double p = new Point2D.Double(x, y);
            T.transform(p, p);
            mark(g, p.getX(), p.getY());
        }
    }
    
    
    /**
     * Returns the internal <code>PlotMarkAlgorithm</code>
     * or <code>null</code> if an algorithm is not used.
     */
    public final PlotMarkAlgorithm getAlgorithm() { return algorithm; }
    
    
    /**
     * Returns the size setting.  This will not be used if
     * the plot mark is defined by a paintable.
     *
     */
    public final int getSize() { return size; }
    
    
    /**
     * Returns the fill setting.  This will not be used if
     * the plot mark is defined by a paintable.
     *
     */
    public final boolean getFill() { return fill; }
    
    
    /**
     * Returns the internal <code>Paintable</code>
     * or <code>null</code> if a paintable is not used.
     */
    public final Paintable getPaintable() { return paintable; }
    
}
