//
// viewport-Objekt für 2D
//
//
import java.awt.*;
import java.lang.Math;
import java.lang.String;

//--------------------------------------------------------------------------

public class viewport2D
{

	private float x_w_min, y_w_min, x_w_max, y_w_max;
	//Weltkoordinaten des windows
	private int x_s_min, y_s_min, x_s_max, y_s_max;
	//Gerätekoordinaten des viewport
	private float s_x, s_y;
	//Skalierungsfaktoren für x und y
	private int s_sx=4;
	private int s_sy=4;
	//Länger der Teilungsstriche auf den Achsen
	private int s_pl=15;
	private int s_pw=5;
	//Abmessungen der Pfeilspitzen (Länge/Breite)
	private Graphics g;

	//--------------------------------------------------------------------------

	private void drawLine(wKoord w_start, sKoord s_delta)
		//zeichnet Linie zwischen Punkt start in Weltkoord.
		//und um delta Pixel verschobenem Punkt
		//Unterfunktion von drawAxes zum Zeichnen der Pfeile und der Achsenteilungen
	{

		sKoord s_start=W2S(w_start);
		sKoord s_end=new sKoord(s_start.x+s_delta.x, s_start.y+s_delta.y);
		if (inViewport(s_start) && inViewport(s_end)) g.drawLine(s_start.x, s_start.y, s_end.x, s_end.y);
	}
	
	//--------------------------------------------------------------------------

	private String fillZero(String tofill, String format)
		//Füllt Nachkommastellen von tofill mit "0", ibs identisch mit Formatforlage format
		//Unterfunktion von drawAxes zum Beschriften der Achsen
	{
		if (!(format.indexOf(".")==-1)) {
			if (tofill.indexOf(".")==-1) tofill+=".0";
				while ((tofill.length()-tofill.indexOf("."))<(format.length()-format.indexOf(".")))
					tofill+="0";
		}
		return tofill;
	}
	
	//--------------------------------------------------------------------------

	private void drawNumber_x(wKoord w_p, float interval_x)
		//Gibt Werte zentriert aus
		// - um Dezimalpunkt oder letzte Ziffer (falls keine Nachkommastellen vorhanden)
		//Unterfunktion von drawAxes zur Beschriftung der x-Achse
		//Verwendete Konstanten beziehen sich auf die Schriftgröße 10
	{
		String label=String.valueOf(w_p.x);
		String interval=String.valueOf(interval_x);
		label=this.fillZero(label, interval);
		
		//Länge des gesamten Strings bzw. der Nachkommastellen in Pixel bestimmen
		int width=g.getFontMetrics().stringWidth(label)-4;
		if (!(label.indexOf(".")==-1))
			width=g.getFontMetrics().stringWidth(label.substring(0,label.indexOf(".")));

		sKoord s_p;
		s_p=W2S(w_p);
		//Wert positioniert ausgeben, wenn kein Nullwert
		if (!(w_p.x==0.0f)) g.drawString(label, s_p.x-width-1, s_p.y+s_sx+15);
	}	

	//--------------------------------------------------------------------------
	
	private void drawNumber_y(wKoord w_p, float interval_y)
		//Gibt Werte rechtsbündig aus
		//Unterfunktion von drawAxes zur Beschriftung der y-Achse
		//Verwendete Konstanten beziehen sich auf die Schriftgröße 10
	{
		String label=String.valueOf(w_p.y);
		String interval=String.valueOf(interval_y);
		label=this.fillZero(label, interval);
		
		//Länge des Strings in Pixel bestimmen
		int width=g.getFontMetrics().stringWidth(label);

		sKoord s_p;
		s_p=W2S(w_p);
		//Wert positioniert ausgeben, wenn kein Nullwert
		if (!(w_p.y==0.0f)) g.drawString(label, s_p.x-width-s_sy-5, s_p.y+5);
	}	

	//--------------------------------------------------------------------------
	
	private void drawNumber_zero(float interval)
		//Gibt einen Nullwert aus
		//Unterfunktion von drawAxes zur Beschriftung des Ursprungs
		//Verwendete Konstanten beziehen sich auf die Schriftgröße 10
	{
		String zero="0";
		zero=this.fillZero(zero, String.valueOf(interval));

		int width=g.getFontMetrics().stringWidth(zero);
		
		sKoord s_zero=W2S(new wKoord(0,0));
		if (inViewport(s_zero)) g.drawString(zero, s_zero.x-width-s_sy-5, s_zero.y+s_sx+15);		
	}

	//--------------------------------------------------------------------------
	
	public boolean inViewport(sKoord target)
		//Prüft, ob ein Zielpunkt target innerhalb des viewport liegt
		//Unterfunktion von drawLine und drawNumber_zero
	{
		if (target.x>=x_s_min && target.x<=x_s_max && target.y>=y_s_min && target.y<=y_s_max)
			return true;
		else
			return false;
	}

	//--------------------------------------------------------------------------
	
	public sKoord W2S(wKoord w) 
		//rechnet World- in Screenkoord. um
	{
		sKoord vkoord=W2V(w);
		return new sKoord(vkoord.x+x_s_min, vkoord.y+y_s_min);
	}

	//--------------------------------------------------------------------------
	
	public sKoord W2V(wKoord w) 
		//rechnet World- in Viewportkoord. um
		// - nicht zum Zeichnen geeignet!
	{
		int x, y;
		x=Math.round(s_x * (w.x - x_w_min));
		y=Math.round(y_s_max - y_s_min - s_y * (w.y - y_w_min));
		return new sKoord(x,y);
	}

	//--------------------------------------------------------------------------
	
	public wKoord S2W(sKoord s)
		//rechnet Screen- in Worldkoord. um
	{
		return(V2W(new sKoord(s.x-x_s_min, s.y-y_s_min)));
	}

	//--------------------------------------------------------------------------
	
	public wKoord V2W(sKoord s)
		//rechnet Viewport- in Worldkoord. um
	{
		wKoord erg;
		float x,y;
		x=x_w_min+s.x/s_x;
		y=y_w_min+(y_s_max-y_s_min-s.y)/s_y;
		return new wKoord(x,y);
	}

	//--------------------------------------------------------------------------
	
	public void drawLine(wKoord w_p1, wKoord w_p2) 
		//zeichnet Linie im Viewport zwischen w_p1 und w_p2
	{
		sKoord s_p1, s_p2;
		s_p1=W2S(w_p1);
		s_p2=W2S(w_p2);

		if (inViewport(s_p1) && inViewport(s_p2)) g.drawLine(s_p1.x, s_p1.y, s_p2.x, s_p2.y);
	}

	//--------------------------------------------------------------------------
	
	public void drawBorder()
		//Zeichnet Rahmen um Viewport
	{
		g.drawLine(x_s_min, y_s_min, x_s_max, y_s_min);
		g.drawLine(x_s_min, y_s_min, x_s_min, y_s_max);
		g.drawLine(x_s_min, y_s_max, x_s_max, y_s_max);
		g.drawLine(x_s_max, y_s_min, x_s_max, y_s_max);
	}

	//--------------------------------------------------------------------------

	public void drawAxes(float interval_x, float interval_y) 
		//zeichnet Achsen von min->max des Windows
		//beschriftet die Achsen, wenn interval_x|y>0 
		//versieht sie mit Teilung der Länge s_s_x|y
		//und Pfeilspitzen mit den Abmessungen  s_pl|w
	{
		float x_w_a, y_w_a;
		
		//x-Achse mit Pfeilspitzen zeichnen
		this.drawLine(new wKoord(x_w_min, 0), new wKoord(x_w_max, 0));
		this.drawLine(new wKoord(0, y_w_max), new sKoord(-s_pw, s_pl));
		this.drawLine(new wKoord(0, y_w_max), new sKoord(s_pw, s_pl));

		//und Beschriften, falls ein gültiges Intervall angegeben ist
		if (!(interval_x==0)) {
			x_w_a=(float)Math.ceil(Math.abs(x_w_min) / Math.abs(interval_x)-1)* (x_w_min/Math.abs(x_w_min)) * Math.abs(interval_x);
			do {
				this.drawLine(new wKoord(x_w_a, 0), new sKoord(0, s_sx));
				this.drawNumber_x(new wKoord(x_w_a, 0), interval_x);
				x_w_a+=Math.abs(interval_x); }
			while (x_w_a<x_w_max);
		}

		//y-Achse mit Pfeilspitzen zeichnen
		this.drawLine(new wKoord(0, y_w_min), new wKoord(0, y_w_max));
		this.drawLine(new wKoord(x_w_max, 0), new sKoord(-s_pl, -s_pw));
		this.drawLine(new wKoord(x_w_max, 0), new sKoord(-s_pl, s_pw));

		//und Beschriften, falls ein gültiges Intervall angegeben ist
		if (!(interval_y==0)) {
			y_w_a=(float)Math.ceil(Math.abs(y_w_min) / Math.abs(interval_y)-1)* (y_w_min/Math.abs(y_w_min)) * Math.abs(interval_y);
			do {
				this.drawLine(new wKoord(0, y_w_a), new sKoord(-s_sy, 0));
				this.drawNumber_y(new wKoord(0, y_w_a), interval_y);
				y_w_a+=interval_y; }
			while (y_w_a<y_w_max);
		}
				
		//Ursprung beschriften
		this.drawNumber_zero(interval_y);
	}

	//--------------------------------------------------------------------------
	
	public viewport2D(Graphics gr, wKoord wMin, wKoord wMax, sKoord sMin, sKoord sMax) 
		//Konstruktor für viewport2D
		//Aufspann des Viewport durch zwei Eckpunkte
	{
		g=gr;
		x_w_min=wMin.x;
		y_w_min=wMin.y;
		x_w_max=wMax.x;
		y_w_max=wMax.y;
		x_s_min=sMin.x;
		y_s_min=sMin.y;
		x_s_max=sMax.x;
		y_s_max=sMax.y;
		s_x=(x_s_max-x_s_min)/(x_w_max-x_w_min);
		s_y=(y_s_max-y_s_min)/(y_w_max-y_w_min);

		//viewport löschen
		g.setColor(Color.white);
		g.fillRect(x_s_min, y_s_min, x_s_max-x_s_min, y_s_max-y_s_min);
		g.setColor(Color.black);
	}

	//--------------------------------------------------------------------------

	public viewport2D(Graphics gr, wKoord wMin, wKoord wMax, sKoord sMin, float sx, float sy) 
		//Konstruktor für viewport2D
		//Erzeugung des Viewport durch einen Eckpunkt und Angabe des Seitenverhältnisses
	{
		g=gr;
		x_w_min=wMin.x;
		y_w_min=wMin.y;
		x_w_max=wMax.x;
		y_w_max=wMax.y;
		x_s_min=sMin.x;
		y_s_min=sMin.y;
		s_x=sx;
		s_y=sy;
		x_s_max=Math.round(s_x*(x_w_max-x_w_min)+x_s_min);
		y_s_max=Math.round(s_y*(y_w_max-y_w_min)+y_s_min);

		//viewport löschen
		g.setColor(Color.white);
		g.fillRect(x_s_min, y_s_min, x_s_max-x_s_min, y_s_max-y_s_min);
		g.setColor(Color.black);
	}

}

//--------------------------------------------------------------------------
