samer@0
|
1 /*
|
samer@0
|
2 * Copyright (c) 2000, Samer Abdallah, King's College London.
|
samer@0
|
3 * All rights reserved.
|
samer@0
|
4 *
|
samer@0
|
5 * This software is provided AS iS and WITHOUT ANY WARRANTY;
|
samer@0
|
6 * without even the implied warranty of MERCHANTABILITY or
|
samer@0
|
7 * FITNESS FOR A PARTICULAR PURPOSE.
|
samer@0
|
8 */
|
samer@0
|
9
|
samer@0
|
10 package samer.tools;
|
samer@0
|
11 import samer.core.*;
|
samer@0
|
12 import samer.core.util.*;
|
samer@0
|
13 import java.awt.*;
|
samer@0
|
14
|
samer@0
|
15 /**
|
samer@0
|
16 <p>
|
samer@0
|
17 A Plotter object is a Canvas with its own Graphics
|
samer@0
|
18 (ie a GCanvas) which has two LinearMaps
|
samer@0
|
19 to map a portion of 2d space to the window.
|
samer@0
|
20 It can optionally draw the X and Y axes.
|
samer@0
|
21
|
samer@0
|
22 <p>
|
samer@0
|
23 The inner class Pen provides access to drawing
|
samer@0
|
24 functions in the real-valued coordinate space.
|
samer@0
|
25 */
|
samer@0
|
26
|
samer@0
|
27 public class Plotter extends samer.core.util.heavy.VCanvas
|
samer@0
|
28 {
|
samer@0
|
29 /** for passing to setAxes(int) */
|
samer@0
|
30 public static final int NEITHER = 0;
|
samer@0
|
31 public static final int YAXIS = 2;
|
samer@0
|
32 public static final int XAXIS = 1;
|
samer@0
|
33 public static final int BOTH = 3;
|
samer@0
|
34 public IMap xmap, ymap;
|
samer@0
|
35
|
samer@0
|
36 int axesFlags=BOTH;
|
samer@0
|
37 Color axesColor;
|
samer@0
|
38
|
samer@0
|
39 /**
|
samer@0
|
40 Default constructor makes Plotter with both axes
|
samer@0
|
41 drawn, in grey. LinearMaps take their default
|
samer@0
|
42 setup.
|
samer@0
|
43
|
samer@0
|
44 @see samer.core.util.LinearMap
|
samer@0
|
45 */
|
samer@0
|
46
|
samer@0
|
47 public Plotter()
|
samer@0
|
48 {
|
samer@0
|
49 xmap = new LinearMap();
|
samer@0
|
50 ymap = new LinearMap();
|
samer@0
|
51
|
samer@0
|
52 axesColor=Shell.getColor("axesColor",Color.gray);
|
samer@0
|
53 }
|
samer@0
|
54
|
samer@0
|
55 public void exposeMaps() {
|
samer@0
|
56 exposeCommands(new VMap(xmap));
|
samer@0
|
57 exposeCommands(new VMap(ymap));
|
samer@0
|
58 }
|
samer@0
|
59
|
samer@0
|
60 protected void sized() {
|
samer@0
|
61 // make sure maps map to whole window
|
samer@0
|
62 xmap.setIntRange(width);
|
samer@0
|
63 ymap.setIntRange(height);
|
samer@0
|
64 }
|
samer@0
|
65
|
samer@0
|
66 public Dimension getPreferredSize() { return new Dimension(256,256); }
|
samer@0
|
67
|
samer@0
|
68 /** use one of NONE, XAXIS, YAXIS or BOTH */
|
samer@0
|
69 public void setAxes(int f) { axesFlags=f; }
|
samer@0
|
70 public void setAxesColor(Color c) { axesColor=c; }
|
samer@0
|
71
|
samer@0
|
72 /** not really for public use */
|
samer@0
|
73 public void drawAxes(Graphics g)
|
samer@0
|
74 {
|
samer@0
|
75 if (axesFlags!=0) {
|
samer@0
|
76 g.setColor(axesColor);
|
samer@0
|
77
|
samer@0
|
78 if ((axesFlags&XAXIS)!=0) {
|
samer@0
|
79 int oy = height-ymap.toInt(0.0);
|
samer@0
|
80 if (!ymap.wasClipped()) g.drawLine(0,oy,width-1,oy);
|
samer@0
|
81 }
|
samer@0
|
82
|
samer@0
|
83 if ((axesFlags&YAXIS)!=0) {
|
samer@0
|
84 int ox = xmap.toInt(0.0);
|
samer@0
|
85 if (!xmap.wasClipped()) g.drawLine(ox,0,ox,height-1);
|
samer@0
|
86 }
|
samer@0
|
87
|
samer@0
|
88 g.setColor(getForeground());
|
samer@0
|
89 }
|
samer@0
|
90 }
|
samer@0
|
91
|
samer@0
|
92 public void clear(Graphics g) { super.clear(g); drawAxes(g); }
|
samer@0
|
93 public void paint(Graphics g) { Tools.initGraphics(g); drawAxes(g); }
|
samer@0
|
94
|
samer@0
|
95 /** return a new Pen for this Plotter */
|
samer@0
|
96 public Pen getPen() { return new Pen(graphics); }
|
samer@0
|
97 public Pen getPen(Graphics g) { return new Pen(g); }
|
samer@0
|
98
|
samer@0
|
99 // ----- drawing tools -------
|
samer@0
|
100
|
samer@0
|
101 /**
|
samer@0
|
102 <p>
|
samer@0
|
103 This class provides a state based plotting
|
samer@0
|
104 environment (slightly inspired by Postscripts
|
samer@0
|
105 crazy reverse polish). There is are <i>two</i>
|
samer@0
|
106 current points: one for general use and another
|
samer@0
|
107 which serves as the other point for lines,
|
samer@0
|
108 rectangles and ellipses.
|
samer@0
|
109
|
samer@0
|
110 <p>
|
samer@0
|
111 The primary current point can be set in absolute
|
samer@0
|
112 coordinates using <code>abs(x,y)</code> or
|
samer@0
|
113 moved relatively using <code>rel(dx,dy)</code>.
|
samer@0
|
114 The folling functions use <i>only</i> the primary
|
samer@0
|
115 current point:
|
samer@0
|
116 <ul>
|
samer@0
|
117 <li> <code>marker()</code>;
|
samer@0
|
118 <li> <code>pixel()</code>;
|
samer@0
|
119 <li> <code>blob()</code>.
|
samer@0
|
120 </ul>
|
samer@0
|
121 Calling <code>move()</code> copies the the location
|
samer@0
|
122 of the primary current point to the secondary
|
samer@0
|
123 current point. The following functions use both
|
samer@0
|
124 the current points:
|
samer@0
|
125 <ul>
|
samer@0
|
126 <li> <code>line()</code>: line between both points;
|
samer@0
|
127 <li> <code>rect()</code>: empty rectangle bounding both points
|
samer@0
|
128 <li> <code>fillRect()</code>;
|
samer@0
|
129 <li> <code>fill3DRect(boolean inOrOut)</code>;
|
samer@0
|
130 <li> <code>ellipse()</code>;
|
samer@0
|
131 <li> <code>fillEllipse()</code>.
|
samer@0
|
132 </ul>
|
samer@0
|
133 <p>
|
samer@0
|
134 Some of the rectangle drawing commands
|
samer@0
|
135 only work if the primary current point is below
|
samer@0
|
136 and to the right (on the screen, that is) of the
|
samer@0
|
137 secondary. The <code>rectify</code> function
|
samer@0
|
138 swaps coordinates between the two points to make
|
samer@0
|
139 it so. It will screw up any line drawing though.
|
samer@0
|
140
|
samer@0
|
141 */
|
samer@0
|
142
|
samer@0
|
143 public class Pen
|
samer@0
|
144 {
|
samer@0
|
145 Graphics graphics;
|
samer@0
|
146 public Point p0=new Point(); // secondary
|
samer@0
|
147 public Point p1=new Point(); // primary
|
samer@0
|
148 vec2 r=new vec2(0,0);
|
samer@0
|
149 int moff=3, msz=moff*2; // marker size
|
samer@0
|
150 int ix, iy; // blob image hotspot
|
samer@0
|
151 Color col=getForeground(); // current colour
|
samer@0
|
152 Image blobImg; // drawn by blob()
|
samer@0
|
153
|
samer@0
|
154 /**
|
samer@0
|
155 <p>
|
samer@0
|
156 A program can have several Pens, each with different
|
samer@0
|
157 colours, but the underlying Graphics has a
|
samer@0
|
158 single current Color. So, if you have multiple Pens,
|
samer@0
|
159 each pen must <code>activate()</code> its colour
|
samer@0
|
160 before drawing anything.
|
samer@0
|
161 */
|
samer@0
|
162
|
samer@0
|
163 public Pen(Graphics g) { graphics=g; }
|
samer@0
|
164 final public Pen activate() { graphics.setColor(col); return this; }
|
samer@0
|
165 final public Pen setColor(Color c) { col=c; graphics.setColor(c); return this; }
|
samer@0
|
166 final public void setMarkerSize(int r) { moff=r; msz=2*r; }
|
samer@0
|
167
|
samer@0
|
168 /** returns true if the last primary current point was clipped */
|
samer@0
|
169 public final boolean clipped() { return xmap.wasClipped() | ymap.wasClipped(); }
|
samer@0
|
170 public final Pen abs( double x, double y) { r.x=x; r.y=y; map(); return this; }
|
samer@0
|
171 public final Pen rel( double x, double y) { r.x+=x; r.y+=y; map(); return this; }
|
samer@0
|
172
|
samer@0
|
173 /** copies position of given pen to primary current point */
|
samer@0
|
174 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; }
|
samer@0
|
175 public final Pen move() { p0.x=p1.x; p0.y=p1.y; return this; }
|
samer@0
|
176 public final Pen line() {
|
samer@0
|
177 graphics.drawLine(p0.x,p0.y,p1.x,p1.y);
|
samer@0
|
178 return this;
|
samer@0
|
179 }
|
samer@0
|
180
|
samer@0
|
181 public Pen rect() {
|
samer@0
|
182 graphics.drawRect(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y);
|
samer@0
|
183 return this;
|
samer@0
|
184 }
|
samer@0
|
185
|
samer@0
|
186 public Pen fillRect() {
|
samer@0
|
187 graphics.fillRect(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y);
|
samer@0
|
188 return this;
|
samer@0
|
189 }
|
samer@0
|
190
|
samer@0
|
191 /** this swaps xs and ys to make sure rectangles work */
|
samer@0
|
192 public Pen rectify() {
|
samer@0
|
193 if (p1.y<p0.y) { int tmp=p0.y; p0.y=p1.y; p1.y=tmp; }
|
samer@0
|
194 if (p1.x<p0.x) { int tmp=p0.x; p0.x=p1.x; p1.x=tmp; }
|
samer@0
|
195 return this;
|
samer@0
|
196 }
|
samer@0
|
197
|
samer@0
|
198 public Pen fill3DRect(boolean outin) {
|
samer@0
|
199 if (p1.y!=p0.y) { // goes wrong otherwise
|
samer@0
|
200 graphics.fill3DRect(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y,outin);
|
samer@0
|
201 }
|
samer@0
|
202 return this;
|
samer@0
|
203 }
|
samer@0
|
204
|
samer@0
|
205 public Pen ellipse() {
|
samer@0
|
206 graphics.drawOval(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y);
|
samer@0
|
207 return this;
|
samer@0
|
208 }
|
samer@0
|
209
|
samer@0
|
210 public Pen fillEllipse() {
|
samer@0
|
211 graphics.fillOval(p0.x,p0.y,p1.x-p0.x,p1.y-p0.y);
|
samer@0
|
212 return this;
|
samer@0
|
213 }
|
samer@0
|
214
|
samer@0
|
215 public Pen marker() {
|
samer@0
|
216 // graphics.drawOval(p1.x-moff,p1.y-moff,msz,msz);
|
samer@0
|
217 graphics.fillRect(p1.x-moff,p1.y-moff,msz+1,msz+1);
|
samer@0
|
218 // graphics.drawRect(p1.x-moff,p1.y-moff,msz,msz);
|
samer@0
|
219 return this;
|
samer@0
|
220 }
|
samer@0
|
221
|
samer@0
|
222 /** pixel drawn as 1x1 filled rectangle */
|
samer@0
|
223 public Pen pixel() {
|
samer@0
|
224 graphics.fillRect(p1.x,p1.y,1,1);
|
samer@0
|
225 return this;
|
samer@0
|
226 }
|
samer@0
|
227
|
samer@0
|
228 private Pen map() {
|
samer@0
|
229 p1.x=xmap.toInt(r.x);
|
samer@0
|
230 p1.y=ymap.toInt(r.y);
|
samer@0
|
231 // if (xmap.wasClipped()) {
|
samer@0
|
232 // if (p1.x<-16000) p1.x=-16000; else p1.x=16000;
|
samer@0
|
233 // }
|
samer@0
|
234 if (ymap.wasClipped()) {
|
samer@0
|
235 if (p1.y<-32000) p1.y=32000;
|
samer@0
|
236 else if (p1.y>32000) p1.y=-32000;
|
samer@0
|
237 else p1.y=height-p1.y;
|
samer@0
|
238 } else p1.y=height-p1.y;
|
samer@0
|
239 return this;
|
samer@0
|
240 }
|
samer@0
|
241
|
samer@0
|
242 /* These last three functions are here to speed up
|
samer@0
|
243 two common operations:
|
samer@0
|
244
|
samer@0
|
245 moveto(x,y) is exactly equivalent to abs(x,y).map().move();
|
samer@0
|
246 lineto(x,y) is exactly equivalent to abs(x,y).map().line().move();
|
samer@0
|
247 lineto(x,y) is exactly equivalent to line().move() ;
|
samer@0
|
248 */
|
samer@0
|
249
|
samer@0
|
250 public final Pen lineto()
|
samer@0
|
251 {
|
samer@0
|
252 graphics.drawLine(p0.x,p0.y,p1.x,p1.y);
|
samer@0
|
253 p0.x=p1.x; p0.y=p1.y;
|
samer@0
|
254 return this;
|
samer@0
|
255 }
|
samer@0
|
256
|
samer@0
|
257 public final Pen lineto(double x, double y)
|
samer@0
|
258 {
|
samer@0
|
259 r.x=x; r.y=y; map();
|
samer@0
|
260 graphics.drawLine(p0.x,p0.y,p1.x,p1.y);
|
samer@0
|
261 p0.x=p1.x; p0.y=p1.y;
|
samer@0
|
262 return this;
|
samer@0
|
263 }
|
samer@0
|
264
|
samer@0
|
265 // this is exactly equivalent to abs(x,y).map().move();
|
samer@0
|
266 public final Pen moveto(double x, double y)
|
samer@0
|
267 {
|
samer@0
|
268 r.x=x; r.y=y; map();
|
samer@0
|
269 p0.x=p1.x; p0.y=p1.y;
|
samer@0
|
270 return this;
|
samer@0
|
271 }
|
samer@0
|
272
|
samer@0
|
273 /** draws the current blob image at the current point */
|
samer@0
|
274 public Pen blob() {
|
samer@0
|
275 graphics.drawImage(blobImg,ix+p1.x,iy+p1.y,null);
|
samer@0
|
276 return this;
|
samer@0
|
277 }
|
samer@0
|
278
|
samer@0
|
279 /** sets the blob image to be pixel of a given colour */
|
samer@0
|
280 public void setBlobColor(Color col) {
|
samer@0
|
281 blobImg=createPixel(col);
|
samer@0
|
282 }
|
samer@0
|
283
|
samer@0
|
284 /** sets blob image to given image */
|
samer@0
|
285 public void setBlobImage(Image i) { blobImg=i; ix=0; iy=0; }
|
samer@0
|
286
|
samer@0
|
287 /** sets blob image with given hotspot */
|
samer@0
|
288 public void setBlobImage(Image i, int xhot, int yhot) { blobImg=i; ix=-xhot; iy=-yhot; }
|
samer@0
|
289 }
|
samer@0
|
290 }
|
samer@0
|
291
|