module Plot
export
	Plot2D,				% Plot two functions of one parameter
	Plot3D,				% Plot three functions of two parameters
	SetRotation,			% Set rotation about x,y,z axes
	SetScale,			% Set the scale at which drawing is
					% 	displayed
	SetOffset,			% Set origin of display
	SetColour,			% Set the colour of lines to be
					%	displayed
	RealPoint3D,			% Data type (x,y,z) in R3
	RealPoint2D,			% Data type (x,y,z) in R2
	IntPoint2D			% Data type (x,y,z) in Z2
	
%
%	To use a type like RealPoint3D outside of this module, then what
%	you do is make a declaration like this:
%	
%	var foo: Plot.RealPoint3D
%

type RealPoint3D:
	record
		x,y,z: real
	end record

type RealPoint2D:
	record
		x,y: real
	end record

type IntPoint2D:
	record
		x,y: int
	end record

%
%	Some display parameters and access methods for them
%

var Rotation:	RealPoint3D := init(0.0,0.0,0.0)
var Scale:	RealPoint2D := init(100.0,100.0)
var Offset:	IntPoint2D  := init(300,100)
var Colour:	int	    := 1

procedure SetRotation(x,y,z: real)
	Rotation.x := x
	Rotation.y := y
	Rotation.z := z
end SetRotation

procedure SetScale(x,y: real)
	Scale.x := x
	Scale.y := y
end SetScale

procedure SetOffset(x,y: int);
	Offset.x := x
	Offset.y := y
end SetOffset

procedure SetColour(c: int)
	Colour := c
end SetColour


%
%	These routines rotate points about the x,y,z axes.
%	The function ApplyRotations below rotates a point about all three
%	axes
%

function RotatePtZ(pt:RealPoint3D, ang: real): RealPoint3D
	var s,c: real
	var p: RealPoint3D

	s := sin(ang)
	c := cos(ang)

	p.z := pt.z;
	p.x := c*pt.x - s*pt.y
	p.y := s*pt.x + c*pt.y
	result(p)
end RotatePtZ

function RotatePtY(pt:RealPoint3D, ang: real): RealPoint3D
	var s,c: real
	var p: RealPoint3D

	s := sin(ang)
	c := cos(ang)

	p.y := pt.y;
	p.x := c*pt.x + s*pt.z
	p.z := -s*pt.x + c*pt.z
	result(p)
end RotatePtY

function RotatePtX(pt:RealPoint3D, ang: real): RealPoint3D
	var s,c: real
	var p: RealPoint3D

	s := sin(ang)
	c := cos(ang)

	p.x := pt.x;
	p.y := c*pt.y - s*pt.z
	p.z := s*pt.y + c*pt.z
	result(p)
end RotatePtX


function ApplyRotations(pt:RealPoint3D): RealPoint3D
	result(RotatePtZ(RotatePtY(RotatePtX(pt,Rotation.x),Rotation.y),Rotation.z))
end ApplyRotations



%
%	This routine takes a 2D real co-ordinate and converts it to an integer
%	pixel address.  Note that the user-set Scale factor and Offset are
%	applied here.
%

function ConvertToScreen(p: RealPoint2D): IntPoint2D
	var i: IntPoint2D

	i.x := round(Scale.x*p.x) + Offset.x
	i.y := round(Scale.y*p.y) + Offset.y
	result(i)
end ConvertToScreen



%
%	R3DtoR2D technically simply takes the x and y component from a 3D point
%	and returns a 2D point.  What it really does is perform an orthographic
%	projection of a point P(x,y,z) in space onto a point (x,y) on the screen.
%
%
%	The combination of:
%		ConvertToScreen(R3DtoR2D(P))
%
%	returns a pixel value on the screen (that can be used to draw lines with).
%
%	You will need ConvertToScreen for both 2D and 3D plotting.
%	You will need R3DtoR2D only for 3D plotting.
%

function R3DtoR2D(p: RealPoint3D): RealPoint2D
	var q: RealPoint2D
	q.x := p.x
	q.y := p.y
	result(q)
end R3DtoR2D


%
%	Draw a line segment with real-valued endpoints.
%	It handles conversion to screen co-ordinates, but it will require
%	the user to specify the correct scale and offset.
%
procedure Line(p0,p1: RealPoint2D)
	var i0,i1: IntPoint2D

	i0 := ConvertToScreen(p0)
	i1 := ConvertToScreen(p1)
	% The next statement can be uncommented for debugging.
	% put "(", i0.x, ",", i0.y,"), (", i1.x, ",", i1.y, "): ", Colour
	drawline(i0.x, i0.y, i1.x, i1.y, Colour);
end Line

%
%	Here is the stub for the 2D plotter you'll be writing.
%	I'm not very concerned about efficiency here, because the code
%	is essentially from your notes.
%
%	Do a little bit of error checking to make sure that the range and the
%	number of steps nt are reasonable.
%
proc Plot2D(
	function x(t:real): real,			% our parametric functions
	function y(t:real): real,
	t0, t1: 	    real,			% plotting range
	nt: 		    int)			% number of steps

end Plot2D

%
%	The stub for the 3D plotter.
%	I am concerned about efficiency here.  Start with a simple brute force
%	method that computes the same points several times and then refine
%	your solution so that you compute a point only once and so that you
%	only draw a given line segment once.
%

proc Plot3D(
	function x(s,t:real): real,
	function y(s,t:real): real,
	function z(s,t:real): real,
	t0, t1: real,
	nt: int,
	s0, s1: real,
	ns: int)
end Plot3D

end Plot
