Files

331 lines
8.6 KiB
Java

import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
public class Camera
{
// whenever graphics context is changed by Basic, all
// cameras get shared access to that context
// *******************************************************************
private static Graphics gc; // the graphics context
public static void setGraphicsContext( Graphics g )
{
gc = g;
}
// instance variables
// *******************************************************************
private Color backgroundColor;
private int px, py, pw, ph; // the pixel grid rectangle where draw
private double left, right, bottom, top; // the visible rectangle in the
// virtual graph paper
private Font currentFont; // the current font
private int currentFontHeight; // height of a typical char in current font
// methods for creating and changing camera properties
// *******************************************************************
// construct a camera with both rectangles (pixelgrid and graph paper)
// specified, and background color
// Note: the top coordinate of the visible region is computed so that
// the visible region has the same aspect ratio as the pixel
// grid region (can change if desired by using setRegion)
public Camera( int vx, int vy, int vw, int vh,
double l, double r, double b,
Color bc )
{
px=vx; py=vy; pw=vw; ph=vh;
left=l; right=r; bottom=b; top=bottom + ph*(right-left)/pw;
backgroundColor=bc;
}
// construct a camera with both rectangles specified, and back color
// Note: this constructor lets you select coordinates each way,
// dangerous to draw geometrical entities
public Camera( int vx, int vy, int vw, int vh,
double l, double r, double b, double t,
Color bc )
{
px=vx; py=vy; pw=vw; ph=vh;
left=l; right=r; bottom=b; top=t;
backgroundColor=bc;
}
// set the background color
// (might want to change it after camera is first built)
public void setBackgroundColor( Color color )
{
backgroundColor = color;
}
// move the graph paper viewing rectangle
public void setRegion( double l, double r, double b, double t )
{
left=l; right=r; bottom=b; top=t;
}
// shift the graph paper rectangle by given fractions in x and y
public void shiftRegion( double fx, double fy )
{
double dx = fx*(right-left);
double dy = fy*(top-bottom);
left += dx; right += dx;
bottom += dy; top += dy;
}
// scale the graph paper rectangle by given fractions in x and y,
// keeping the center fixed
public void scaleRegion( double sx, double sy )
{
double cx = (left+right)/2, cy = (bottom+top)/2;
double dx = sx*(right-left);
double dy = sy*(top-bottom);
left = cx - dx/2; right = cx + dx/2;
bottom = cy - dy/2; top = cy + dy/2;
}
public double getRegionLeft()
{
return left;
}
public double getRegionBottom()
{
return bottom;
}
public double getRegionWidth()
{
return right-left;
}
public double getRegionHeight()
{
return top-bottom;
}
// methods for drawing things used by application
// *******************************************************************
// prepare pixel grid for this camera to draw in its region
public void activate()
{
// set up clipping so no drawing can occur outside this camera's
// pixel grid region
gc.setClip(null);
gc.clipRect( px, py, pw, ph );
// clear the background of entire window
gc.setColor( backgroundColor );
gc.fillRect( px, py, pw, ph );
}
// set the drawing color
public void setColor( Color color )
{
gc.setColor( color );
}
// draw a rectangle---just the border---with lower left corner (x,y),
// width w, height h, in graph paper coords
public void drawRect( double x, double y, double w, double h )
{
rect( false, x, y, w, h );
}
// draw a filled rectangle with lower left corner (x,y),
// width w, height h, in graph paper coords
public void fillRect( double x, double y, double w, double h )
{
rect( true, x, y, w, h );
}
public void drawCircle( double x, double y, double r )
{
circle( false, x, y, r );
}
public void fillCircle( double x, double y, double r )
{
circle( true, x, y, r );
}
public void drawTri( double x1, double y1,
double x2, double y2,
double x3, double y3 )
{
tri( false, x1, y1, x2, y2, x3, y3 );
}
public void fillTri( double x1, double y1,
double x2, double y2,
double x3, double y3 )
{
tri( true, x1, y1, x2, y2, x3, y3 );
}
public void drawLine( double x1, double y1,
double x2, double y2 )
{
gc.drawLine( mapX(x1), mapY(y1), mapX(x2), mapY(y2) );
}
public void drawText( String text, double x, double y )
{
gc.drawString( text, mapX(x), mapY(y) );
}
public void setFont( Font f )
{
currentFont = f;
Rectangle r = figureBounds( "e", currentFont );
currentFontHeight = r.height;
gc.setFont( f );
}
// compute the width of the text in the current font
// in the visible region
public double getWidth( String text )
{
FontMetrics fm = gc.getFontMetrics();
double w = invMapX( fm.stringWidth( text ) );
return w;
}
// compute the height of the current font
// in the visible region
public double getHeight()
{
FontMetrics fm = gc.getFontMetrics();
double h = invMapY( fm.getHeight() );
return h;
}
// draw the text centered about (x,y)
// using height of typical char (as in setFont) for vertical centering
public void drawCenteredText( String text, double x, double y )
{
FontMetrics fm = gc.getFontMetrics();
int w = fm.stringWidth( text );
gc.drawString( text, mapX(x)-w/2, mapY(y) + currentFontHeight/2 );
}
// figure bounds of the given string
private Rectangle figureBounds( String s, Font font )
{
Graphics2D g2 = (Graphics2D) gc;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setFont( font );
FontRenderContext frc = g2.getFontRenderContext();
GlyphVector gv = g2.getFont().createGlyphVector(frc, s);
return gv.getPixelBounds(null, 0, 0);
}
// draw the text horizontally centered about x,
// with baseline at y
public void drawHorizCenteredText( String text, double x, double y )
{
FontMetrics fm = gc.getFontMetrics();
int w = fm.stringWidth( text );
gc.drawString( text, mapX(x)-w/2, mapY(y) );
}
// draw the text vertically centered about y,
// with left edge at x
public void drawVertCenteredText( String text, double x, double y )
{
gc.drawString( text, mapX(x), mapY(y) + currentFontHeight/2 );
}
// use these carefully!
// -----------------------------------------------------------------
public int mapX( double x )
{
return px + nicefy( ((x-left)/(right-left))*pw );
}
public int mapY( double y )
{
return py + ph - nicefy( ((y-bottom)/(top-bottom))*ph );
}
// does the mouse location in pixel grid (ix,iy) hit this
// camera's pixel grid region
public boolean hits( int ix, int iy )
{
boolean result = px<=ix && ix<=px+pw && py<=iy && iy<=py+ph;
return result;
}
public double invMapX( int ix )
{
return left + (right-left)*(ix-px)/pw;
}
public double invMapY( int iy )
{
return bottom + (top-bottom)*(ph-(iy-py))/ph;
}
// -----------------------------------------------------------------
// methods used internally
private int nicefy( double a )
{
return (int) Math.round(a);
}
// draw or fill a rectangle
private void rect( boolean fill, double x, double y, double w, double h )
{
int x1, x2, y1, y2;
x1 = mapX(x); x2 = mapX(x+w);
y1 = mapY(y); y2 = mapY(y+h);
if( fill )
gc.fillRect( x1, y2, x2-x1, y1-y2 );
else
gc.drawRect( x1, y2, x2-x1, y1-y2 );
}
private void circle( boolean fill, double x, double y, double r )
{
int x1=mapX(x-r), y1=mapY(y+r), x2=mapX(x+r);
if( fill )
gc.fillOval( x1, y1, x2-x1, x2-x1 );
else
gc.drawOval( x1, y1, x2-x1, x2-x1 );
}
private void tri( boolean fill, double x1, double y1,
double x2, double y2,
double x3, double y3 )
{
int[] x = new int[3]; x[0]=mapX(x1); x[1]=mapX(x2); x[2]=mapX(x3);
int[] y = new int[3]; y[0]=mapY(y1); y[1]=mapY(y2); y[2]=mapY(y3);
if( fill )
gc.fillPolygon( x, y, 3 );
else
gc.drawPolygon( x, y, 3 );
}
}