(*:Copyright: Copyright 1989-1999, Wolfram Research, Inc. *)

(*:Mathematica Version: 3.0 *)

(*:Title: Control Functions for Animation *)

(*:Author: R. Maeder, after a version by Theodore Gray and David Ballman *)

(*:Keywords: Animation, Movie, Animate *)

(*:Requirements: Machine-dependent animation capabilities.  If such
are available, the machine-specific definitions for rendering
animations will be set up in the graphics initialization files, such
as X11.m. The Notebook frontend also sets up suitable values for animation.
*)

(*:Summary:
This package contains various animation functions for
regular x-y, density, contour, and parametric curve plots.  Animation
of three-dimensional plots and rotation of two-dimensional plots
is also supported.
*)


BeginPackage["Graphics`Animation`",
             {"Utilities`FilterOptions`", "Geometry`Rotations`"}]

Animation::usage = "Graphics`Animation` contains various animation functions.
	Options used are: RasterFunction, AnimationFunction, Frames, and Closed.
	They pass any extra options to the embedded Show command to render
	the frames."

ShowAnimation::usage = "ShowAnimation[{g,h,...}, options...] produces
	an animation from a sequence of graphics objects."

System`DisplayAnimation::usage = "DisplayAnimation[file, glist] writes the
	PostScript code of all frames in the list of graphics glist to file.
	File can be animated under the Notebook front end or in most
	PostScript renderers supplied with Mathematica."

Animate::usage = "Animate[command, iterator, options...] uses the
	iterator to run the specified graphics command,
	and animates the results."

MoviePlot::usage = "MoviePlot[f[x,t], {x,x0,x1}, {t,t0,t1}, options...] will
	animate plots of f[x,t] regarded as a function of x, with t serving as
	the animation (or time) variable."

MoviePlot3D::usage = "MoviePlot3D[f[x,y,t], {x,x0,x1}, {y,y0,y1}, {t,t0,t1},
	options...] will animate x,y-plots of the given function by varying t."

MovieDensityPlot::usage = "MovieDensityPlot[f[x,y,t], {x,x0,x1}, {y,y0,y1},
	{t,t0,t1}, options...] will animate x,y-density-plots of the given
	function by varying t."

MovieContourPlot::usage = "MovieContourPlot[f[x,y,t], {x,x0,x1}, {y,y0,y1},
	{t,t0,t1}, options...] will animate x,y-contour-plots of the given
	function by varying t."

MovieParametricPlot::usage = "MovieParametricPlot[{f[x,t],g{x,t}}, {x,x0,x1},
	{t,t0,t1}, options...] will animate parametric curve plots of the given
	function by varying t."

SpinShow::usage = "SpinShow[graphics, opts...]
	will animate a three-dimensional graphics object by rotating it."

SpinOrigin::usage = "SpinOrigin is an option of SpinShow which is used
	with SpinDistance to determine the ViewPoint of each frame."
	
SpinDistance::usage = "SpinDistance is an option of SpinShow which is used
	with SpinOrigin to determine the ViewPoint of each frame."

SpinTilt::usage = "SpinTilt is an option of SpinShow which specifies
	Euler angles to give a tilt to the rotation."

SpinRange::usage = "SpinRange is an option of SpinShow which specifies
	the range over which the first Euler angle is varied."

RotateLights::usage = "RotateLights is an option of SpinShow which specifies
	whether the light sources should rotate with the object."

RasterFunction::usage = "RasterFunction is an option of ShowAnimation
that specifies the function to use to rasterize the individual
frames in an animation.  It is also used by Animate, MoviePlot,
SpinShow, etc."

AnimationFunction::usage = "AnimationFunction is an option of ShowAnimation
	that specifies the function to use to show the animation.
	It is also used by Animate, MoviePlot, SpinShow, etc."

Frames::usage = "Frames is an option of Animate and SpinShow that specifies
	number of frames to render, if the animation iterator does not
	specify an increment."

Closed::usage = "Closed -> False/True is an option of Animate
and SpinShow that specifies whether the last value of the animation
iterator is assumed to give the same picture as the first one. If
True, the last frame is not rendered. For example, {t, 0, 2Pi},
Closed->True, Frames -> 24 will generate frames for t = 0, 2Pi/24,...,
2Pi - 2Pi/24."

Unprotect[ShowAnimation, Animate, DisplayAnimation, MoviePlot, MoviePlot3D,
	MovieDensityPlot, MovieContourPlot, MovieParametricPlot, SpinShow,
	Frames, Closed, RasterFunction, AnimationFunction, SpinOrigin,
	SpinTilt, SpinDistance, SpinRange, RotateLights]

Options[ ShowAnimation ] = {
	RasterFunction :> System`$RasterFunction,
	AnimationFunction :> System`$AnimationFunction
	}

Options[ Animate ] = {
	Frames -> 24,
	Closed -> False
	}

Options[SpinShow] = {
	Frames -> 24,
	Closed -> True,
	SpinOrigin -> {0,0,1.5},
	SpinTilt -> {0,0},
	SpinDistance -> 2,
	SpinRange -> {0 Degree, 360 Degree},
	RotateLights -> False
	}


Begin["`Private`"]

(* Here is how it works:
 *
 * For each frame, Show[ -graphics-, DisplayFunction -> $RasterFunction ]
 * is called. The results are saved in a list.
 * At the end, $AnimationFunction[ list ] is called.
 * Normally the idea is that $RasterFunction leaves a raster (or other)
 * image of the current frame in a file named filename.
 * $AnimationFunction then calls an external program to do the animation.
 * Alternatively, $RasterFunction can be Identity, collecting
 * the graphics themselves in the list.
 *
 * The values of these two functions should be set in
 * the device-dependent graphics initialization file.
 *
 * The defaults below concatenate the PostScript code of all frames
 * and write it to $Display.
 * This default is appropriate for the graphics renderers supplied
 * by Wolfram Research and for frontend versions.
 *
 * The combined PostScript code of an animation can be saved
 * in the file file.anim with the command
 *
 *	DisplayAnimation["file.anim", glist]
 *
 *)

(* defaults for $RasterFunction and $AnimationFunction
   in case the graphics init file did not define them *)

If[ !ValueQ[System`$RasterFunction],
	$RasterFunction = Identity ]
If[ !ValueQ[System`$AnimationFunction],
	$AnimationFunction = DisplayAnimation[$Display, #]& ]

(* DisplayAnimation *)

(* work on multiple output channels sequentially *)

DisplayAnimation[disp_List, pics_, rest___] :=
    DisplayAnimation[#, pics, rest]& /@ disp

DisplayAnimation[display_, pics_, rest___] :=
	CallAnimation[ display, pics, rest]

(* open file, if it is a string that does not refer to a stream *)

CallAnimation[display_String, pics_, rest___] :=
	Module[{stream, res, open},
		stream = Streams[display];
		open = Length[stream] > 0;
		If[!open, stream = OpenWrite[display], stream=stream[[1]]];
		If[ stream === $Failed, Return[stream]];
		res = CallAnimation[stream, pics, rest];
		If[!open, Close[stream]];
		res
	]

(* write all frames to presumably open stream *)

CallAnimation[display_, pics_, rest___] :=
	Display[display, #, rest]& /@ pics

(* end of defaults section *)

Pixelize[ go_, rasterFunction_, opts___ ] :=
	Module[ {gtype = Head[go]},
	    If[gtype === List, gtype = Head[First[Flatten[go]]]];
		Show[ go, DisplayFunction -> rasterFunction,
		          FilterOptions[gtype, opts] ]
	]


ShowAnimation[ gl_List, opts___?OptionQ ] :=
	Module[{res, rasterFunction, animationFunction},
		{rasterFunction, animationFunction} =
		    {RasterFunction, AnimationFunction} /.
		    Flatten[{opts, Options[ShowAnimation]}];
		res = Pixelize[#, rasterFunction, opts]& /@ gl;
		animationFunction[ res ]
	]

Attributes[Animate] = {HoldFirst};
Animate[ function_, {t_, t0_, t1_, dt_:Automatic}, opts___?OptionQ ] :=
	Module[{res, rasterFunction, animationFunction,
	       ndt = dt, closed, nt1 = t1, frames},
	    {closed, rasterFunction, animationFunction, frames} =
	        {Closed, RasterFunction, AnimationFunction, Frames - 1}/.
	        Flatten[{opts, Options[Animate], Options[ShowAnimation]}];
		If[ dt === Automatic,
			If[ closed, frames++ ];
			ndt = (t1 - t0)/frames ];
		If[ closed, nt1 -= ndt];
		Block[{$DisplayFunction = Identity,
		       $SoundDisplayFunction = Identity},
		  res = Table[ Pixelize[function, rasterFunction, opts],
			          {t, t0, nt1, ndt} ]
		];
		animationFunction[ res ]
	]

(* the following borrow their options from Animate *)

Attributes[MoviePlot] = {HoldFirst};
MoviePlot[ function_, xRange_List, animationRange_List, opts___?OptionQ ] := 
	Animate[ Plot[function, xRange, DisplayFunction->Identity,
	            Evaluate[FilterOptions[Plot, opts]]], 
	       animationRange, FilterOptions[Animate, opts] ];

Attributes[MoviePlot3D] = {HoldFirst};
MoviePlot3D[ function_, xRange_List, yRange_List, animationRange_List, opts___?OptionQ ] := 
	Animate[ Plot3D[function, xRange, yRange, DisplayFunction->Identity,
	            Evaluate[FilterOptions[Plot3D, opts]]], 
	       animationRange, FilterOptions[Animate, opts] ];

Attributes[MovieDensityPlot] = {HoldFirst};
MovieDensityPlot[function_, xRange_List, yRange_List, animationRange_List, opts___?OptionQ ] :=
	Animate[ DensityPlot[function, xRange, yRange, DisplayFunction->Identity,
	            Evaluate[FilterOptions[DensityPlot, opts]]], 
	       animationRange, FilterOptions[Animate, opts] ];

Attributes[MovieContourPlot] = {HoldFirst};
MovieContourPlot[ function_, xRange_List, yRange_List, animationRange_List, opts___?OptionQ ] := 
	Animate[ ContourPlot[function, xRange, yRange, DisplayFunction->Identity,
	            Evaluate[FilterOptions[ContourPlot, opts]]], 
	       animationRange, FilterOptions[Animate, opts] ];

Attributes[MovieParametricPlot] = {HoldFirst};
MovieParametricPlot[ function_, xRange_List, animationRange_List, opts___?OptionQ ] := 
	Animate[ ParametricPlot[function, xRange, DisplayFunction->Identity,
	            Evaluate[FilterOptions[ParametricPlot, opts]]], 
	       animationRange, FilterOptions[Animate, opts] ];

SpinShow[theGraphic_, options___?OptionQ] :=
    Module[{spinOrigin, spinTilt, spinDistance, spinRange, rotateLights,
            closed, frames, lightSources, thetaFactor, phiFactor,
            rhoFactor, theta},
       (* handle the options *)
        {spinOrigin, spinTilt, spinDistance, spinRange, rotateLights,
            closed, frames} = {SpinOrigin, SpinTilt, SpinDistance,
            SpinRange, RotateLights, Closed, Frames}/.
            Flatten[{options, Options[SpinShow]}];
        lightSources = LightSources /.
            Flatten[{Options[theGraphic], Options[Head[theGraphic]]}];
      (* handle the angles *)
        thetaFactor = Switch[rotateLights,  False, 0, 
	                                    True, 1,
	                                    Opposite, -1,
	                                    _, 0];
	    phiFactor = If[rotateLights, 1, 0, 0];
	    rhoFactor = If[rotateLights, 1, 0, 0];
	  (* do the animation *)
	    Animate[ Show[theGraphic, DisplayFunction -> Identity,
		    ViewPoint->(spinOrigin +
		     spinDistance Rotate3D[{1,0,0}, theta, spinTilt[[1]], spinTilt[[2]]]),
		    LightSources ->  Map[{Rotate3D[#[[1]], 
					thetaFactor theta,
					phiFactor spinTilt[[1]],
					rhoFactor spinTilt[[2]]], #[[2]]}&, 
					lightSources],
	            FilterOptions[Head[theGraphic], options],
	            SphericalRegion -> True],
	       {theta, spinRange[[1]], spinRange[[2]]},
	       Closed -> closed, Frames -> frames
	    ]
	]

End[]   (* Graphics`Animation`Private` *)

Protect[Evaluate[Context[] <> "*"]]

EndPackage[]   (* Graphics`Animation` *)

(*:Examples:
Animate[ Plot[ Sin[x t], {x,-3,3}, PlotRange->{-1, 1} ], {t,0,1} ]

MoviePlot[ Sin[x t], {x,-5,5}, {t,0,1}, PlotRange->{-1, 1} ]

MoviePlot3D[ Sin[ x y t], {x,-2,2}, {y,-2,2}, {t,0,1},
    PlotRange->{-1, 1}, Frames->24]

MovieParametricPlot[ {Sin[x t], Cos[x t]}, {x, -Pi, Pi}, {t, 0, 1, 1/11},
	PlotRange->{{-1, 1}, {-1, 1}}, AspectRatio->1 ]

graphics = Plot3D[ Sin[x y],{x,-2,2},{y,-2,2}, Axes->None, Boxed->False];
SpinShow[ graphics ]

*)
