Mercurial > hg > jslab
view src/samer/tools/Plotter.java @ 8:5e3cbbf173aa tip
Reorganise some more
author | samer |
---|---|
date | Fri, 05 Apr 2019 22:41:58 +0100 |
parents | bf79fb79ee13 |
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; } } }