package psplot;

import java.awt.*;

import real.*;

/* a `real' path is an array of pieces --- 
   moves, lines, curves --- not yet rendered to a physical object 
   a kind of abstract mathematical object */

abstract class PathComponent {
	abstract public void translate(double tx, double ty);
	abstract public void scale(double sx, double sy);
	abstract public void rotate(double theta);
	abstract public void transform(double m[][], double t[]);
	abstract public String toPostScript();
}

class MoveTo extends PathComponent {
	RealPoint P;
	
	public MoveTo(double x, double y) {
		P = new RealPoint(x, y);
	}
	
	public MoveTo(RealPoint Q) {
		P = new RealPoint(Q.x, Q.y);
	}

	public void translate(double tx, double ty) {
		P.x += tx;
		P.y += ty;
	}
	
	public void scale(double sx, double sy) {
		P.x *= sx;
		P.y *= sy;
	}
	
	public void rotate(double theta) {
		double c = Math.cos(theta);
		double s = Math.sin(theta);
		double x = c*P.x - s*P.y;
		P.y = s*P.x + c*P.y;
		P.x = x;
	}
	
	public String toPostScript() {
		return(Real.toString(P.x, 6, 8) + " " 
			+ Real.toString(P.y, 6, 8) + " moveto\n");
	}
	
	public void transform(double m[][], double t[]) {
		double x0 = m[0][0]*P.x + m[0][1]*P.y + t[0];
		double y0 = m[1][0]*P.x + m[1][1]*P.y + t[1];
		P.x = x0;
		P.y = y0;
	}
}

class CurveTo extends PathComponent {
	RealPoint P1;
	RealPoint P2;
	RealPoint P3;

	public CurveTo(RealPoint Q1, RealPoint Q2, RealPoint Q3) {
		P1 = Q1;
		P2 = Q2;
		P3 = Q3;
	}
	
	public CurveTo(double x1, double y1,
		double x2, double y2,
		double x3, double y3) {
		
		P1 = new RealPoint(x1, y1);
		P2 = new RealPoint(x2, y2);
		P3 = new RealPoint(x3, y3);
	}
	public void translate(double tx, double ty) {
		P1.x += tx;
		P1.y += ty;
		P2.x += tx;
		P2.y += ty;
		P3.x += tx;
		P3.y += ty;
	}
	public void scale(double sx, double sy){
		P1.x *= sx;
		P1.y *= sy;
		P2.x *= sx;
		P2.y *= sy;
		P3.x *= sx;
		P3.y *= sy;
	}
	 
	public void rotate(double theta) {
		double c = Math.cos(theta);
		double s = Math.sin(theta);
		double x;
		x = c*P1.x - s*P1.y;
		P1.y = s*P1.x + c*P1.y;
		P1.x = x;
		x = c*P2.x - s*P2.y;
		P2.y = s*P2.x + c*P2.y;
		P2.x = x;
		x = c*P3.x - s*P3.y;
		P3.y = s*P3.x + c*P3.y;
		P3.x = x;
	}

	public void transform(double m[][], double t[]) {
		double x0;
		double y0;
		x0 = m[0][0]*P1.x + m[0][1]*P1.y + t[0];
		y0 = m[1][0]*P1.x + m[1][1]*P1.y + t[1];
		P1.x = x0;
		P1.y = y0;
		x0 = m[0][0]*P2.x + m[0][1]*P2.y + t[0];
		y0 = m[1][0]*P2.x + m[1][1]*P2.y + t[1];
		P2.x = x0;
		P2.y = y0;
		x0 = m[0][0]*P3.x + m[0][1]*P3.y + t[0];
		y0 = m[1][0]*P3.x + m[1][1]*P3.y + t[1];
		P3.x = x0;
		P3.y = y0;
	}
	
	public String toPostScript() {
		return(Real.toString(P1.x, 6, 8) + " " 
			+ Real.toString(P1.y, 6, 8) + " "
			+ Real.toString(P2.x, 6, 8) 
			+ Real.toString(P2.y, 6, 8) + " "
			+ Real.toString(P3.x, 6, 8) 
			+ Real.toString(P3.y, 6, 8) + " "
			+ "curveto\n");
	}
	 
}

class LineTo extends PathComponent {
	RealPoint P;
	
	public LineTo(double x, double y) {
		P = new RealPoint(x, y);
	}

	public LineTo(RealPoint Q) {
		P = Q;
	}
	
	public void translate(double tx, double ty) {
		P.x += tx;
		P.y += ty;
	}

	public void scale(double sx, double sy) {
		P.x *= sx;
		P.y *= sy;
	}

	public void rotate(double theta) {
		double c = Math.cos(theta);
		double s = Math.sin(theta);
		double x = c*P.x - s*P.y;
		P.y = s*P.x + c*P.y;
		P.x = x;
	}

	public void transform(double m[][], double t[]) {
		double x0 = m[0][0]*P.x + m[0][1]*P.y + t[0];
		double y0 = m[1][0]*P.x + m[1][1]*P.y + t[1];
		P.x = x0;
		P.y = y0;
	}
	public String toPostScript() {
		return(Real.toString(P.x, 6, 8) + " " 
			+ Real.toString(P.y, 6, 8) + " moveto\n");
	}
}

class Close extends PathComponent {
	public void Close() {
	}
	
	public void translate(double tx, double ty) {
	}

	public void scale(double sx, double sy) {
	}
	
	public void rotate(double theta) {
	}
	
	public void transform(double m[][], double t[]) {
	}
	
	public String toPostScript() {
		return("closepath\n");
	}
}

class RealPath {
	final static int INC = 128;

	PathComponent segment[];
	PathComponent currentPathComponent = null;
	int maxPathComponentNo = INC;
	int segmentNo;
	
	// mark last move:
	int lastmove = -1;
	RealPoint CurrentPoint;
	int N = 0;

	
	public RealPath() {
		segment = new PathComponent[INC];
		segmentNo = 0;
	}

	RealPoint currentPoint() {
		return(CurrentPoint);
	}

	public PlotPath toPlotPath(PlotCanvas canvas) {
		PlotPath p = new PlotPath(canvas);
		for (int i=0;i<segmentNo;i++) {
			PathComponent c = segment[i];
			if (c instanceof MoveTo) {
				MoveTo m = (MoveTo) c;
				p.moveto(m.P);
			}
			else if (c instanceof LineTo) {
				LineTo m = (LineTo) c;
				p.lineto(m.P);
			}
			else if (c instanceof MoveTo) {
				CurveTo m = (CurveTo) c;
				p.curveto(m.P1, m.P2, m.P3);
			}
			else {
				Close m = (Close) c;
				p.close();
			}
		}
		return(p);
	}

	public void translate(double x, double y) {
		for (int i=0;i<segmentNo;i++) {
			PathComponent c = segment[i];
			c.translate(x, y);
		}
	}
	
	public void scale(double sx, double sy) {
		for (int i=0;i<segmentNo;i++) {
			PathComponent c = segment[i];
			c.scale(sx, sy);
		}
	}

	public void rotate(double t) {
		for (int i=0;i<segmentNo;i++) {
			PathComponent c = segment[i];
			c.rotate(t);
		}
	}
	
	// - moves --------------------------------------------

	public void moveto(double x, double y) {
		if (segmentNo >= maxPathComponentNo) {
			// System.out.println("overflow!");
			maxPathComponentNo += INC;
			PathComponent newseg[] = new PathComponent[maxPathComponentNo];
			for (int i=0;i<segmentNo;i++) {
				newseg[i] = segment[i];
			}
			segment = newseg;
		}
		segment[segmentNo] = new MoveTo(x, y);
		lastmove = segmentNo;
		currentPathComponent = segment[segmentNo++];
		CurrentPoint = new RealPoint(x, y);
	}

	public void moveto(RealPoint p) {
		moveto(p.x, p.y);
	}
	
	public void rmoveto(RealPoint p) {
		double cx = CurrentPoint.x + p.x;
		double cy = CurrentPoint.y + p.y;
		moveto(cx, cy);
	}

	public void rmoveto(double x, double y) {
		double cx = CurrentPoint.x + x;
		double cy = CurrentPoint.y + y;
		moveto(cx, cy);
	}

	// - lines --------------------------------------------

	public void lineto(double x, double y) {
		if (segmentNo >= maxPathComponentNo) {
			// System.out.println("overflow!");
			maxPathComponentNo += INC;
			PathComponent newseg[] = new PathComponent[maxPathComponentNo];
			for (int i=0;i<segmentNo;i++) {
				newseg[i] = segment[i];
			}
			segment = newseg;
		}
		segment[segmentNo] = new LineTo(x, y);
		lastmove = segmentNo;
		currentPathComponent = segment[segmentNo++];
		CurrentPoint = new RealPoint(x, y);
	}
	
	public void lineto(RealPoint P) {
		lineto(P.x, P.y);
	}

	public void rlineto(RealPoint p) {
		CurrentPoint.x += p.x;
		CurrentPoint.y += p.y;
		lineto(CurrentPoint.x, CurrentPoint.y);
	}

	public void rlineto(double x, double y) {
		CurrentPoint.x += x;
		CurrentPoint.y += y;
		lineto(CurrentPoint.x, CurrentPoint.y);
	}

	// - curves -------------------------------------------
	
	public void curveto(RealPoint P1, RealPoint P2, RealPoint P3) {
		if (segmentNo >= maxPathComponentNo) {
			// System.out.println("overflow!");
			maxPathComponentNo += INC;
			PathComponent newseg[] = new PathComponent[maxPathComponentNo];
			for (int i=0;i<segmentNo;i++) {
				newseg[i] = segment[i];
			}
			segment = newseg;
		}
		segment[segmentNo] = new CurveTo(P1, P2, P3);
		lastmove = segmentNo;
		currentPathComponent = segment[segmentNo++];
		CurrentPoint = new RealPoint(P3.x, P3.y);
	}

	// ----------------------------------------------------
	
	public void close() {
		if (segmentNo >= maxPathComponentNo) {
			// System.out.println("overflow!");
			maxPathComponentNo += INC;
			PathComponent newseg[] = new PathComponent[maxPathComponentNo];
			for (int i=0;i<segmentNo;i++) {
				newseg[i] = segment[i];
			}
			segment = newseg;
		}
		segment[segmentNo] = new Close();
		currentPathComponent = segment[segmentNo++];
		MoveTo m = (MoveTo) segment[lastmove];
		CurrentPoint = m.P;
	}

	public String toPostScript() {
		StringBuffer s = new StringBuffer();
		for (int i=0;i<segmentNo;i++) {
			s.append(segment[i].toPostScript());
		}
		return(new String(s));
	}
}

