import java.awt.*;
/**
*
* An abstract canvas object which serves the purpose of insulating the
* mathematical part of the program (Panorama object below) from the
* graphics (DrawPanel object above). The Panorama object does its
* drawing by making calls to the KaliCanvas object, which in turn
* translates them into the appropriate graphics calls for drawing on
* the DrawPanel object.
*
*
*
* (It might be more efficient to have the Panorama object make the
* graphics calls directly, but for now we try it this way.)
*
*
*
* Coordinate systems:
*
*
*
* Kali uses 3 different coordinate systems for the image in the DrawPanel:
*
*
* - Raw Screen Coordinates
*
- This is the original screen coordinate system of the
* drawing area, which goes from (0,0) in the upper left to
* (screenWidth, screenHeight) in the lower right (in
* pixels). Y increases DOWN in this system.
*
* Mouse events happen in this coordinate system.
*
*
*
- Screen Coordinates
*
- This system is the result of translating the raw screen
* system so that (0,0) is at the center of the screen; so it
* goes from (-screenWidth/2, -screenHeight/2) in the upper
* left to (screenWidth/2, screenHeight/2) in the lower
* right. Y increases DOWN in this system too.
*
* Drawing happens in this coordinate system; the
* KaliCanvas.setGraphics() method calls g.translate() to
* move the Graphics object's origin from the upper left (the
* default) to the center of the screen. Also, the
* translation computations in the Panorama object happen in
* this system.
*
*
*
- Internal Coordinates
*
- This system goes from (-internalWidth, internalHeight) in
* the upper left to (internalWidth, -internalHeight) in the
* lower right; so (0,0) is at the center of the screen. Y
* increases UP in this system.
*
* The coordinates of drawn segments are stored in this
* system. Also, the Panorama object does some of the group
* action math (reflection and rotation) in this system.
*
* Also, in the future, scaling and translation may be
* implemented by transforming this system.
*
*
*
*
* @see Panorama
* @see DrawPanel
*/
class KaliCanvas {
/**
* screenWidth and screenHeight record the size, in pixels, of the
* current screen. We initialize these to reasonable values here so
* that we can do the initial updateCoordinateSystems() call without
* risk of division by zero; they get set to actual screen values
* before any actual drawing is done, by the call to setGraphics().
*/
int screenWidth=500, screenHeight=500;
/**
* internalWidth and internalHeight record the dimensions of of
* internal coordinate system (-internalWidth,-internalHeight) to
* (+internalWidth,+internalHeight). In the current version these
* numbers are fixed, but future versions might allow for scaling
* and/or translations by modifying these values.
*/
double internalWidth=-2.5, internalHeight=2.5;
/**
* position and positionInverse are used for converting
* between the internal and screen coordinate systems:
* position * internal = screen;
* positionInverse * screen = internal
*/
DMatrix position, positionInverse;
/**
* screenLeft,screenRight,screenBottom, and screenTop hold the
* bounds of the screen coordinate system
*/
int screenLeft = -250;
int screenRight = 250;
int screenBottom = -250;
int screenTop = 250;
/**
* The graphics context that we use for actual drawing.
*/
Graphics g;
public KaliCanvas() {
super();
updateCoordinateSystems();
}
/**
* Causes the KaliCanvas object to do the internal updating that
* is necessary after a new coordinate system has been set.
*/
private void updateCoordinateSystems() {
double xfactor = 2 * internalWidth / screenWidth;
double yfactor = -2 * internalHeight / screenHeight;
positionInverse = new DMatrix(xfactor, 0,
0, yfactor);
position = positionInverse.inverse();
}
/**
* Convert raw screen coordinates to internal coordinates.
*/
public DVector rawScreenToInternal(int x, int y) {
return positionInverse.times(new DVector(x - screenWidth/2,
y - screenHeight/2));
}
/**
* Convert internal coordinates to screen coordinates.
*/
public DVector internalToScreen(DVector v) {
return position.times(v);
}
/**
* Set the graphics context for future drawing; this results in
* a new screen coordinate system being computed based on the
* current size of this graphics context.
*/
public void setGraphics(Graphics g, int w, int h) {
this.g = g;
Rectangle area = g.getClipRect();
screenWidth = w;
screenHeight = h;
updateCoordinateSystems();
screenLeft = -screenWidth/2;
screenRight = screenWidth/2;
screenBottom = -screenHeight/2;
screenTop = screenHeight/2;
g.translate(screenWidth/2, screenHeight/2);
}
/**
* Draw a single line segment.
*/
public void drawSegment(Segment s, Color c) {
g.setColor(c);
g.drawLine((int)(s.p[0].c[0]+0.5),
(int)(s.p[0].c[1]+0.5),
(int)(s.p[1].c[0]+0.5),
(int)(s.p[1].c[1]+0.5));
}
}