package jplot;
/*
 * This file is part of jPlot
 *
 * jPlot is a Java version of Tim Shepard's xplot which is distributed
 * under the M.I.T. license (as below).
 *
 * Tim Shepard's original xplot is an amazing piece of software. It was
 * designed to be extraordinarily fast and efficient and as such, it has
 * only a very thin user interface. Those design goals, however, make
 * xplot extremely difficult to port to new architectures and also make it
 * difficult to use for the casual user. jPlot was designed to address
 * these limitations while keeping as much of the original efficiency as
 * possible. We thank Tim Shepard for his hard work on xplot and hope that
 * jPlot can live up to his high standards.
 *
 * jPlot was developed by Avinash Lakhiani at the Internetworking Research
 * Lab (IRG), Ohio University, as part of Dr. Shawn Ostermann's tcptrace
 * project. The main goal behind the development of jPlot was to make the
 * plotting tool used with tcptrace more portable. As a result, jPlot
 * retains many of the original algorithms implemented in xplot, and as per
 * the terms and conditions of the M.I.T. license, continue to remain under
 * that license. The M.I.T license is as follows:
 *
 * --- M.I.T License Begin ---
 *
 * Copyright 1992,1993 by the Massachusetts Institute of Technology.
 *                   All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS", AND M.I.T. MAKES NO REPRESENTATIONS
 * OR WARRANTIES, EXPRESS OR IMPLIED.  By way of example, but not
 * limitation, M.I.T. MAKES NO REPRESENTATIONS OR WARRANTIES OF
 * MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE
 * OF THE LICENSED SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD
 * PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
 *
 * The name of the Massachusetts Institute of Technology or M.I.T. may
 * NOT be used in advertising or publicity pertaining to distribution of
 * the software.  Title to copyright in this software and any associated
 * documentation shall at all times remain with M.I.T., and USER agrees
 * to preserve same.
 *
 * --- M.I.T. License End ---
 *
 * jPlot also has a lot of added features that are not part of the original
 * xplot code. Those sections of code are distributed under the following
 * BSD License:
 *
 * --- BSD License Begin ---
 *
 * Copyright (c) 2002, 2003 by Avinash Lakhiani
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   - Neither the name of Ohio University nor the names of its contributors
 *     may be used to endorse or promote products derived from this software
 *     without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * --- BSD License End ---
 *
 * You should have received a file named "DISTINCTION" along with this
 * distribution, that clearly identifies all the algorithms that were
 * retained from the original xplot code, and remain under the M.I.T.
 * license. The rest of the code was written for jPlot, and falls under
 * the BSD license.
 *
 * jPlot is free software; you can redistribute it and/or modify it under the
 * terms and conditions of both the above licenses.
 *
 * jPlot 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.  See the above licenses for more details.
 *
 * Author:  Avinash Lakhiani
 *          Internetworking Research Group
 *          Ohio University
 *          Athens, OH
 *          avinash.lakhiani@ohiou.edu
 *          http://www.tcptrace.org/jPlot
 *
 */

/*
 * $Id: DrawPlot.java,v 1.2 2002/12/04 01:58:52 alakhian Exp $
 */

/* Import Packages */
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.NoSuchElementException;

import javax.swing.JFrame;
import javax.swing.JPanel;

/* Class       - DrawPlot
 * Description - This is the main class where the paint method is implemented, which
 * does all the plotting of the graphs.
 * Extends     - JPanel
 * Implements  - MouseListener, MouseMotionListener
 */
public class DrawPlot extends JPanel implements MouseListener, MouseMotionListener
{
   /* Memeber Variable Declaration */
   private Point2D m1 = new Point2D.Double(0, 0);     // Mouse coordinates for the upper-left end of the rubberband box (used for zooming)
   private Point2D m2 = new Point2D.Double(0, 0);     // Mouse coordinates for the lower-right end of the rubberband box (used for zooming)
//   private boolean showDist  = false;                 // Indicates whether the distance tool is to be invoked
   private boolean repainted = false;                 // Indicates whether the canvas was repainted or not
   protected boolean shiftPressed   = false;          // Indicates wherether the shift key is pressed or not
   protected boolean controlPressed = false;          // Indicates wherether the control key is pressed or not
   protected boolean altPressed     = false;          // Indicates wherether the alt key is pressed or not
   protected boolean whiteOnBlack   = true;           // Indicated the drawing color combination selected (white on black is default)
   private Image canvas;                            // The image on which the plot is going the be drawn
   private Plotter pl;                              // Holds all the attributes and commands for the plotter
   protected Arguments args;
   private JFrame parent;                   /* keeping a reference to the parent.
						       * (used to implement correct exit of program, when all the windows are disposed. See
						       * killJFrame()).
						       */
//   private int version = 0;                           /* This is a counter for the version number of the postscript file being generated.

   /* Method      - DrawPlot (constructor)
    * Parameters  - NULL
    * Returns     - NULL
    * Description - called by default when the class is instantiated.
    */
   public DrawPlot(String file, ApplicationFrame parent, Arguments args)
     {
	this.parent = parent;            // Maintaining a reference to the parent. (Used for proper exit in the case of multiple plots)

	/* Add listeners for mouse events */
	addMouseListener(this);         // mouse button events
	addMouseMotionListener(this);   // mouse motion events

	/* Created the plotter for this input file */
	this.args = args;
	setPl(new PlotterFile(file, args.isMargin()));

     } // End of constructor DrawPlot()

   public DrawPlot(Arguments args, String content)
   {
      this.parent = null;            // Maintaining a reference to the parent. (Used for proper exit in the case of multiple plots)

      /* Add listeners for mouse events */
      addMouseListener(this);         // mouse button events
      addMouseMotionListener(this);   // mouse motion events

      /* Created the plotter for this input file */
      this.args = args;
      setPl(new PlotterMemory(content, args.isMargin()));
   } // End of constructor DrawPlot()

   /* Methods for mouse events */
   /* Invoked when the mouse enters a component */
   public void mouseEntered(MouseEvent me)
     {
	/* Set cross-hair cursor */
     if (null != parent)
       parent.getContentPane().setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
     else
       this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
     }

   /* Invoked when the mouse exits a component */
   public void mouseExited(MouseEvent me)
     {
	/* Set arrow-head cursor */
     if (null != parent)
       parent.getContentPane().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
     else
       this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
     }

   /* Invoked when the mouse has been clicked on a component */
   public void mouseClicked(MouseEvent me)
     {
	int mouseButton = 0;  // number of the mouse button {1:left ; 2:middle ; 3:right}

	if((me.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) mouseButton = 1;  // left click
	if((me.getModifiers() & MouseEvent.BUTTON2_MASK) != 0) mouseButton = 2;  // middle click
	if((me.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) mouseButton = 3;  // right click

	/* Undo the view change in mousePressed(), it was tentative, in case the mouse is dragged */
	getPl().setViewNo(getPl().getViewNo() - 1);

 	switch(mouseButton)
	  {
	   case 1:  // left click
	     if(getPl().getViewNo() > 0)  // pop the stack
	       getPl().setViewNo(getPl().getViewNo() - 1);
	     if(getPl().getViewNo() == 0)
	       getPl().setState(State.NORMAL);
	     repaint();
	     repainted = true;
	     break;

	   case 2:  // middle click
	     /* Do Nothing */


//	   case 3:  // right click
//	     parent.callKillJFrame(false);
//	     break;

	   default:
	     break;

	  }

     }

   /* Invoked when a mouse button has been pressed on a component */
   public void mousePressed(MouseEvent me)
     {
	int mouseButton = 0;  // number of the mouse button {1:left ; 2:middle ; 3:right}

	if((me.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) mouseButton = 1;  // left click
	if((me.getModifiers() & MouseEvent.BUTTON2_MASK) != 0) mouseButton = 2;  // middle click
	if((me.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) mouseButton = 3;  // right click

	/* Change to a new view (This may be tentative, since the mouse may be clicked, and not dragged.
	 * This is implemented this way because the event mouse clicked = mouse pressed + mouse released + mouse clicked.
	 */
	getPl().setViewNo(getPl().getViewNo() + 1);
	if(getPl().getViewNo() == Constant.NUMVIEWS) getPl().setViewNo(getPl().getViewNo() - 1);

	switch(mouseButton)
	  {
	   case 1:  // left click
	     m1.setLocation(me.getPoint().getX(), me.getPoint().getY());
	     m2.setLocation(me.getPoint().getX(), me.getPoint().getY());
	     if(shiftPressed == false && controlPressed == false)
	       {
		  if(m1.getY() > (getPl().origin.getY() + getPl().size.getY()))
		    getPl().setState(State.HZOOM);
		  else if(m1.getX() < getPl().origin.getX())
		    getPl().setState(State.VZOOM);
		  else
		    getPl().setState(State.ZOOM);
	       }
	     else if(shiftPressed == true && controlPressed == false)
	       {
		  if(m1.getY() > (getPl().origin.getY() + getPl().size.getY()))
		    getPl().setState(State.HDRAG);
		  else if(m1.getX() < getPl().origin.getX())
		    getPl().setState(State.VDRAG);
		  else
		    getPl().setState(State.DRAG);
	       }
	     else if(shiftPressed == false && controlPressed == true)
	       {
		  getPl().setState(State.SHOWDIST);
	       }
	     break;

	   case 2:  // middle click
	     break;

	   case 3:  // right click
	     break;
	   default:
	     System.out.println("jPlot: unknown mouse button pressed");
	  }

     }

   /* Invoked when a mouse button has been released on a component */
   public void mouseReleased(MouseEvent me)
     {
	int mouseButton = 0;  // number of the mouse button {1:left ; 2:middle ; 3:right}

	if((me.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) mouseButton = 1;  // left click
	if((me.getModifiers() & MouseEvent.BUTTON2_MASK) != 0) mouseButton = 2;  // middle click
	if((me.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) mouseButton = 3;  // right click

	/* Set the new mouse pointer location */
	m1.setLocation(me.getPoint().getX(), me.getPoint().getY());

	/* Set the drag ending point */
	getPl().dragEnd = PointFromPoint2D(toSub(m2));

	/* Correct the start and end drag points based on the plotter state */
	switch(getPl().getState())
	  {
	   case State.HZOOM:
	     getPl().dragStart.y = (int)Math.rint(getPl().origin.getY());
	     getPl().dragEnd.y   = (int)Math.rint(getPl().origin.getY()) + (int)Math.rint(getPl().size.getY());
	     break;
	   case State.VZOOM:
	     getPl().dragStart.x = (int)Math.rint(getPl().origin.getX());
	     getPl().dragEnd.x   = (int)Math.rint(getPl().origin.getX()) + (int)Math.rint(getPl().size.getX());
	     break;
	   case State.ZOOM:
	     break;
	   case State.HDRAG:
	     getPl().dragStart.y = getPl().dragEnd.y = ((int)Math.rint(getPl().origin.getY()) + (int)Math.rint(getPl().size.getY()) - 1);
	     break;
	   case State.VDRAG:
	     getPl().dragStart.x = getPl().dragEnd.x = (int)Math.rint(getPl().origin.getX());
	     break;
	   case State.DRAG:
	     break;
	  }

	switch(mouseButton)
	  {
	   case 1:  // left click
	     if((getPl().getState() == State.HZOOM) ||
		(getPl().getState() == State.VZOOM) ||
		(getPl().getState() == State.ZOOM)
		)
	       {
		  /* Check if mouse is dragged over threshold value, else don't zoom-in. (Default threshold is 7 pixels) */
		  if((Math.abs(getPl().dragStart.x - getPl().dragEnd.x) > Constant.MOUSE_MOTION_THRESHOLD) ||
		     (Math.abs(getPl().dragStart.y - getPl().dragEnd.y) > Constant.MOUSE_MOTION_THRESHOLD)
		     )
		    {
		       if((getPl().getState() != State.VZOOM) && (Math.abs(getPl().dragStart.y - getPl().dragEnd.y) > Constant.MOUSE_MOTION_THRESHOLD) ||
			  (getPl().getState() != State.HZOOM) && (Math.abs(getPl().dragStart.x - getPl().dragEnd.x) > Constant.MOUSE_MOTION_THRESHOLD)
			  )
			 {
			    /* Zoom-in */
			    if(Math.abs(getPl().dragStart.x - getPl().dragEnd.x) > Constant.MOUSE_MOTION_THRESHOLD)
			      {
				 zoomInCoord(getPl().xLeft[getPl().getViewNo()-1], getPl().xRight[getPl().getViewNo()-1],
					     getPl().dragStart.x, getPl().dragEnd.x,
					     getPl().size.getX(),
					     getPl().xLeft[getPl().getViewNo()], getPl().xRight[getPl().getViewNo()]
					     );
			      }
			    else
			      {
				 getPl().xLeft[getPl().getViewNo()].set(getPl().xLeft[getPl().getViewNo()-1].get());
				 getPl().xRight[getPl().getViewNo()].set(getPl().xRight[getPl().getViewNo()-1].get());
			      }

			    if(Math.abs(getPl().dragStart.y - getPl().dragEnd.y) > Constant.MOUSE_MOTION_THRESHOLD)
			      {
				 zoomInCoord(getPl().yBottom[getPl().getViewNo()-1], getPl().yTop[getPl().getViewNo()-1],
					     (int)Math.rint(getPl().size.getY()) - getPl().dragStart.y,
					     (int)Math.rint(getPl().size.getY()) - getPl().dragEnd.y,
					     getPl().size.getY(),
					     getPl().yBottom[getPl().getViewNo()], getPl().yTop[getPl().getViewNo()]
					     );
			      }
			    else
			      {
				 getPl().yBottom[getPl().getViewNo()].set(getPl().yBottom[getPl().getViewNo()-1].get());
				 getPl().yTop[getPl().getViewNo()].set(getPl().yTop[getPl().getViewNo()-1].get());
			      }

			    /* Draw the zoomed-in plot */
			    repaint();
			    repainted = true;
			    getPl().setState(State.NORMAL);
			 }
		    }
	       }
	     /* Drag the plot/scroll the plot */
	     else if((getPl().getState() == State.HDRAG) ||
		     (getPl().getState() == State.VDRAG) ||
		     (getPl().getState() == State.DRAG)
		     )
	       {
		  dragCoord(getPl().xType,
			    getPl().xLeft[getPl().getViewNo()-1], getPl().xRight[getPl().getViewNo()-1],
			    getPl().dragEnd.x, getPl().dragStart.x,
			    getPl().size.getX(),
			    getPl().xLeft[getPl().getViewNo()], getPl().xRight[getPl().getViewNo()]
			    );

		  dragCoord(getPl().yType,
			    getPl().yBottom[getPl().getViewNo()-1], getPl().yTop[getPl().getViewNo()-1],
			    (int)Math.rint(getPl().size.getY()) - getPl().dragEnd.y,
			    (int)Math.rint(getPl().size.getY()) - getPl().dragStart.y,
			    getPl().size.getY(),
			    getPl().yBottom[getPl().getViewNo()], getPl().yTop[getPl().getViewNo()]
			    );
	       }
	     else if(getPl().getState() == State.SHOWDIST)
	       {
		  /* Undo the view change in mousePressed(), it was tentative, in case the mouse is dragged */
		  getPl().setViewNo(getPl().getViewNo() - 1);
	       }

	     /* Return back to the normal state */
	     getPl().setState(State.NORMAL);

	     /* Draw the scrolled plot OR just repaint to wipe the distance triangle */
	     repaint();
	     repainted = true;
	     break;

	   case 2:  // middle click
	       {
		  /* Do Nothing */
	       }
	     break;

	   case 3:  // right click
	       {
		  /* Do Nothing */
	       }
	     break;

	   default:
	     System.out.println("jPlot: unknown mouse button released");
	  }

     }

   /* Invoked when a mouse button is pressed on a component and then dragged */
   public void mouseDragged(MouseEvent me)
     {
	int mouseButton = 0;  // number of the mouse button {1:left ; 2:middle ; 3:right}

	if((me.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) mouseButton = 1;  // left click
	if((me.getModifiers() & MouseEvent.BUTTON2_MASK) != 0) mouseButton = 2;  // middle click
	if((me.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) mouseButton = 3;  // right click

	/* Set the drag starting point */
	getPl().dragStart = PointFromPoint2D(toSub(m1));

	switch(mouseButton)
	  {
	   case 1:  // left click
	     /* Check whether the state is suppose to be HZOOM, VZOOM or ZOOM */
	     switch(getPl().getState())
	       {
		case State.HZOOM:
		    {
		       getPl().setState(State.HZOOM);
		       Point2D p1 = new Point2D.Double(m1.getX(), getPl().origin.getY());
		       Point2D p2 = new Point2D.Double(m2.getX(), (getPl().origin.getY() + getPl().size.getY() - 2));
		       drawRubberbandBox((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       m2.setLocation(me.getPoint().getX(), me.getPoint().getY());
		       p2.setLocation(m2.getX(), (getPl().origin.getY() + getPl().size.getY() - 2));
		       drawRubberbandBox((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       break;
		    }

		case State.VZOOM:
		    {
		       getPl().setState(State.VZOOM);
		       Point2D p1 = new Point2D.Double((getPl().origin.getX() + 2), m1.getY());
		       Point2D p2 = new Point2D.Double((getPl().origin.getX() + getPl().size.getX()), m2.getY());
		       drawRubberbandBox((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       m2.setLocation(me.getPoint().getX(), me.getPoint().getY());
		       p2.setLocation((getPl().origin.getX() + getPl().size.getX()), m2.getY());
		       drawRubberbandBox((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       break;
		    }

		case State.ZOOM:
		    {
		       getPl().setState(State.ZOOM);
		       drawRubberbandBox((Graphics2D)me.getComponent().getGraphics(), m1, m2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       m2.setLocation(me.getPoint().getX(), me.getPoint().getY());
		       drawRubberbandBox((Graphics2D)me.getComponent().getGraphics(), m1, m2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       break;
		    }

		case State.HDRAG:
		    {
		       /* Check whether the state is suppose to be HDRAG, VDRAG or DRAG */
		       getPl().setState(State.HDRAG);
		       Point2D p1 = new Point2D.Double(m1.getX(), (getPl().origin.getY() + getPl().size.getY() - 2));
		       Point2D p2 = new Point2D.Double(m2.getX(), (getPl().origin.getY() + getPl().size.getY() - 2));
		       drawRubberbandLine((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       m2.setLocation(me.getPoint().getX(), me.getPoint().getY());
		       p2.setLocation(m2.getX(), (getPl().origin.getY() + getPl().size.getY() - 2));
		       /* Set the drag ending point */
		       getPl().dragEnd = PointFromPoint2D(toSub(m2));
		       drawRubberbandLine((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       break;
		    }

		case State.VDRAG:
		    {
		       getPl().setState(State.VDRAG);
		       Point2D p1 = new Point2D.Double((getPl().origin.getX() + 1), m1.getY());
		       Point2D p2 = new Point2D.Double((getPl().origin.getX() + 1), m2.getY());
		       drawRubberbandLine((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       m2.setLocation(me.getPoint().getX(), me.getPoint().getY());
		       p2.setLocation((getPl().origin.getX() + 1), m2.getY());
		       /* Set the drag ending point */
		       getPl().dragEnd = PointFromPoint2D(toSub(m2));
		       drawRubberbandLine((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       break;
		    }

		case State.DRAG:
		    {
		       getPl().setState(State.DRAG);
		       Point2D p1 = new Point2D.Double(m1.getX(), m1.getY());
		       Point2D p2 = new Point2D.Double(m2.getX(), m2.getY());
		       drawRubberbandLine((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       m2.setLocation(me.getPoint().getX(), me.getPoint().getY());
		       p2.setLocation(m2.getX(), m2.getY());
		       /* Set the drag ending point */
		       getPl().dragEnd = PointFromPoint2D(toSub(m2));
		       drawRubberbandLine((Graphics2D)me.getComponent().getGraphics(), p1, p2);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       break;
		    }

		case State.SHOWDIST:
		    {
		       /* Check whether the state is suppose to be SHOWDIST */
		       getPl().setState(State.SHOWDIST);
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       showDistance((Graphics2D)me.getComponent().getGraphics());
		       m2.setLocation(me.getPoint().getX(), me.getPoint().getY());
		       /* Set the drag ending point */
		       getPl().dragEnd = PointFromPoint2D(toSub(m2));
		       drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m2);
		       showDistance((Graphics2D)me.getComponent().getGraphics());
		    }

		default:
		  break;

	       }
	     break;

	   case 2:  // middle click
	     /* Do Nothing */
	     break;

	   case 3:  // right click
	     /* Do Nothing */
	     break;

	   default:
	     System.out.println("jPlot: unknown mouse button used for dragging");
	  }

     }

   /* Invoked when the mouse button has been moved on a component (with no buttons no down) */
   public void mouseMoved(MouseEvent me)
     {
	/* Draw the pointer marks */
	if(!repainted)   // This is ugly because we repaint when the mouse is clicked, dragged etc.
	  drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m1);
	else
	  repainted = false;
	m1.setLocation(me.getPoint().getX(), me.getPoint().getY());
	m2.setLocation(me.getPoint().getX(), me.getPoint().getY());
	drawPointerMarks((Graphics2D)me.getComponent().getGraphics(), m1);
     }

   /* Method      - update
    * Parameters  - the graphics component
    * Returns     - NULL
    * Description - overriding the graphics update method.
    * Does the same thing, not really needed
    */
   public void update(Graphics g)
     {
	paint(g);
     }

   /* Method      - paint
    * Parameters  - the graphics component
    * Returns     - NULL
    * Description - The graphics paint method.
    * This is the main method that does all the plotting of graphs.
    */
   public void paint(Graphics g)
     {
	/* Cast graphics to graphics2D */
	Graphics2D g2 = (Graphics2D)g;

	/* Clear the offscreen image */
	checkOffScreenImage();
	Graphics offG = getCanvas().getGraphics();
	offG.setColor(getPl().backgroundColor);
	Dimension d = getSize();
	offG.fillRect(0, 0, d.width, d.height);

	/* Draw onto the offscreen image */
	paintOffScreen((Graphics2D)getCanvas().getGraphics());

	/* Put the offscreen image on the screen */
	g2.drawImage(getCanvas(), 0, 0, null);

     }

   /* Method      - checkOffScreenImage
    * Parameters  - NULL
    * Returns     - NULL
    * Description - Checks if the off-screen image already exist, else creates
    * a new one.
    */
   private void checkOffScreenImage()
     {
	try
	  {
	     Dimension d = getSize();
	     if (getCanvas() == null || getCanvas().getWidth(null) != d.width || getCanvas().getHeight(null) != d.height)
	       {
		  setCanvas(createImage(d.width, d.height));
	       }
	  }
	catch(Exception e)
	  {
	     System.out.println("Image creation failed");
	  }
     }

   /* Method      - printOffScreen
    * Parameters  - the graphics2D component
    * Returns     - NULL
    * Description - Paints on the off-screen image.
    * MOST OF THE PLOTTING WILL BE DONE HERE.
    */
   public void paintOffScreen(Graphics2D g2)
     {
	/* Setting the origin and appropriate window range. This step also maps all coordinates. */
	sizeWindow();

	/* Set the initial drawing color */
	g2.setColor(getPl().foregroundColor);
	/* Clamp the coordinates against an upper limit of 3000. (This is done to avoid very hight coordinate
	 * values during zooming, else java freezes sometimes) */
	try
	  {
	     int size = getPl().commands.size();
	     for(int i = 0; i < size; i++)
	       {
		  Command cmd = (Command)getPl().commands.get(i);
		  if(cmd.mapped == true)
		    {
		       clampCoord(cmd);
		       Point p1 = new Point((int)Math.rint(cmd.a.getX()), (int)Math.rint(cmd.a.getY()));
		       Point p2 = new Point((int)Math.rint(cmd.b.getX()), (int)Math.rint(cmd.b.getY()));

		       /* Set the appropriate clipping rectangle */
		       if((cmd.decoration) ||
			  ((cmd.type == cmdType.TITLE)  ||
			   (cmd.type == cmdType.XLABEL) ||
			   (cmd.type == cmdType.YLABEL)
			  )
			 )
			 g2.setClip(0, 0,
				    (int)getPl().mainSize.getX(), (int)getPl().mainSize.getY()
				    );
		       else
			 g2.setClip(((int)getPl().origin.getX()), ((int)getPl().origin.getY() - 2),
				    ((int)getPl().size.getX() + 2), ((int)getPl().size.getY() + 2)
				    );

			 {   /* ----- Actual plotting begins here ----- */

			    if(args.isMono())
			      {
				 if(g2.getColor() != getPl().foregroundColor)
				   g2.setColor(getPl().foregroundColor);
			      }
			    else if(cmd.color != null)
			      {
				 if(cmd.color == Color.white)
				   {
				      /* Revert on white if the color option is set to black on white */
				      if(whiteOnBlack != true)
					g2.setColor(Color.black);
				      else
					g2.setColor(Color.white);
				   }
				 else
				   {
				      g2.setColor(cmd.color);
				   }
			      }
			    else
			      g2.setColor(getPl().foregroundColor);

			    switch(cmd.type)
			      {
			       case cmdType.DLINE     :
				 g2.drawLine(p1.x     , p1.y - 2 , p1.x     , p1.y + 2 );
				 g2.drawLine(p1.x - 2 , p1.y     , p1.x + 2 , p1.y     );
				 g2.drawLine(p2.x     , p2.y - 2 , p2.x     , p2.y + 2 );
				 g2.drawLine(p2.x - 2 , p2.y     , p2.x + 2 , p2.y     );
				 /* fall through */
			       case cmdType.LINE      :
				 g2.drawLine(p1.x, p1.y, p2.x, p2.y);
				 break;
			       case cmdType.X         :
				 /*
				    *   *
				     * *
				      *
				     * *
				    *   *
				  */
				 g2.drawLine(p1.x - 2 , p1.y - 2 , p1.x + 2 , p1.y + 2 );
				 g2.drawLine(p1.x - 2 , p1.y + 2 , p1.x + 2 , p1.y - 2 );
				 break;
			       case cmdType.DOT       :
				 // decides the type of dot {dense : 0, medium: 1, light: 2} CHANGE IN "Constant.java" FOR PREFERRED TYPE OF DOT.

				 if(Constant.DOT == 0)
				   {

				      /*
				         ***
				        *****
				        *****
				        *****
				         ***
				       */
				      g2.drawLine(p1.x - 1 , p1.y - 2 , p1.x + 1 , p1.y - 2 );
				      g2.drawLine(p1.x - 2 , p1.y - 1 , p1.x + 2 , p1.y - 1 );
				      g2.drawLine(p1.x - 2 , p1.y     , p1.x + 2 , p1.y     );
				      g2.drawLine(p1.x - 2 , p1.y + 1 , p1.x + 2 , p1.y + 1 );
				      g2.drawLine(p1.x - 1 , p1.y + 2 , p1.x + 1 , p1.y + 2 );
				   }
				 else if(Constant.DOT == 1)
				   {
				      /*
				         *
				        ***
				         *
				       */
				      g2.drawLine(p1.x - 1 , p1.y     , p1.x + 1 , p1.y     );
				      g2.drawLine(p1.x     , p1.y - 1 , p1.x     , p1.y + 1 );
				   }
				 else if(Constant.DOT == 2)
				   {
				      /*

				         *

				       */
				      g2.drawLine(p1.x, p1.y, p1.x, p1.y);
				   }
				 else Error("jPlot: {DrawPlot.java: paintOffScreen()}: unknown \"dot\" preferrence, check \"Constant.java\" for valid values.");
				 break;
			       case cmdType.PLUS      :
				 /*
				      *
				      *
				    *****
				      *
				      *
				  */
				 g2.drawLine(p1.x     , p1.y - 2 , p1.x     , p1.y + 2 );
				 g2.drawLine(p1.x - 2 , p1.y     , p1.x + 2 , p1.y     );
				 break;
			       case cmdType.BOX       :
				 /*
				    --0-|
				    |   |
				    3   1
				    |   |
				    |-2--
				  */
				 /* 0 */ g2.drawLine(p1.x - Constant.BOXRADIUS     , p1.y - Constant.BOXRADIUS     , p1.x + Constant.BOXRADIUS - 1 , p1.y - Constant.BOXRADIUS     );
				 /* 1 */ g2.drawLine(p1.x + Constant.BOXRADIUS     , p1.y - Constant.BOXRADIUS     , p1.x + Constant.BOXRADIUS     , p1.y + Constant.BOXRADIUS - 1 );
				 /* 2 */ g2.drawLine(p1.x - Constant.BOXRADIUS + 1 , p1.y + Constant.BOXRADIUS     , p1.x + Constant.BOXRADIUS     , p1.y + Constant.BOXRADIUS     );
				 /* 3 */ g2.drawLine(p1.x - Constant.BOXRADIUS     , p1.y - Constant.BOXRADIUS + 1 , p1.x - Constant.BOXRADIUS     , p1.y + Constant.BOXRADIUS     );
				 break;
			       case cmdType.DIAMOND   :
				 /*
				     /
		                    1 \
		                   /    2
		                  \     \
		                   0   /
		                    \ 3
			             /

				  */

				 g2.drawLine(p1.x - 1 , p1.y + 2 , p1.x - 3 , p1.y     );
				 g2.drawLine(p1.x - 2 , p1.y - 1 , p1.x     , p1.y - 3 );
				 g2.drawLine(p1.x + 1 , p1.y - 2 , p1.x + 3 , p1.y     );
				 g2.drawLine(p1.x + 2 , p1.y + 1 , p1.x     , p1.y + 3 );
				 break;
			       case cmdType.UTICK     :
				 g2.drawLine(p1.x, p1.y, p1.x, p1.y - Constant.D);
				 break;
			       case cmdType.DTICK     :
				 g2.drawLine(p1.x, p1.y, p1.x, p1.y + Constant.D);
				 break;
			       case cmdType.LTICK     :
				 g2.drawLine(p1.x, p1.y, p1.x - Constant.D, p1.y);
				 break;
			       case cmdType.RTICK     :
				 g2.drawLine(p1.x, p1.y, p1.x + Constant.D, p1.y);
				 break;
			       case cmdType.HTICK     :
				 g2.drawLine(p1.x - Constant.D, p1.y, p1.x + Constant.D, p1.y);
				 break;
			       case cmdType.VTICK     :
				 g2.drawLine(p1.x, p1.y - Constant.D, p1.x, p1.y + Constant.D);
				 break;
			       case cmdType.UARROW    :
				 g2.drawLine(p1.x - Constant.U, p1.y + Constant.U, p1.x, p1.y);
				 g2.drawLine(p1.x + Constant.U, p1.y + Constant.U, p1.x, p1.y);
				 break;
			       case cmdType.DARROW    :
				 g2.drawLine(p1.x - Constant.U, p1.y - Constant.U, p1.x, p1.y);
				 g2.drawLine(p1.x + Constant.U, p1.y - Constant.U, p1.x, p1.y);
				 break;
			       case cmdType.LARROW    :
				 g2.drawLine(p1.x + Constant.U, p1.y - Constant.U, p1.x, p1.y);
				 g2.drawLine(p1.x + Constant.U, p1.y + Constant.U, p1.x, p1.y);
				 break;
			       case cmdType.RARROW    :
				 g2.drawLine(p1.x - Constant.U, p1.y - Constant.U, p1.x, p1.y);
				 g2.drawLine(p1.x - Constant.U, p1.y + Constant.U, p1.x, p1.y);
				 break;
			       case cmdType.INVISIBLE :
				 break;
			       case cmdType.TEXT      :
			       case cmdType.TITLE     :
			       case cmdType.XLABEL    :
			       case cmdType.YLABEL    :
				   {
				      Point          k = new Point(0, 0);
				      int        width = 0;
				      int   fontAscent = 0;
				      int  fontDescent = 0;
				      final int  space = 5;
				      /* Set the font */
				      Font f = new Font(null, Font.PLAIN, Constant.FONTSIZE);
				      g2.setFont(f);
				      FontMetrics   fm = g2.getFontMetrics();
				      char[] text;

				      if(cmd.type == cmdType.TITLE)
					{
					   k.x = (int)Math.rint(getPl().size.getX() / 2);
					   k.y = 0;
					   k = PointFromPoint2D(toMain(Point2DFromPoint(k)));
					   cmd.position = Position.ABOVE;
					}
				      else if(cmd.type == cmdType.XLABEL)
					{
					   k.x = (int)Math.rint(getPl().size.getX());
					   k.y = (int)Math.rint(getPl().size.getY()) + 22;;
					   k = PointFromPoint2D(toMain(Point2DFromPoint(k)));
					   cmd.position = Position.LEFT;
					}
				      else if(cmd.type == cmdType.YLABEL)
					{
					   k.x = 0;
					   k.y = -5;
					   k = PointFromPoint2D(toMain(Point2DFromPoint(k)));
					   cmd.position = Position.ABOVE;
					}
				      else if(cmd.type == cmdType.TEXT)
					{
					   k = new Point(p1);
					}

				      /* Get the width for displaying the text */
				      text        = cmd.text.toCharArray();
				      width       = fm.charsWidth(text, 0, text.length);
				      fontAscent  = fm.getAscent();
				      fontDescent = fm.getDescent();

				      switch(cmd.position)
					{
					 case Position.CENTERED :
					 case Position.ABOVE    :
					 case Position.BELOW    : k.x -= width/2       ;  break;
					 case Position.LEFT     : k.x -= width + space ;  break;
					 case Position.RIGHT    : k.x += space         ;  break;
					 default: Error("jPlot: {DrawPlot.java: paintOffScreen()}: unknown text positioning");
					}

				      switch(cmd.position)
					{
					 case Position.CENTERED :
					 case Position.LEFT     :
					 case Position.RIGHT    : k.y += fontAscent/2        ;  break;
					 case Position.ABOVE    : k.y -= space + fontDescent ;  break;
					 case Position.BELOW    : k.y += space + fontAscent  ;  break;
					 default: Error("jPlot: {DrawPlot.java: paintOffScreen()}: unknown text positioning");
					}

				      g2.drawString(cmd.text, k.x, k.y);

				   }
				 break;
			       default                :
				 Error("jPlot: {DrawPlot.java: paintOffScreen()}: unknown command type");
			      }

			 }   /* ----- Actual plotting ends here ----- */

		    } // End of if(cmd.mapped)
	       } // End of for-loop
	  }
	catch(IndexOutOfBoundsException e)
	  {
	     System.out.println(e.toString());
	  }

     } // End of method PaintOffScreen()

   /* Method      - drawRubberbandBox
    * Parameters  - the graphics2D component, and the upperleft and lowerright points of the rubberband box
    * Returns     - NULL
    * Description - Draws the rubberband box used for zooming.
    */
   public void drawRubberbandBox(Graphics2D g2, Point2D p1, Point2D p2)
     {
	/* Setting the color of the rubberband box */
	g2.setColor(getPl().foregroundColor);

	/* Setting the stroke to XOR mode */
	setExclusiveOr(g2);

	/* Get the coordinates */
	Point k1 = PointFromPoint2D(p1);
	Point k2 = PointFromPoint2D(p2);

	/* Drawing the rubberband box */
	g2.drawLine(k1.x, k1.y, k2.x ,k1.y);
	g2.drawLine(k2.x, k1.y, k2.x ,k2.y);
	g2.drawLine(k1.x ,k2.y, k2.x, k2.y);
	g2.drawLine(k1.x, k1.y, k1.x ,k2.y);

     } // End of method drawRubberbandBox()

   /* Method      - drawRubberbandLine
    * Parameters  - the graphics2D component, and the ends of the rubberband line
    * Returns     - NULL
    * Description - Draws the rubberband line used for zooming
    */
   public void drawRubberbandLine(Graphics2D g2, Point2D p1, Point2D p2)
     {
	/* Setting the color of the rubberband box */
	g2.setColor(getPl().foregroundColor);

	/* Setting the stroke to XOR mode */
	setExclusiveOr(g2);

	/* Get the mouse points */
	Point k1 = PointFromPoint2D(p1);
	Point k2 = PointFromPoint2D(p2);
	/* Drawing the rubberband line */
	g2.drawLine(k1.x, k1.y, k2.x, k2.y);

     } // End of method drawRubberBandLine

   /* Method      - drawPointerMarks
    * Parameters  - the graphics2D component and the point at which the pointer mark is to be drawn
    * Returns     - NULL
    * Description - draws the crosswire for the mouse pointer
    */
   public void drawPointerMarks(Graphics2D g2, Point2D me)
     {
	Point      m = PointFromPoint2D(me);
	Point origin = PointFromPoint2D(getPl().origin);
	Point   size = PointFromPoint2D(getPl().size);

	/* Setting the color of the pointer marks */
	g2.setColor(getPl().foregroundColor);

	/* Setting the stroke to XOR mode */
	setExclusiveOr(g2);

	/* Draw the pointer marks on the axis */
	g2.drawLine(m.x , (origin.y + size.y - 1),
		    m.x , (origin.y + size.y - 1 - Constant.POINTER_MARK_LENGTH)
		    );
	g2.drawLine(origin.x                                , m.y,
		    origin.x + Constant.POINTER_MARK_LENGTH , m.y
		    );

	/* Draw the cross-hair cursor - JAVA DOES IT SO I REMOVED THIS PART
	g2.drawLine(m.x - Constant.POINTER_MARK_LENGTH, m.y,
		    m.x + Constant.POINTER_MARK_LENGTH, m.y
		    );
	g2.drawLine(m.x, m.y - Constant.POINTER_MARK_LENGTH,
		    m.x, m.y + Constant.POINTER_MARK_LENGTH
		    );
	 */

     } // End of method drawPointerMarks()

   /* Method      - setExclusiveOr
    * Parameters  - the graphics2D component
    * Returns     - NULL
    * Description - sets the stroke to XOR mode so that the rubberband box can be drawn
    * exclusively and erased.
    */
   private static void setExclusiveOr(Graphics2D g2)
     {
	Color c = g2.getColor();
	int r = c.getRed(), b = c.getBlue(), g = c.getGreen();
	r=0xff^r; b=0xff^b; g=0xff^g;
	g2.setXORMode(new Color(r,g,b));
     }

   /* Method      - sizeWindow
    * Parameters  - NULL
    * Returns     - NULL
    * Description - this method computes the window ranges, sets the origin, and the size of the
    * clipping rectangle. It also maps all the commands to the viewport coordinates.
    */
   private void sizeWindow()
     {
	/* Setting the main window size */
	Dimension d = getSize();
	getPl().mainSize.setLocation(d.width, d.height);
	/* Setting the clipping rectangle size */
	getPl().size.setLocation(((double)d.width - getPl().origin.getX() - 30), ((double)d.height - getPl().origin.getY() - 30));
	/* Clear the old axis before making the new one. (New axis is created everytime the plotter view changes) */
	Command cmd = null;
	for( ; ;)
	  {
	     try
	       {
		  cmd = (Command)getPl().commands.get(0);  // Axis is always at the begining of the list
	       }
	     catch(IndexOutOfBoundsException e)
	       {
		  Error(e.toString());
	       }
	     if(cmd.decoration == true)
	       getPl().commands.remove(0);
	     else       //break else we will land up deleting the labels too since they are marked as decoration
	       break;
	  }

	/* Creating the axis */
	getPl().makeAxis();

	/* Mapping all the command coordinates to window coordinates */
	try
	  {
	     int size = getPl().commands.size();
	     for(int i = 0; i < size; i++)
	       {
		  cmd = (Command)getPl().commands.get(i);
		  computeWindowCoords(cmd);
	       }
	  }
	catch(IndexOutOfBoundsException e)
	  {
	     System.out.println(e.toString());
	  }

     } // End of method sizeWidow()


   /* Method      - computeWindowCoords
    * Parameters  - command for which the window coordinates are to be computed.
    * Returns     - NULL
    * Description - this method computes the appropriate window coordinates for the command passed to
    * it, based on the current window size, and clipping rectangle size, and sets these values in the
    * command object's mapped coordinate fields.
    */
   private void computeWindowCoords(Command cmd)
     {
	/*  0  |  1  |  2
	 * ----+-----+----
	 *  3  |  4  |  5
	 * ----+-----+----
	 *  6  |  7  |  8
	 */
	/* 1. Any line originating or terminating in region 4 is ought to be displayed.     (YES)
	 * 2. Any line that is believed to intersect with region 4 MAYBE displayed.         (MAYBE)
	 * 3. Any line that does not satisfy any of the above conditions, is not displayed  (NO)
	 */
	int inRectTable[][] = {
	     { Constant.NO,    Constant.NO,    Constant.NO,    Constant.NO,    Constant.YES,   Constant.MAYBE, Constant.NO,    Constant.MAYBE, Constant.MAYBE },
	     { Constant.NO,    Constant.NO,    Constant.NO,    Constant.MAYBE, Constant.YES,   Constant.MAYBE, Constant.MAYBE, Constant.YES,   Constant.MAYBE },
	     { Constant.NO,    Constant.NO,    Constant.NO,    Constant.MAYBE, Constant.YES,   Constant.NO,    Constant.MAYBE, Constant.MAYBE, Constant.NO    },
	     { Constant.NO,    Constant.MAYBE, Constant.MAYBE, Constant.NO,    Constant.YES,   Constant.YES,   Constant.NO,    Constant.MAYBE, Constant.MAYBE },
	     { Constant.YES,   Constant.YES,   Constant.YES,   Constant.YES,   Constant.YES,   Constant.YES,   Constant.YES,   Constant.YES,   Constant.YES   },
	     { Constant.MAYBE, Constant.MAYBE, Constant.NO,    Constant.YES,   Constant.YES,   Constant.NO,    Constant.MAYBE, Constant.MAYBE, Constant.NO    },
	     { Constant.NO,    Constant.MAYBE, Constant.MAYBE, Constant.NO,    Constant.YES,   Constant.MAYBE, Constant.NO,    Constant.NO,    Constant.NO    },
	     { Constant.MAYBE, Constant.YES,   Constant.MAYBE, Constant.MAYBE, Constant.YES,   Constant.MAYBE, Constant.NO,    Constant.NO,    Constant.NO    },
	     { Constant.MAYBE, Constant.MAYBE, Constant.NO,    Constant.MAYBE, Constant.YES,   Constant.NO,    Constant.NO,    Constant.NO,    Constant.NO    }
	};

	/* Computing locations to map into the above grid matrix */
	int loc1 = -1, loc2 = -1;

	if((cmd.type == cmdType.TITLE)  ||
	   (cmd.type == cmdType.XLABEL) ||
	   (cmd.type == cmdType.YLABEL)
	   )
	  {
	     cmd.mapped = true;
	     return;
	  }

	if(cmd.type == cmdType.INVISIBLE)
	  {
	     cmd.mapped = false;
	     return;
	  }

	if(cmd.xa.compareTo(getPl().xLeft[getPl().getViewNo()]) < 0)
	  if(cmd.ya.compareTo(getPl().yTop[getPl().getViewNo()]) > 0) loc1 = 0;
	  else
	    if(cmd.ya.compareTo(getPl().yBottom[getPl().getViewNo()]) < 0) loc1 = 6;
	    else loc1 = 3;
	else
	  if(cmd.xa.compareTo(getPl().xRight[getPl().getViewNo()]) > 0)
	    if(cmd.ya.compareTo(getPl().yTop[getPl().getViewNo()]) > 0) loc1 = 2;
	    else
	      if(cmd.ya.compareTo(getPl().yBottom[getPl().getViewNo()]) < 0) loc1 = 8;
	      else loc1 = 5;
	  else
	    if(cmd.ya.compareTo(getPl().yTop[getPl().getViewNo()]) > 0) loc1 = 1;
	    else
	      if(cmd.ya.compareTo(getPl().yBottom[getPl().getViewNo()]) < 0) loc1 = 7;
	      else loc1 = 4;

	switch(cmd.type)
	  {
	   case cmdType.X         :
	   case cmdType.DOT       :
	   case cmdType.PLUS      :
	   case cmdType.BOX       :
	   case cmdType.DIAMOND   :
	   case cmdType.UTICK     :
	   case cmdType.DTICK     :
	   case cmdType.LTICK     :
	   case cmdType.RTICK     :
	   case cmdType.HTICK     :
	   case cmdType.VTICK     :
	   case cmdType.UARROW    :
	   case cmdType.DARROW    :
	   case cmdType.LARROW    :
	   case cmdType.RARROW    :
	   case cmdType.INVISIBLE :
	   case cmdType.TEXT      :
	     loc2 = loc1;
	     break;
	   case cmdType.DLINE     :
	   case cmdType.LINE      :
	     if(cmd.xb.compareTo(getPl().xLeft[getPl().getViewNo()]) < 0)
	       if(cmd.yb.compareTo(getPl().yTop[getPl().getViewNo()]) > 0) loc2 = 0;
	       else
	         if(cmd.yb.compareTo(getPl().yBottom[getPl().getViewNo()]) < 0) loc2 = 6;
	         else loc2 = 3;
	     else
	       if(cmd.xb.compareTo(getPl().xRight[getPl().getViewNo()]) > 0)
		 if(cmd.yb.compareTo(getPl().yTop[getPl().getViewNo()]) > 0) loc2 = 2;
	         else
	           if(cmd.yb.compareTo(getPl().yBottom[getPl().getViewNo()]) < 0) loc2 = 8;
	           else loc2 = 5;
	       else
	         if(cmd.yb.compareTo(getPl().yTop[getPl().getViewNo()]) > 0) loc2 = 1;
	         else
	           if(cmd.yb.compareTo(getPl().yBottom[getPl().getViewNo()]) < 0) loc2 = 7;
	           else loc2 = 4;
	     break;
	   default                :
	     Error("DrawPlot.java: {method computeWindowCoord(Command) (case 1)}: unknown command type");
	  }

	cmd.mapped = false;
	switch(inRectTable[loc1][loc2])
	  {
	   case Constant.NO:
	     return;
	   case Constant.MAYBE:
	   case Constant.YES:
	     break;
	   default:
	     Error("DrawPlot.java: {method computeWindowCoord(Command)}: unknown value from table");
	  }

	/* Mapping the first coordinate */
	cmd.a.setLocation(cmd.xa.map(getPl().xLeft[getPl().getViewNo()]   , getPl().xRight[getPl().getViewNo()] , getPl().size.getX()),
			  ((getPl().size.getY() - 1) - cmd.ya.map(getPl().yBottom[getPl().getViewNo()] , getPl().yTop[getPl().getViewNo()]   , getPl().size.getY()))
			  );

	switch(cmd.type)
	  {
	   case cmdType.X         :
	   case cmdType.DOT       :
	   case cmdType.PLUS      :
	   case cmdType.BOX       :
	   case cmdType.DIAMOND   :
	   case cmdType.UTICK     :
	   case cmdType.DTICK     :
	   case cmdType.LTICK     :
	   case cmdType.RTICK     :
	   case cmdType.HTICK     :
	   case cmdType.VTICK     :
	   case cmdType.UARROW    :
	   case cmdType.DARROW    :
	   case cmdType.LARROW    :
	   case cmdType.RARROW    :
	   case cmdType.INVISIBLE :
	   case cmdType.TEXT      :
	     break;
	   case cmdType.DLINE     :
	   case cmdType.LINE      :
	     /* Mapping the second coordinate */
	     cmd.b.setLocation(cmd.xb.map(getPl().xLeft[getPl().getViewNo()]   , getPl().xRight[getPl().getViewNo()] , (int)getPl().size.getX()),
			       ((getPl().size.getY() - 1) - cmd.yb.map(getPl().yBottom[getPl().getViewNo()] , getPl().yTop[getPl().getViewNo()]   , (int)getPl().size.getY()))
			       );
	     break;
	   default                :
	     Error("DrawPlot.java: {method computeWindowCoord(Command) (case 2)}: unknown command type");
	  }

	cmd.a = toMain(cmd.a);
	cmd.b = toMain(cmd.b);
	cmd.mapped = true;
	return;

     } // End of method computeWindowCoords()

   /* Method      - toMain
    * Parameters  - point to be translated
    * Returns     - translated point
    * Description - this method translates the given point to its correct location with respect to the
    * plotter origin.
    */
   private Point2D toMain(Point2D r)
     {
	return(new Point2D.Double((r.getX() + getPl().origin.getX()),
				  (r.getY() + getPl().origin.getY())
				  )
	       );
     } // End of method toMain()

   /* Method      - toSub
    * Parameters  - point to be translated
    * Returns     - translated point
    * Description - this method translates the given point back to its original location with respect to the
    * plotter origin. (Oppsoite functionality of toMain()).
    */
   private Point2D toSub(Point2D r)
     {
	return(new Point2D.Double((r.getX() - getPl().origin.getX()),
				  (r.getY() - getPl().origin.getY())
				  )
	       );
     } // End of method toSub()

   /* Method      - Point2DFromPoint
    * Parameters  - Point
    * Returns     - Point2D.Double version of point
    * Description - creates an object of type Point2D.Double with values from Point, and returns this new object.
    */
   private Point2D Point2DFromPoint(Point p)
     {
	return(new Point2D.Double((double)p.x, (double)p.y));
     } // End of method Point2DFromPoint()

   /* Method      - PointFromPoint2D
    * Parameters  - Point2D
    * Returns     - Point version of Point2D
    * Description - creates an object of type Point with values from Point2D.Double, and returns this new object.
    */
   private Point PointFromPoint2D(Point2D p2)
     {
	return(new Point((int)Math.rint(p2.getX()), (int)Math.rint(p2.getY())));
     } // End of method PointFromPoint2D()


   /* Method      - clampCoord
    * Parameters  - the command who's mapped coordinates are to be clamped
    * Returns     - NULL
    * Description - this method clamps a commands clamped coordiates against an upper limit rectangle
    * of 3000x3000. This is done to avoid very high pixel values during zooming. (Java tends to freeze
    * under such circumstances).
    */
   public void clampCoord(Command cmd)
     {
	double x1 = cmd.a.getX();  double y1 = cmd.a.getY();
	double x2 = cmd.b.getX();  double y2 = cmd.b.getY();

	if (x1 >  Constant.CLAMP) {
	   if (x2-x1 != 0)
	     y1 -= (y2-y1)*(x1-Constant.CLAMP)/(x2-x1);
	   x1 =  Constant.CLAMP;}
	if (x1 < -Constant.CLAMP) {
	   if (x2-x1 != 0)
	     y1 -= (y2-y1)*(x1+Constant.CLAMP)/(x2-x1);
	   x1 = -Constant.CLAMP;}
	if (y1 >  Constant.CLAMP) {
	   if (y2-y1 != 0)
	     x1 -= (x2-x1)*(y1-Constant.CLAMP)/(y2-y1);
	   y1 =  Constant.CLAMP;}
	if (y1 < -Constant.CLAMP) {
	   if (y2-y1 != 0)
	     x1 -= (x2-x1)*(y1+Constant.CLAMP)/(y2-y1);
	   y1 = -Constant.CLAMP;}

	if (x2 >  Constant.CLAMP) {
	   if (x1-x2 != 0)
	     y2 -= (y1-y2)*(x2-Constant.CLAMP)/(x1-x2);
	   x2 =  Constant.CLAMP;}
	if (x2 < -Constant.CLAMP) {
	   if (x1-x2 != 0)
	     y2 -= (y1-y2)*(x2+Constant.CLAMP)/(x1-x2);
	   x2 = -Constant.CLAMP;}
	if (y2 >  Constant.CLAMP) {
	   if (y1-y2 != 0)
	     x2 -= (x1-x2)*(y2-Constant.CLAMP)/(y1-y2);
	   y2 =  Constant.CLAMP;}
	if (y2 < -Constant.CLAMP) {
	   if (y1-y2 != 0)
	     x2 -= (x1-x2)*(y2+Constant.CLAMP)/(y1-y2);
	   y2 = -Constant.CLAMP;}

	cmd.a.setLocation(x1, y1);
	cmd.b.setLocation(x2, y2);

     } // End of method clampCoord

   /* Method      - zoomInCoord
    * Parameters  - the current window range, the drag start and end points,
    * the size of the window and the new window range
    * Returns     - NULL
    * Description - sets the new window range to the appropriate value to zoom into the selected region.
    */
   public void zoomInCoord(Coord first, Coord last,
			   int x1, int x2,
			   double n,
			   Coord newFirst, Coord newLast
			   )
     {
	int xf = 0;
	int xl = 0;

	if(x1 < x2) { xf = x1; xl = x2; }
	else        { xf = x2; xl = x1; }

	newFirst.unmap(first, last, n, xf);
	newLast.unmap(first, last, n, xl);

	if(newFirst.compareTo(newLast) == 0)
	  {
	     newFirst.set(first.get());
	     newLast.set(last.get());
	  }

     } // End of method zoomInCoord()

   /* Method      - dragCoord
    * Parameters  - axis type, the current window range, the drag start and end points,
    * the size of the window and the new window range
    * Returns     - NULL
    * Description - displaces all the coordinates by the drag distance. (gives a scrolling effect)
    */
   public void dragCoord(int type,
			 Coord first, Coord last,
			 int x1, int x2,
			 double n,
			 Coord newFirst, Coord newLast
			 )
     {
	Coord c1 = null;    // Unmapped newFirst
	Coord c2 = null;    // unmapped newLast

	switch(type)
	  {
	   case cType.U_INT:
	     c1 = new cUnsigned();
	     c2 = new cUnsigned();
	     break;

	   case cType.INT:
	     c1 = new cSigned();
	     c2 = new cSigned();
	     break;

	   case cType.DOUBLE:
	     c1 = new cDouble();
	     c2 = new cDouble();
	     break;

	   case cType.TIMEVAL:
	     c1 = new cTimeVal();
	     c2 = new cTimeVal();
	     break;

	   case cType.DTIME:
	     c1 = new cDTime();
	     c2 = new cDTime();
	     break;

	   default:
	     Error("DrawPlot.java {method dragCoord()}: unknown coord type");
	  }

	c1.unmap(first, last, n, x2);
	c2.unmap(first, last, n, x1);

	switch(c1.compareTo(c2))
	  {
	   case 1:
	     c1.subtract(c2);
	     newFirst.set(first.get());
	     newFirst.add(c1);
	     newLast.set(last.get());
	     newLast.add(c1);
	     break;
	   case 0:
	     newFirst.set(first.get());
	     newLast.set(last.get());
	     break;
	   case -1:
	     c2.subtract(c1);
	     newFirst.set(first.get());
	     newFirst.subtract(c2);
	     newLast.set(last.get());
	     newLast.subtract(c2);
	     break;
	  }

	return;
     } // End of method dragCoord()

   /* Method      - showDistance
    * Parameters  - the graphics2D component
    * Returns     - NULL
    * Description - this is MARK ALLMAN'S triangulation distance tool. It is taken as-is from TIM SHEPARD'S xplot.
    */
   public void showDistance(Graphics2D g2)
     {
	Point  p        = new Point(0, 0);
	Coord  axdist   = null;
	Coord  aydist   = null;
	String s        = null;
	Double dObj     = null;
	double pwidth   = getPl().size.getX();
	double pheight  = getPl().size.getY();
	double xscale   = 0;
	double yscale   = 0;
	double xdist    = 0;
	double ydist    = 0;
	double slope    = 0;
	double hyp_dist = 0;
	long   pxdist   = 0;
	long   pydist   = 0;
	NumberFormat nf = NumberFormat.getInstance();  // Returns the default number format for the current default locale
	nf.setGroupingUsed(false);                     // Turn off the grouping (placing commas' every 3 digits)
	nf.setMaximumFractionDigits(3);                // Set the fractional part to a max of 10 places

	/* Set the graphics rendering hints */
	g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

	/* Get the drag start and end points */
	Point dragStart = PointFromPoint2D(m1);
	Point dragEnd   = PointFromPoint2D(m2);

	/* Setting the color of the pointer marks */
	g2.setPaint(Color.white);

	/* Setting the stroke to XOR mode */
	setExclusiveOr(g2);

	g2.drawLine(dragStart.x , dragStart.y , dragEnd.x   , dragEnd.y);
	g2.drawLine(dragStart.x , dragStart.y , dragStart.x , dragEnd.y);
	g2.drawLine(dragStart.x , dragEnd.y   , dragEnd.x   , dragEnd.y);

	/* Set the font */
	Font f = new Font("LucidaBrightRegular", Font.PLAIN, Constant.FONTSIZE + 3);
	g2.setFont(f);

	pxdist = dragEnd.x   - dragStart.x;
	pydist = dragStart.y - dragEnd.y;
	xscale = pxdist / pwidth;
	yscale = pydist / pheight;

	switch(getPl().xType)
	  {
	   case cType.U_INT   : axdist = new cUnsigned(getPl().xRight[getPl().getViewNo()-1]) ; break; // The viewNo was inremented when the mouse button was pressed, hence the -1
	   case cType.INT     : axdist = new cSigned(getPl().xRight[getPl().getViewNo()-1])   ; break;
	   case cType.DOUBLE  : axdist = new cDouble(getPl().xRight[getPl().getViewNo()-1])   ; break;
	   case cType.TIMEVAL : axdist = new cTimeVal(getPl().xRight[getPl().getViewNo()-1])  ; break;
	   case cType.DTIME   : axdist = new cDTime(getPl().xRight[getPl().getViewNo()-1])    ; break;
	   default            : Error("DrawPlot.java {method showDistance()}: unknown x-coord type");
	  }

	switch(getPl().yType)
	  {
	   case cType.U_INT   : aydist = new cUnsigned(getPl().yTop[getPl().getViewNo()-1])   ; break;
	   case cType.INT     : aydist = new cSigned(getPl().yTop[getPl().getViewNo()-1])     ; break;
	   case cType.DOUBLE  : aydist = new cDouble(getPl().yTop[getPl().getViewNo()-1])     ; break;
	   case cType.TIMEVAL :	aydist = new cTimeVal(getPl().xRight[getPl().getViewNo()-1])  ; break;
	   case cType.DTIME   : aydist = new cDTime(getPl().xRight[getPl().getViewNo()-1])    ; break;
	   default            : Error("DrawPlot.java {method showDistance()}: unknown y-coord type");
	  }

	axdist.subtract(getPl().xLeft[getPl().getViewNo()-1]);
	aydist.subtract(getPl().yBottom[getPl().getViewNo()-1]);

	if (getPl().xType != cType.TIMEVAL)
	  {
	     s = axdist.unparse();
	     dObj = new Double(s);
	     xdist = dObj.doubleValue();
	     s = null;
	  }
	else
	  xdist = axdist.get();

	xdist *= xscale;

	if (getPl().yType != cType.TIMEVAL)
	  {
	     s = aydist.unparse();
	     dObj = new Double(s);
	     ydist = dObj.doubleValue();
	     s = null;
	  }
	else
	  ydist = aydist.get();

	ydist *= yscale;

	if(xdist == 0 && ydist == 0) return;

	slope    = ydist / xdist;
	hyp_dist = Math.sqrt((xdist * xdist) + (ydist * ydist));

	if (xdist > 0)
	    p.x = dragStart.x - 65;
	else
	    p.x = dragStart.x + 10;

	p.y = (int)(dragStart.y - (pydist / 2));

	if (getPl().yType != cType.TIMEVAL)
	  s = new String(nf.format(ydist) + " " + getPl().yUnits);
	else
	  s = new String(nf.format(ydist) + " secs");
	g2.drawString(s, p.x, p.y);

	p.x = (int)(dragStart.x + (pxdist / 2));
	if (ydist > 0)
	    p.y = dragEnd.y - 5;
	else
	    p.y = dragEnd.y + 15;

	if (getPl().xType != cType.TIMEVAL)
	  s = new String(nf.format(xdist) + " " + getPl().xUnits);
	else
	  s = new String(nf.format(xdist) + " secs");
	g2.drawString(s, p.x, p.y);

	p.x = ((dragEnd.x - dragStart.x) / 2) + dragStart.x;
	p.y = ((dragEnd.y - dragStart.y) / 2) + dragStart.y;
	if (xdist > 0)
	    p.x += 15;
	else
	    p.x -= 85;
	if (ydist > 0)
	    p.y += 10;
	else
	    p.y -= 20;

	String xUnits = getPl().xType == cType.TIMEVAL?"sec":getPl().xUnits.equals("")?"units":getPl().xUnits;
	String yUnits = getPl().yType == cType.TIMEVAL?"sec":getPl().yUnits.equals("")?"units":getPl().yUnits;
	s = new String("s : " + nf.format(slope) + " " + yUnits + "/" + xUnits);
	g2.drawString(s, p.x, p.y);

/* Removed the diagonal distance, it is wrong. The value was being calculated
 * without taking into account the units of the x & y axis. Since it is not
 * always possible to convert the different axis types from one form to the other,
 * it is safe to remove this value all together.

	p.y += 15;
	s = new String("d : " + nf.format(hyp_dist));
	g2.drawString(s, p.x, p.y);
 */

     } // End of method showDistance()


   /* Method      - emitPS
    * Parameters  - postscript file buffer, and the plotter state
    * Returns     - NULL
    * Description - emits a postscript file of the current plotter view
    */
   public void emitPS(BufferedWriter out, int state)
     {
	/* Error checking first */
	if(out == null)
	  return;

	/* Declarations and initializations */
	Command cmd;
	int counter;
	Color currentColor;
	boolean finishedDecoration, outputDecoration;
	double figWidth;
	double figHeight;
	double lMargin,rMargin,bMargin,tMargin;
	double bbllx,bblly,bburx,bbury;
	double limitHeight;
	Point2D savedOrigin = new Point2D.Double(getPl().origin.getX(), getPl().origin.getY());
	Point2D savedSize   = new Point2D.Double(getPl().size.getX(), getPl().size.getY());

	String ColorNames[] = {"white", "green", "red", "blue", "yellow", "purple", "orange", "magenta", "pink", "gray20"};
	String GrayPSrep[] = {
	     "0 setgray",			/* white */
	     ".3 setgray",			/* green */
	     ".5 setgray",			/* red */
	     ".7 setgray",			/* blue */
	     ".9 setgray",			/* yellow infreq */
	     ".6 setgray",			/* purple infreq */
	     ".8 setgray",
	     ".4 setgray",
	     ".95 setgray",
	     ".2 setgray"			/* 20% gray */
	};
	String ColorPSrep[] = {
	     "0 setgray", "0 1 0 setrgbcolor", "1 0 0 setrgbcolor", "0 0 1 setrgbcolor", "1 1 0 setrgbcolor", "0.7 0.3 1.0 setrgbcolor", "1.0 0.7 0.0 setrgbcolor", "1 0 1 setrgbcolor", "1.0 0.5 0.5 setrgbcolor", "0.2 0.2 0.2 setrgbcolor"
	};

	Color Colors[] = {
	     Color.white, Color.green, Color.red, Color.blue, Color.yellow, new Color((float)0.7, (float)0.3, (float)1.0), new Color((float)1.0, (float)0.7, (float)0.0), Color.magenta, Color.pink, new Color((float)0.2, (float)0.2, (float)0.2)
	};

	/* Because xplot only deals with integer output coords, use PS units
	 * which are a multiple of the pixels per inch of the actual printer.
	 * By doing so, some undesirable effects are avoided.
	 * 7200 is the least common multiple of 1440 and 1200.
	 * There is some code below that just might depend on this being
	 * a multiple of 600.   So think carefully if you are tuning this.
	 *
         * Constant.PIXELS_PER_INCH  = 7200
	 *
	 * defined in "Constant.java"
	 */

	/* Calculate new window coordinates for everything. */
	switch(state)
	  {
	   default:
	     Error("jPlot: {DrawPlot.java: emitPS(BufferedWriter, int): unexpected plotter state}");

	   case State.PRINTING:
	     /* landscape mode */
	     lMargin = 1.25;
	     rMargin = 0.75;
	     bMargin = 0.85;
	     tMargin = 0.75;
	     figWidth = 11;
	     figHeight = 8.5;
	     limitHeight = figHeight;
	     break;

	   case State.FIGING:
	   case State.THINFIGING:
	     /* portrait mode, for use in documents */
	     lMargin = 0.7;
	     rMargin = 0.2;
	     bMargin = 0.3;
	     tMargin = 0.15;
	     figWidth = 6.0;
	     figHeight = 4.0; /* changed below if THINFIGING */
	     limitHeight = 7.5; /* biggest figure height TeX can handle */
	     break;
	  }

	if(state == State.THINFIGING)
	  figHeight = 2.75;

	if (getPl().aspectRatio != 0.0)
	  {

	     double plotterWidth  = figWidth  - (lMargin + rMargin);
	     double plotterHeight = figHeight - (bMargin + tMargin);

	     /* stretch vertically to make aspect ratio correct */
	     plotterHeight = 1/getPl().aspectRatio * plotterWidth;
	     figHeight = plotterHeight + (bMargin + tMargin);

	     if (figHeight > limitHeight)
	       {
		  /* figure is too tall, scale back plotter width and height equally to fit */
		  double scale = (limitHeight - (bMargin + tMargin)) / plotterHeight;
		  plotterHeight *= scale;
		  plotterWidth  *= scale;

	       }

	     figWidth = plotterWidth   + (lMargin + rMargin);
	     figHeight = plotterHeight + (bMargin + tMargin);
	  }

	/* we translate the origin to provide margins, so bb is 0 to figsize */
	bbllx = 0.0 * 72.0;
	bblly = 0.0 * 72.0;
	bburx = figWidth  * 72.0;
	bbury = figHeight * 72.0;

	getPl().origin.setLocation(0.0, 0.0); /* not necessary? */
	getPl().size.setLocation((int)((figWidth - lMargin - rMargin) * Constant.PIXELS_PER_INCH),
			    (int)((figHeight - tMargin - bMargin) * Constant.PIXELS_PER_INCH)
			    );

	/* code copied from size_window above */

	/* Clear the old axis before making the new one. (New axis is created everytime the plotter view changes) */
	cmd = null;
	for( ; ;)
	  {
	     cmd = (Command)getPl().commands.get(0);  // Axis is always at the begining of the list
	     if(cmd.decoration == true)
	       getPl().commands.remove(0);
	     else       //break else we will land up deleting the labels too since they are marked as decoration
	       break;
	  }

	/* Creating the axis */
	getPl().makeAxis();

	/* Mapping all the command coordinates to window coordinates */
	try
	  {
	     int size = getPl().commands.size();
	     for(int i = 0; i < size; i++)
	       {
		  cmd = (Command)getPl().commands.get(i);
		  computeWindowCoords(cmd);
	       }
	  }
	catch(IndexOutOfBoundsException e)
	  {
	     System.out.println(e.toString());
	  }

	/*
	 * Print out the prologue for the picture.
	 */

	try
	  {
	     out.write("%!PS\n");

	     /* Bracket the PS program with gsave/grestore so these page descriptions
	      can be concatenated, then printed. */
	     out.write("%%BoundingBox: ");
	     out.write(new String((int)bbllx + " " + (int)bblly + " " + (int)bburx + " " + (int)bbury + "\n"));
	     out.write("%%EndComments\n");
	     out.write("/Docolors true def\n"); /* gdt - delete to not print in color */
	     out.write("gsave\n");

	     /* Set up scale */
	     out.write("\n" +
"%/sign { dup 0 gt { pop 1 } { 0 lt { -1 } { 0 } ifelse } ifelse } def\n" +
"\n" +
"%matrix currentmatrix\n" +
"%aload pop\n" +
"%6 2 roll sign\n" +
"%6 1 roll sign\n" +
"%6 1 roll sign\n" +
"%6 1 roll sign\n" +
"%6 1 roll\n" +
"%matrix astore setmatrix\n" +
"\n");

  out.write(new String("72 " + Constant.PIXELS_PER_INCH + " div dup scale\n"));

  out.write(new String("/theta {" + ((state == State.PRINTING) ? Constant.PIXELS_PER_INCH/150 : Constant.PIXELS_PER_INCH/300) + " mul} def\n"));

  /* Set up units of measurement. */
  out.write(new String("/inch {" + Constant.PIXELS_PER_INCH + " mul} def\n"));
  out.write("/pt {inch 72 div} def\n" +
"%\n" +
"%\n" +
"/tfont /Times-Bold findfont 12 pt scalefont def\n" +
"%\n" +
"/lfont /Times-Roman findfont 10 pt scalefont def\n" +
"%\n" +
"%tfont /FontBBox get\n" +
"%  aload pop\n" +
"%  tfont /FontMatrix get dtransform pop /tascent exch def\n" +
"%  tfont /FontMatrix get dtransform pop neg /tdescent exch def\n" +
"lfont /FontBBox get\n" +
"  aload pop\n" +
"  lfont /FontMatrix get dtransform pop 0.65 mul /lascent exch def\n" +
"  lfont /FontMatrix get dtransform pop neg /ldescent exch def\n" +
"% begin gdt mod\n" +
"% define font for xplot characters\n" +
"/BuildCharDict 10 dict def\n" +
"/Xplotfontdict 7 dict def\n" +
"Xplotfontdict begin\n" +
"  /FontType 3 def\n" +
"  /FontMatrix [1 0 0 1 0 0] def\n" +
"  /FontBBox [-1 -1 1 1]def\n" +
"  /Encoding 256 array def\n" +
"  0 1 255 {Encoding exch /.notdef put} for\n" +
"  Encoding (.) 0 get /xplotfontdot put\n" +
"  /CharacterDefs 3 dict def\n" +
"  CharacterDefs /.notdef {} put\n" +
"  CharacterDefs /xplotfontdot\n" +
"    { newpath\n" +
"	0 0 1 0 360 arc fill\n" +
"    } put\n" +
"  /BuildChar\n" +
"    { BuildCharDict begin\n" +
"	/char exch def\n" +
"	/fontdict exch def\n" +
"	/charname fontdict /Encoding get\n" +
"	char get def\n" +
"	/charproc fontdict /CharacterDefs\n" +
"        get charname get def\n" +
"	1 0 -1 -1 1 1 setcachedevice\n" +
"	gsave charproc grestore\n" +
"      end\n" +
"    } def\n" +
"end\n" +
"/XplotFont Xplotfontdict definefont pop\n" +
"% scale font according to theta\n" +
"/dotsetup { /dotfont /XplotFont findfont 4 theta scalefont def } def\n" +
"% DONE gdt mod\n" +
"%define procedures for each xplot primitive.\n" +
"% x y x --\n" +
"/x {moveto 8 8 rlineto -16 -16 rlineto\n" +
"    8 8 rmoveto\n" +
"    -8 8 rlineto 16 -16 rlineto} def\n" +
"% x y ?arrow --\n" +
"/darrow {moveto 8 theta 8 theta rmoveto -8 theta -8 theta rlineto\n" +
"         -8 theta 8 theta rlineto } def\n" +
"/uarrow {moveto -8 theta -8 theta rmoveto 8 theta 8 theta rlineto\n" +
"         8 theta -8 theta rlineto } def\n" +
"/rarrow {moveto -8 theta 8 theta rmoveto 8 theta -8 theta rlineto\n" +
"         -8 theta -8 theta rlineto } def\n" +
"/larrow {moveto 8 theta 8 theta rmoveto -8 theta -8 theta rlineto\n" +
"         8 theta -8 theta rlineto } def\n" +
"%x y x y line --\n" +
"/line {moveto lineto} def\n" +
"%x y dot --\n" +
"% begin gdt mod\n" +
"/dot { moveto dotfont setfont (.) show } def\n" +
"%/dot {stroke 8 theta 0 360 arc fill} def\n" +
"% end gdt mod\n" +
"%x y . --\n" +
"% begin gdt mod\n" +
"/. { moveto dotfont setfont (.) show } def\n" +
"%/. {stroke 8 theta 0 360 arc fill} def\n" +
"% end gdt mod\n" +
"%x y plus --\n" +
"/plus {moveto -8 theta 0 rmoveto 16 theta 0 rlineto\n" +
"       -8 theta -8 theta rmoveto 0 16 theta rlineto} def\n" +
"%x y + --\n" +
"/+ {moveto -8 theta 0 rmoveto 16 theta 0 rlineto\n" +
"       -8 theta -8 theta rmoveto 0 16 theta rlineto} def\n" +
"%x y box --\n" +
"/box {moveto -8 theta -8 theta rmoveto\n" +
"      16 theta 0 rlineto\n" +
"      0 16 theta rlineto\n" +
"      -16 theta 0 rlineto\n" +
"      0 -16 theta rlineto} def\n" +
"%x y diamond --\n" +
"/diamond { moveto 0 theta 24 theta rmoveto\n" +
"           -24 theta -24 theta rlineto\n" +
"            24 theta -24 theta rlineto\n" +
"            24 theta  24 theta rlineto\n" +
"           -24 theta  24 theta rlineto} def\n" +
"%x y ?tick --\n" +
"/utick {moveto 0 6 theta rlineto} def\n" +
"/dtick {moveto 0 -6 theta rlineto} def\n" +
"/ltick {moveto -6 theta 0 rlineto} def\n" +
"/rtick {moveto 6 theta 0 rlineto} def\n" +
"/htick {moveto -6 theta 0 rmoveto 12 theta 0 rlineto} def\n" +
"/vtick {moveto 0 -6 theta rmoveto 0 12 theta rlineto} def\n" +
"%Separate functions for each text position.\n" +
"%x y string ?text --\n" +
"/space 6 pt def\n" +
"% Set the font, figure out the width.\n" +
"% x y string tsetup string x width y\n" +
"/tsetup {lfont setfont dup stringwidth pop exch\n" +
"         4 1 roll exch} def\n" +
"%CENTER\n" +
"/ctext {tsetup lascent 2 div sub\n" +
"        3 1 roll 2 div sub exch\n" +
"% stack should now be string x y\n" +
"        moveto show} def\n" +
"%ABOVE\n" +
"/atext {tsetup space ldescent add add\n" +
"        3 1 roll 2 div sub exch moveto show} def\n" +
"%BELOW\n" +
"/btext {tsetup space lascent add sub\n" +
"        3 1 roll 2 div sub exch moveto show} def\n" +
"%TO_THE_LEFT\n" +
"/ltext {tsetup lascent 2 div sub\n" +
"        3 1 roll space add sub exch moveto show} def\n" +
"%TO_THE_RIGHT\n" +
"/rtext {tsetup lascent 2 div sub\n" +
"        3 1 roll pop space add exch moveto show} def\n" +
"\n");

	     out.write("/XPlotUseColor\n");
	     out.write("/Docolors where { pop true } { false } ifelse\n");
	     out.write("%product (Ghostscript) eq or\n");
	     out.write("def\n");
	     out.write("XPlotUseColor\n{\n");
	     for (int i = 0; i < Constant.NCOLORS; i++ )
	       out.write("/color" + ColorNames[i] + " { " + ColorPSrep[i] + " } def\n");
	     out.write("}\n{\n");
	     for (int i = 0; i < Constant.NCOLORS; i++ )
	       out.write("/color" + ColorNames[i] + " { " + GrayPSrep[i] + " } def\n");
	     out.write("}\nifelse\n");

	     out.write("%% string title --\n");
	     out.write("/title {tfont setfont dup stringwidth pop neg\n");
	     out.write("        " + ((int)Math.rint(getPl().size.getX())) + " add 2 div\n");
	     out.write("        " + ((int)Math.rint(getPl().size.getY())) + "\n");
	     out.write("        moveto show} def\n");

	     out.write("%% string xlabel --\n");
	     out.write("/xlabel {tfont setfont dup stringwidth pop neg\n");
	     out.write("         " + ((int)Math.rint(getPl().size.getX())) + " add\n");
	     out.write("         0 lascent ldescent add 3 mul sub\n");
	     out.write("         moveto show} def\n");

	     out.write("%% string ylabel --\n");
	     out.write("/ylabel {tfont setfont dup stringwidth pop neg\n");
	     out.write("         0 add 2 div\n");
	     out.write("         " + ((int)Math.rint(getPl().size.getY())) + " lascent ldescent add 1 mul add\n");
	     out.write("         moveto show} def\n");

	     /* Final prelude:  Change scale, move & rotate origin for landscape
	      printing & provide for a margin. */

	     /* Orient for landscape printing, margin to lower left corner. */
	     if(state == State.PRINTING)
	       {
		  out.write("-90 rotate -11 inch 0 inch translate\n");
	       }
	     else
	       {
		  out.write("\n" +
"/notintex { currentdict userdict eq } def\n" +
"notintex { 1.5 inch 5.0 inch translate } if\n" +
"\n");
	       }

	     /* Move origin to create left & bottom margins. */
	     out.write(lMargin + " inch " + bMargin + " inch translate\n");
	     /* Relatively thick lines for axes & ticks. */
	     out.write("4 theta setlinewidth newpath\n");

	     out.write("\n% The actual drawing:\n\n");

	     /*
	      * Now do all the drawing commands.
	      */
	     finishedDecoration = outputDecoration = false;
	     counter = 0;
	     currentColor = null;

	     int size = getPl().commands.size();
	     for(int i = 0; i < size; i++)
	       {
		  cmd = (Command)getPl().commands.get(i);
		  if((finishedDecoration == true) && (outputDecoration == false))
		    {
		       /* Thinner lines for the actual drawing. */
		       out.write("stroke\n");
		       out.write("/theta {" + ((state == State.PRINTING) ? Constant.PIXELS_PER_INCH/300 : Constant.PIXELS_PER_INCH/600) + " mul} def\n");
		       out.write("2 theta setlinewidth\n");
		       out.write("dotsetup\n");	/* gdt */
		       /* Set clipping region so that we don't draw past the axes. */
		       out.write("0 0 moveto " + ((int)Math.rint(getPl().size.getX())) + " 0 lineto " + ((int)Math.rint(getPl().size.getX())) + " " + ((int)Math.rint(getPl().size.getY())) + " lineto\n");
		       out.write("0 " + ((int)Math.rint(getPl().size.getY())) + " lineto 0 0 lineto clip newpath\n");
		       outputDecoration = true;
		    }

		  if((finishedDecoration == true)  &&
		     ((cmd.decoration)             ||
		      (cmd.type == cmdType.TITLE)  ||
		      (cmd.type == cmdType.XLABEL) ||
		      (cmd.type == cmdType.YLABEL)
		     )
		    )
		    continue;

		  if((finishedDecoration == false)   &&
		     ! ((cmd.decoration)             ||
			(cmd.type == cmdType.TITLE)  ||
		        (cmd.type == cmdType.XLABEL) ||
		        (cmd.type == cmdType.YLABEL)
		       )
		    )
		    {
		       /* Check if we need to reset the counter for the second loop.
			* (Remember, we loop twice, once for decoration, and once for data)
			*/
		       if(((i+1) == size) && (finishedDecoration == false))
			 {
			    finishedDecoration = true;  // First loop is over
			    i = -1;                     // Set to -1 and not 0 since it will increment to 0 when the for-loop is visited again
			 }
		       continue;
		    }

		  if(cmd.mapped == true)
		    {
		       if((args.isMono() == false) && (finishedDecoration == true) && ((cmd.color != null) || (currentColor == null)))
			 {
			    if(counter > 0)
			      {
				 counter = 0;
				 out.write("stroke\n");
			      }

			    if(cmd.color != null)
			      currentColor = cmd.color;
			    else
			      currentColor = Color.white;

			    String cColor = null;
			    for(int j = 0; j < Colors.length; j++)
			      {
				 if(currentColor.equals(Colors[j]))
				   cColor = new String(ColorNames[j]);
			      }

			      out.write("color" + cColor + "\n");
			 }

		       switch(cmd.type)
			 {
			  case cmdType.X:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " x\n");
			    break;
			  case cmdType.DOT:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " dot\n");
			    break;
			  case cmdType.PLUS:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " plus\n");
			    break;
			  case cmdType.BOX:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " box\n");
			    break;
			  case cmdType.DIAMOND:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " diamond\n");
			    break;
			  case cmdType.UTICK:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " utick\n");
			    break;
			  case cmdType.DTICK:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " dtick\n");
			    break;
			  case cmdType.LTICK:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " ltick\n");
			    break;
			  case cmdType.RTICK:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " rtick\n");
			    break;
			  case cmdType.HTICK:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " htick\n");
			    break;
			  case cmdType.VTICK:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " vtick\n");
			    break;
			  case cmdType.UARROW:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " uarrow\n");
			    break;
			  case cmdType.DARROW:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " darrow\n");
			    break;
			  case cmdType.LARROW:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " larrow\n");
			    break;
			  case cmdType.RARROW:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " rarrow\n");
			    break;
			  case cmdType.DLINE:
			    out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " +\n");
			    out.write(xToPSx(cmd.b.getX()) + " " + yToPSy(cmd.b.getY()) + " +\n");
			    counter += 2;
			    /* fall through and draw the line */
			  case cmdType.LINE:
			    out.write(xToPSx(cmd.a.getX()) + " " +  yToPSy(cmd.a.getY()) + " " +
				      xToPSx(cmd.b.getX()) + " " +  yToPSy(cmd.b.getY()) + " line\n");
			    break;
			  case cmdType.TEXT:
			     switch (cmd.position)
			      {
			       case Position.CENTERED:
 				 out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " (" + escParen(cmd.text) + ") ctext\n");
				 break;
			       case Position.ABOVE:
				 out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " (" + escParen(cmd.text) + ") atext\n");
				 break;
			       case Position.BELOW:
 				 out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " (" + escParen(cmd.text) + ") btext\n");
				 break;
			       case Position.LEFT:
 				 out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " (" + escParen(cmd.text) + ") ltext\n");
				 break;
			       case Position.RIGHT:
 				 out.write(xToPSx(cmd.a.getX()) + " " + yToPSy(cmd.a.getY()) + " (" + escParen(cmd.text) + ") rtext\n");
				 break;
			      }
			    break;

			  case cmdType.TITLE:
			    out.write("(" + escParen(cmd.text) + ") title\n");
			    break;
			  case cmdType.XLABEL:
			    out.write("(" + escParen(cmd.text) + ") xlabel\n");
			    break;
			  case cmdType.YLABEL:
			    out.write("(" + escParen(cmd.text) + ") ylabel\n");
			    break;
			  case cmdType.INVISIBLE:
			    break;
			  default:
			    break;
			 }

		       if (++counter > 50)
			 {
			    counter = 0;
			    out.write("stroke\n");
			 }
		    }

		  /* Check if we need to reset the counter for the second loop.
		   * (Remember, we loop twice, once for decoration, and once for data)
		   */
		  if(((i+1) == size) && (finishedDecoration == false))
		    {
		       finishedDecoration = true;  // First loop is over
		       i = -1;                     // Set to -1 and not 0 since it will increment to 0 when the for-loop is visited again
		    }

	       } // End of for-loop

	     out.write("stroke ");
	     if(state == State.PRINTING)
	       out.write("showpage ");
	     else
	       out.write("notintex { showpage } if\n");

	     out.write("grestore\n");
	     /* Close the output stream */
	     out.close();

	  }
	catch(IndexOutOfBoundsException e)
	  {
	     Error(e.toString());
	  }
	catch(NoSuchElementException e)
	  {
	     Error(e.toString());
	  }
	catch(IOException e)
	  {
	     Error(e.toString());
	  }

	/* restore the origin and size */
	getPl().origin.setLocation(savedOrigin.getX(), savedOrigin.getY());
	getPl().size.setLocation(savedSize.getX(), savedSize.getY());

     } // End of method emitPS()


   /* Method      - openPSFile
    * Parameters  - the file name
    * Returns     - a buffered writer for the postscript file to be written to.
    * Description - this method opens a file with a name that is either the title of the plot,
    * or "jPlot.ps" with a version number appended.
    */
   public BufferedWriter openPSFile(String fileName)
     {
	BufferedWriter out = null;    // The buffered writer for the PS file

	/* Open the buffered writer for the new file name */
	try
	  {
	     out = new BufferedWriter(new FileWriter(fileName));
	  }
	catch(IOException e)
	  {
	     System.out.println(e.toString());
       throw new RuntimeException(e);
//	     System.exit(1);
	  }

	/* Return the new buffered writer to the method emitPS() */
	return(out);

     } // End of method openPSFile()


   /* Methods     - xToPSx & yToPSy
    * Parameters  - double point x/y
    * Returns     - int point x/y
    * Description - rounds & translates the point appropriate for postscript plotting.
    */
   private int xToPSx(double x)
     {
	return((int)Math.rint(x));

     } // End of method xToPSx()

   private int yToPSy(double y)
     {
	return((int)Math.rint(getPl().size.getY() - y));

     } // End of method yToPSy()

   /* Method      - escParen
    * Parameters  - the string to be processed
    * Returns     - the processed string
    * Description - this method escapes all the parenthesis that may be present in the provided string.
    * This is needed for postscript plotting. Eg. "(jPlot)" becomes "\(jPlot\)"
    */
   private String escParen(String text)
     {
	StringBuffer newText = new StringBuffer(text);

	for(int i = 0; i < newText.length(); i++)
	  if((newText.charAt(i) == '(') || (newText.charAt(i) == ')'))
	    newText.insert(i++, '\\');

	/* Return the processed string */
	return(newText.toString());
     }

   /* Method      - emitPng
    * Parameters  - FileOutputStream to write to, and graphics image from which the png image is to be created
    * Returns     - NULL
    * Description - this method creates a png encoded image of the current view of the plot.
    * NOTES       - This class makes use of the PNG Encoder written by J. David Eisenberg(david@catcode.com),
    * http://catcode.com/pngencoder/. It is distributed with "jPlot" under the GPL license.
    */
   public void emitPng(FileOutputStream out, Image image)
     {
	/* Check if we have a valid output stream */
	if(out == null)
	  return;

	PngEncoder pe = new PngEncoder(image);         // The image encoder
	byte[] pngBytes = pe.pngEncode();              // The encoded bytes
	try
	  {
	     out.write(pngBytes, 0, pngBytes.length);  // Write the bytes to the file
	     out.close();                              // Close the file
	  }
	catch(FileNotFoundException e)
	  {
	     System.out.println(e.toString());
	  }
	catch(IOException e)
	  {
	     System.out.println(e.toString());
	  }

     } // End of method emitPng()


   /* Method      - openPngFile
    * Parameters  - the file name
    * Returns     - a file output stream for the png file to be written to.
    * Description - this method opens a file with a name that is either the title of the plot,
    * or "jPlot.png" with a version number appended.
    */
   public FileOutputStream openPngFile(String fileName)
     {
	FileOutputStream out = null;    // The buffered writer for the PS file

	/* Open the buffered writer for the new file name */
	try
	  {
	     out = new FileOutputStream(fileName);
	  }
	catch(IOException e)
	  {
	     Error(e.toString());
	  }

	/* Return the new buffered writer to the method emitPng() */
	return(out);

     } // End of method openPngFile()


   /* Method      - Error
    * Parameters  - error message
    * Returns     - NULL
    * Description - prints the error message and exits.
    */
   private void Error(String msg)
     {
	System.out.println(msg);
  throw new RuntimeException(msg);
//	System.exit(1);
     }

   /* DEBUGGING FUNCTIONS */
   public void mousePoints()
     {
	System.out.println("X1: " + m1.getX() + "\t" + "Y1: " + m1.getY());
	System.out.println("X2: " + m2.getX() + "\t" + "Y2: " + m2.getY());
     }

   public void printPoint(Point p)
     {
	System.out.println("X: " + p.x + "\t" + "Y: " + p.y);
     }

   public void printPoint(Point2D p)
     {
	System.out.println("X: " + p.getX() + "\t" + "Y: " + p.getY());
     }

  public void setPl(Plotter pl) {
    this.pl = pl;
  }

  public Plotter getPl() {
    return pl;
  }

  public void setCanvas(Image canvas) {
    this.canvas = canvas;
  }

  public Image getCanvas() {
    return canvas;
  }

} // End of class jPlot

