view src/samer/tools/Plotter.java @ 0:bf79fb79ee13

Initial Mercurial check in.
author samer
date Tue, 17 Jan 2012 17:50:20 +0000
parents
children
line wrap: on
line source
/*
 *	Copyright (c) 2000, Samer Abdallah, King's College London.
 *	All rights reserved.
 *
 *	This software is provided AS iS and WITHOUT ANY WARRANTY; 
 *	without even the implied warranty of MERCHANTABILITY or 
 *	FITNESS FOR A PARTICULAR PURPOSE.
 */

package samer.tools;
import  samer.core.*;
import  samer.core.util.*;
import  java.awt.*;

/**
	<p>
	A Plotter object is a Canvas with its own Graphics
	(ie a GCanvas) which has two LinearMaps
	to map a portion of 2d space to the window.
	It can optionally draw the X and Y axes.

	<p>
	The inner class Pen provides access to drawing
	functions in the real-valued coordinate space.
 */

public class Plotter extends samer.core.util.heavy.VCanvas
{
	/** for passing to setAxes(int) */
	public static final int NEITHER = 0;
	public static final int YAXIS = 2;
	public static final int XAXIS = 1;
	public static final int BOTH = 3;
	public IMap	xmap, ymap;
	
	int			axesFlags=BOTH;
	Color		axesColor;

	/** 
		Default constructor makes Plotter with both axes
		drawn, in grey. LinearMaps take their default 
		setup.
		
		@see samer.core.util.LinearMap
	 */

	public Plotter()
	{
		xmap = new LinearMap();
		ymap = new LinearMap();

		axesColor=Shell.getColor("axesColor",Color.gray);
	}

	public void exposeMaps() {
		exposeCommands(new VMap(xmap));
		exposeCommands(new VMap(ymap));
	}

	protected void sized() {
		// make sure maps map to whole window
		xmap.setIntRange(width);
		ymap.setIntRange(height);
	}

	public Dimension getPreferredSize() { return new Dimension(256,256); }

	/** use one of NONE, XAXIS, YAXIS or BOTH */
	public void setAxes(int f) { axesFlags=f; }
	public void setAxesColor(Color c) { axesColor=c; }

	/** not really for public use */
	public void drawAxes(Graphics g)
	{ 
		if (axesFlags!=0) {
			g.setColor(axesColor);
			
			if ((axesFlags&XAXIS)!=0) {
				int oy = height-ymap.toInt(0.0);
				if (!ymap.wasClipped()) g.drawLine(0,oy,width-1,oy);
			}

			if ((axesFlags&YAXIS)!=0) {
				int ox = xmap.toInt(0.0);
				if (!xmap.wasClipped()) g.drawLine(ox,0,ox,height-1);
			}

			g.setColor(getForeground());
		}
	}

	public void clear(Graphics g) { super.clear(g); drawAxes(g); }
	public void paint(Graphics g) { Tools.initGraphics(g); drawAxes(g); }

	/** return a new Pen for this Plotter */
	public Pen	getPen() { return new Pen(graphics); }
	public Pen	getPen(Graphics g) { return new Pen(g); }

	// ----- drawing tools -------

	/**
		<p>
		This class provides a state based plotting
		environment (slightly inspired by Postscripts
		crazy reverse polish). There is are <i>two</i>
		current points: one for general use and another
		which serves as the other point for lines,
		rectangles and ellipses.

		<p>
		The primary current point can be set in absolute
		coordinates using <code>abs(x,y)</code> or 
		moved relatively using <code>rel(dx,dy)</code>.
		The folling functions use <i>only</i> the primary 
		current point:
		<ul>
			<li> <code>marker()</code>;
			<li> <code>pixel()</code>;
			<li> <code>blob()</code>.
		</ul>
		Calling <code>move()</code> copies the the location
		of the primary current point to the secondary 
		current point. The following functions use both
		the current points:
		<ul>
			<li> <code>line()</code>: line between both points;
			<li> <code>rect()</code>: empty rectangle bounding both points 
			<li> <code>fillRect()</code>;
			<li> <code>fill3DRect(boolean inOrOut)</code>;
			<li> <code>ellipse()</code>;
			<li> <code>fillEllipse()</code>.
		</ul>
		<p>
		Some of the rectangle drawing commands
		only work if the primary current point is below
		and to the right (on the screen, that is) of the
		secondary. The <code>rectify</code> function
		swaps coordinates between the two points to make
		it so. It will screw up any line drawing though.

 */
		
	public class Pen
	{
		Graphics			graphics;
		public Point		p0=new Point(); // secondary
		public Point		p1=new Point(); // primary
		vec2				r=new vec2(0,0);
		int					moff=3, msz=moff*2;	// marker size
		int					ix, iy;							// blob image hotspot
		Color				col=getForeground();		// current colour
		Image			blobImg;						// drawn by blob()

		/**
			<p>
			A program can have several Pens, each with different
			colours, but the underlying Graphics has a
			single current Color. So, if you have multiple Pens,
			each pen must <code>activate()</code> its colour
			before drawing anything.
		 */

		public Pen(Graphics g) { graphics=g; }
		final public Pen  activate() { graphics.setColor(col); return this; }
		final public Pen  setColor(Color c) { col=c; graphics.setColor(c); return this; }
		final public void setMarkerSize(int r) { moff=r; msz=2*r; }

		/** returns true if the last primary current point was clipped */
		public final boolean clipped() { return xmap.wasClipped() | ymap.wasClipped(); }
		public final Pen abs( double x, double y) { r.x=x; r.y=y; map(); return this; }
		public final Pen rel( double x, double y) { r.x+=x; r.y+=y; map(); return this; }

		/** copies position of given pen to primary current point */
		public final Pen set( Pen p) { r.x=p.r.x; r.y=p.r.y; p1.x=p.p1.x; p1.y=p.p1.y; return this; }
		public final Pen move() { p0.x=p1.x; p0.y=p1.y; return this; }
		public final Pen line() {
			graphics.drawLine(p0.x,p0.y,p1.x,p1.y);
			return this;
		}

		public Pen rect() { 
			graphics.drawRect(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y);
			return this;
		}

		public Pen fillRect() { 
			graphics.fillRect(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y);
			return this;
		}

		/** this swaps xs and ys to make sure rectangles work */
		public Pen rectify() {
			if (p1.y<p0.y)	{ int tmp=p0.y; p0.y=p1.y; p1.y=tmp; }
			if (p1.x<p0.x)	{ int tmp=p0.x; p0.x=p1.x; p1.x=tmp;	}
			return this;
		}

		public Pen fill3DRect(boolean outin) {
			if (p1.y!=p0.y) { // goes wrong otherwise
				graphics.fill3DRect(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y,outin);
			}
			return this;
		}

		public Pen ellipse() { 
			graphics.drawOval(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y);
			return this;
		}

		public Pen fillEllipse() {
			graphics.fillOval(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y);
			return this;
		}

		public Pen marker() {
			// graphics.drawOval(p1.x-moff,p1.y-moff,msz,msz);
			graphics.fillRect(p1.x-moff,p1.y-moff,msz+1,msz+1);
			// graphics.drawRect(p1.x-moff,p1.y-moff,msz,msz);
			return this;
		}

		/** pixel drawn as 1x1 filled rectangle */
		public Pen pixel() {
			graphics.fillRect(p1.x,p1.y,1,1);
			return this;
		}

		private Pen map() {
			p1.x=xmap.toInt(r.x);
			p1.y=ymap.toInt(r.y);
//			if (xmap.wasClipped()) {
//				if (p1.x<-16000) p1.x=-16000; else p1.x=16000;
//			}
			if (ymap.wasClipped()) {
				if (p1.y<-32000) p1.y=32000;
				else if (p1.y>32000) p1.y=-32000;
				else p1.y=height-p1.y;
			} else p1.y=height-p1.y;
			return this;
		}

		/* These last three functions are here to speed up 
		   two common operations: 

				moveto(x,y) is exactly equivalent to abs(x,y).map().move();
				lineto(x,y) is exactly equivalent to abs(x,y).map().line().move();
				lineto(x,y) is exactly equivalent to line().move() ;
		 */

		public final Pen lineto() 
		{ 
			graphics.drawLine(p0.x,p0.y,p1.x,p1.y);
			p0.x=p1.x; p0.y=p1.y; 
			return this;
		}

		public final Pen lineto(double x, double y) 
		{ 
			r.x=x; r.y=y; map();
			graphics.drawLine(p0.x,p0.y,p1.x,p1.y);
			p0.x=p1.x; p0.y=p1.y; 
			return this;
		}

		// this is exactly equivalent to abs(x,y).map().move();
		public final Pen moveto(double x, double y) 
		{ 
			r.x=x; r.y=y; map();
			p0.x=p1.x; p0.y=p1.y; 
			return this;
		}

		/** draws the current blob image at the current point */
		public Pen blob() { 
			graphics.drawImage(blobImg,ix+p1.x,iy+p1.y,null); 
			return this; 
		}
		
		/** sets the blob image to be pixel of a given colour */	
		public void setBlobColor(Color col) {
			blobImg=createPixel(col); 
		}

		/** sets blob image to given image */
		public void setBlobImage(Image i) { blobImg=i; ix=0; iy=0; }

		/** sets blob image with given hotspot */
		public void setBlobImage(Image i, int xhot, int yhot) { blobImg=i; ix=-xhot; iy=-yhot; }
	}
}