package fplot;
import java.awt.*;
import java.awt.event.*;
/**
*
*
* @author Christian Semrau
*
* Christian.Semrau@student.uni-magdeburg.de
*
Homepage
*/
public class Zoomer implements MouseListener, MouseMotionListener {
protected Canvas canvas;
// zaehlt die Veraenderungen des Darstellungsbereichs
protected int modCount = 0;
// Darstellungsbereich
protected double xmin, xmax, ymin, ymax;
// letzter Darstellungsbereich
protected double oxmin, oxmax, oymin, oymax;
// letzte und erste Position, an der die Maus mit gedrueckter Taste
// bewegt wurde
protected Point lastDrag, firstDrag;
protected final static int NO_STATE = 0;
public final static int MOVE_STATE = 1, RESIZE_STATE = 2;
// aktueller und voriger Drag-Modus
protected int dragState = MOVE_STATE, preState = NO_STATE;
protected boolean dragging = false;
// der Darstellungsbereich kann maximal bis +- grenze reichen,
// die kleinste erlaubte Aufloesung ist 1/grenze
protected final static double grenze = 1e6;
/**
* Konstruiert einen Zoomer.
* Uebergeben wird das Canvas, in dem die Mausereignisse registriert
* werden, und fuer das ein skalierbarer und verschiebbarer
* Koordinatenbereich realisiert wird.
*/
public Zoomer(Canvas c) {
canvas = c;
}
/** Aendert den Mauscursor entsprechend dem aktuellen Zustand */
public void changeMouseCursor() {
if (dragState != preState)
switch(dragState){
case MOVE_STATE: canvas.setCursor(
Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); break;
case RESIZE_STATE: canvas.setCursor(
Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); break;
case NO_STATE: canvas.setCursor(
Cursor.getDefaultCursor()); break;
}
preState = dragState;
}
/**
* Liefert das Canvas.
* @return java.awt.Canvas
*/
public Canvas getCanvas() {
return canvas;
}
/** Liefert den Veraenderungszaehler. */
public int getModCount() {
return modCount++;
}
public double getXmax() { return xmax;}
public double getXmin() { return xmin;}
public double getYmax() { return ymax;}
public double getYmin() { return ymin;}
/**
* Reagiert auf Klicks der rechten Maustaste.
* Schaltet zwischen Verschieben und Skalieren um.
*/
public void mouseClicked(MouseEvent e) {
int mods = e.getModifiers();
if ((mods & e.BUTTON3_MASK) != 0){
if (dragState == MOVE_STATE) dragState = RESIZE_STATE;
else
if (dragState == RESIZE_STATE) dragState = MOVE_STATE;
changeMouseCursor();
}
}
/**
* Reagiert auf Bewegungen der Maus mit gedrueckter Taste.
* Skaliert oder verschiebt den Darstellungsbereich.
* Ruft die Methode mouseWasDragged auf, die anwendungsspezifische
* Reaktionen implementiert.
* @see mouseWasDragged(java.awt.event.MouseEvent e)
*/
public void mouseDragged(MouseEvent e) {
dragging = true;
oxmin = xmin;
oxmax = xmax;
oymin = ymin;
oymax = ymax;
int dx = e.getX()-lastDrag.x;
int dy = e.getY()-lastDrag.y;
if (dragState == RESIZE_STATE){
double factor = 0.02;
zoomX(firstDrag.x, Math.exp(-dx*factor));
zoomY(firstDrag.y, Math.exp(dy*factor));
}else
if (dragState == MOVE_STATE){
double x1 = xmin - scaleI2X()*dx;
double x2 = xmax - scaleI2X()*dx;
double y1 = ymin + scaleI2Y()*dy;
double y2 = ymax + scaleI2Y()*dy;
// Grenzen einhalten
if (x1<-grenze){ x2 -= grenze+x1; x1 = -grenze; }
if (y1<-grenze){ y2 -= grenze+y1; y1 = -grenze; }
if (x2>+grenze){ x1 += grenze-x2; x2 = +grenze; }
if (y2>+grenze){ y1 += grenze-y2; y2 = +grenze; }
xmin = x1; xmax = x2;
ymin = y1; ymax = y2;
modCount++;
}
lastDrag = e.getPoint();
if (oxmin!=xmin || oxmax!=xmax || oymin!=ymin || oymax!=ymax){
mouseWasDragged(e);
}
}
/** Aktiviert den MouseMotionListener, wenn die Maus ins Fenster kommt */
public void mouseEntered(MouseEvent e) {
canvas.addMouseMotionListener(this);
}
/** Deaktiviert den MouseMotionListener, wenn die Maus das Fenster verlaesst */
public void mouseExited(MouseEvent e) {
canvas.removeMouseMotionListener(this);
}
/**
* Reagiert auf Bewegungen der Maus innerhalb des Canvas.
* Muss ueberschrieben werden, um Reaktionen zu erhalten.
*/
public void mouseMoved(MouseEvent e) {
}
/** Setzt die drag-Koordinaten, wenn eine Maustaste gedrueckt wurde */
public void mousePressed(MouseEvent e) {
firstDrag = lastDrag = e.getPoint();
}
/**
* Deaktiviert den drag-Modus.
* Ruft die Methode mouseWasReleased auf, die anwendungsspezifische
* Reaktionen implementiert.
* @see mouseWasReleased(java.awt.event.MouseEvent e)
*/
public void mouseReleased(MouseEvent e) {
dragging = false;
changeMouseCursor();
mouseWasReleased(e);
}
/**
* Wird automatisch aufgerufen, wenn beim Verschieben oder Skalieren
* tatsaechlich eine Veraenderung des Bildbereiches erfolgte.
* Diese Methode muss ueberschrieben werden, um Reaktionen zu erhalten.
*/
public void mouseWasDragged(MouseEvent e) {
}
/**
* Wird automatisch aufgerufen, wenn eine Maustaste innerhalb des
* Canvas losgelassen wird.
* Diese Methode muss ueberschrieben werden, um Reaktionen zu erhalten.
*/
public void mouseWasReleased(MouseEvent e) {
}
/** Liefert den Skalierungsfaktor in x-Richtung,
* der von Pixelkoordinaten in Funktionskoordinaten umrechnet */
public double scaleI2X() {
return (xmax-xmin)/(canvas.getBounds().width-1);
}
/** Liefert die Funktionskoordinate der Pixelkoordinate x */
public double scaleI2X(int x) {
return x*(xmax-xmin)/(canvas.getBounds().width-1)+xmin;
}
/** Liefert den Skalierungsfaktor in y-Richtung,
* der von Pixelkoordinaten in Funktionskoordinaten umrechnet */
public double scaleI2Y() {
return (ymax-ymin)/(canvas.getBounds().height-1);
}
/** Liefert die Funktionskoordinate der Pixelkoordinate y */
public double scaleI2Y(int y) {
return y*(ymax-ymin)/(canvas.getBounds().height-1)+ymin;
}
/** Liefert die Pixelkoordinate der Funktionskoordinate x */
public int scaleX2I(double x) {
return (int)Math.round((x-xmin)/(xmax-xmin)*(canvas.getBounds().width-1));
}
/** Liefert die Pixelkoordinate der Funktionskoordinate y */
public int scaleY2I(double y) {
return (int)Math.round((y-ymin)/(ymax-ymin)*(canvas.getBounds().height-1));
}
/** Setzt den Darstellungsbereich auf die uebergebenen Werte,
* passt den Bereich an die Grenzen an */
public void setCoords(double xmi, double xma, double ymi, double yma) {
if (xmi < -grenze) xmi = -grenze;
if (ymi < -grenze) ymi = -grenze;
if (xma > +grenze) xma = +grenze;
if (yma > +grenze) yma = +grenze;
double grenzinv = 1./grenze;
if (xmi+grenzinv > xma){
double xmid = (xma+xmi)/2;
xmi = xmid-grenzinv/2;
xma = xmid+grenzinv/2;
}
if (ymi+grenzinv > yma){
double ymid = (yma+ymi)/2;
ymi = ymid-grenzinv/2;
yma = ymid+grenzinv/2;
}
oxmin = xmin;
oxmax = xmax;
oymin = ymin;
oymax = ymax;
xmin = xmi; xmax = xma;
ymin = ymi; ymax = yma;
modCount++;
}
/**
* Setzt den drag-State auf den angegebenen Wert.
* @param state int
*/
public void setDragState(int state) {
if (state==RESIZE_STATE || state==MOVE_STATE){
preState = dragState;
dragState = state;
}
}
/** Zoomt den Darstellungsbereich um den Punkt p */
public void zoom(Point p, double factor) {
zoomX(p.x, factor);
zoomY(p.y, factor);
}
/** Zoomt in x-Richtung */
public void zoomX(int px, double factor) {
double x = scaleI2X(px);
double xma = x-(x-xmax)*factor;
double xmi = x-(x-xmin)*factor;
if (xma > +grenze) xma = +grenze;
if (xmi < -grenze) xmi = -grenze;
if (xmi+1./grenze <= xma){
oxmin = xmin;
oxmax = xmax;
xmin = xmi; xmax = xma;
modCount++;
}
}
/** Zoomt in y-Richtung */
public void zoomY(int py, double factor) {
double y = scaleI2Y(canvas.getBounds().height-1-py);
double yma = y-(y-ymax)*factor;
double ymi = y-(y-ymin)*factor;
if (yma > +grenze) yma = +grenze;
if (ymi < -grenze) ymi = -grenze;
if (ymi+1./grenze <= yma){
oymin = ymin;
oymax = ymax;
ymin = ymi; ymax = yma;
modCount++;
}
}
}