(* ::Package:: *)

(* ::Title:: *)
(*Surface of Revolution*)


(* ::Subsection:: *)
(*Information*)


(* ::Text:: *)
(*Mathematica Version: 	7.0*)
(*Package Version:		1.0*)
(*Copyright: *)
(*Name:				SurfaceOfRevolution` *)
(*Context:			SurfaceOfRevolution` *)
(*Title:				Surface of Revolution *)
(*Author: 			Axel Kilian *)
(*Summary:			This package calculates volume and surface of a SurfaceOfRevolution.*)
(*				It also plots the SurfaceOfRevolution very much like RevolutionPlot3D,*)
(*				but with different defaults and some additional options.*)
(*Keywords:			SurfaceOfRevolution *)
(*Requirements: 		None. *)
(*Source:*)
(*History:			Version 1.0 by Axel Kilian, April 2009.*)
(*Limitations: *)
(*Discussion:*)


(* ::Subsection:: *)
(*Begin Package*)


BeginPackage["SurfaceOfRevolution`"]


(* ::Subsection:: *)
(*usage*)


(* ::Subsubsection:: *)
(*volume and surface*)


sorVolume::usage = 
"sorVolume[f, {a,b}, opts] gives the numerical value of the volume \
of a surface of revolution generated by rotating \
the graph of the function f around the independent variable axis. \
The function f must be non-negative and real-valued within the interval {a,b}.";

sorSurface::usage = 
"sorSurface[f, {a,b}, opts] gives the numerical value of the surface \
of a surface of revolution generated by rotating \
the graph of the function f around the independent variable axis. \
The function f must be non-negative and real-valued within the interval {a,b}.";



(* ::Subsubsection:: *)
(*plot*)


sorPlot3D::usage =
"sorPlot3D[f, {a,b}, opts] plots the surface of \
revolution generated by rotating the graph of the \
function f around the independent variable axis. The \
function f must be real-valued within the interval {a,b}.";

(* ==================== options ==================== *)
sorPlotStyle::usage =
"sorPlotStyle is an option for sorPlot3D \
which combines several plotstyle settings in order to achieve a certain appearance. \
Possible values are \"useColorFunction\" (default), \"SoapBubble\", \"GoldPlated\", \"xyzMesh\". ";

radiusToAxisRatio::usage =
"radiusToAxisRatio is an option for sorPlot3D \
which overwrites the option BoxRatios of PlotStyle and constists of only one real number. \
The default is 1. Possible values: any positive number or Automatic";

sorMethod::usage =
"sorMethod is an option for sorVolume and sorSurface \
which switches between numerical and analytical calculation. \
The default is \"analytical\". Possible values: \"numerical\" or \"analytical\"";


(* ::Subsection:: *)
(*Begin["`Private`"]*)


(************************************************************************)
Begin["`Private`"]
(************************************************************************)


(* ::Subsubsection:: *)
(*check if function f is real - valued within the interval {a, b}*)


isReal[f_, {a_,b_}] := 
Module[
	{x, xWithinInterval, isOk=False},
	xWithinInterval = x>a && x<b;
	If[ Reduce[xWithinInterval] == Reduce[xWithinInterval&&f[x]\[Element]Reals], isOk=True];
	If[ ! isOk, Message[isReal::notrealval]];
	isOk
]


isRealAndNotNegative[f_, {a_,b_}] := 
Module[
	{x, xWithinInterval, isOk=False},
	xWithinInterval = x>a && x<b;
	If[ Reduce[xWithinInterval] == Reduce[xWithinInterval&&f[x]>=0], isOk=True];
	If[ ! isOk, Message[isRealAndNotNegative::negval]];
	isOk
]


isValidInterval[{a_,b_}] := 
Module[
	{isOk = a<b},
	If[ ! isOk, Message[isValidInterval::badinterval]];
	isOk
]


(* ::Subsubsection:: *)
(*Messages for sorVolume, sorSurface, and sorPlot3D*)


isRealAndNotNegative::negval = "the function is negative within {a, b}.";


isReal::notrealval = "the function is not real-valued within {a, b}.";


isValidInterval::badinterval = "for {a, b}, a < b is not valid";


sorSurface::unreliablevalue = "Warning: the value beeing calculated may be wrong";


(* ::Subsubsection:: *)
(*volume and surface*)


Options[sorVolume] = Options[sorSurface] = {sorMethod -> "analytical"};

sorVolume[f_, {a_,b_}, opts___?OptionQ]:=
Module[
	{retval},
	If[ ! isValidInterval[{a,b}], Return[]];
	If[ ! isReal[f,{a,b}], Return[]];

	method = sorMethod /. Flatten[{opts, sorMethod -> "analytical"}];
	If[
		method == "numerical",
		retval = NIntegrate[Pi f[x]^2, {x,a,b}],
		retval = Integrate[Pi f[x]^2, {x,a,b}];
		If[Head[retval]==Integrate, retval = sorVolume[f, {a,b}, sorMethod -> "numerical"]]
	];
	retval
]

sorSurface[f_, {a_,b_}, opts___?OptionQ]:=
Module[
	{retval, method},

	If[! isValidInterval[{a,b}], Return[]];
	If[! isReal[f,{a,b}], Return[]];
	If[! isRealAndNotNegative[f,{a,b}], Message[sorSurface::unreliablevalue]];

	method = sorMethod /. Flatten[{opts, sorMethod -> "analytical"}];
	If[
		method == "numerical",
		retval = NIntegrate[2Pi f[x]Sqrt[1+f'[x]^2], {x,a,b}],
		retval = Integrate[2Pi f[x]Sqrt[1+f'[x]^2], {x,a,b}];
		If[Head[retval]==Integrate, retval = sorSurface[f, {a,b}, sorMethod -> "numerical"]]
	];
	retval
]


(* ::Subsubsection:: *)
(*sorPlot3D*)


Options[sorPlot3D] = 
	Join[
		{sorPlotStyle -> "useColorFunction", radiusToAxisRatio->1},
		Options[RevolutionPlot3D]
	];

RevolutionPlot3DOptionNames = Map[First, Options[RevolutionPlot3D]];

sorPlot3D[f_, {a_,b_}, opts___?OptionQ]:=
Module[
	{sorPlotStyleVal, radiusToAxisRatioVal, sorPlotStyleOpts,
	 sorDefaultOpts,RevolutionPlot3Dopts,allOpts},

	If[! isValidInterval[{a,b}], Return[]];(* return with errormessage *)
	If[! isReal[f,{a,b}], Return[]];(* return with errormessage *)

	{sorPlotStyleVal, radiusToAxisRatioVal} = 
	  {sorPlotStyle, radiusToAxisRatio} /. Flatten[{opts, Options[sorPlot3D]}];
	sorPlotStyleOpts =
	Switch[
	  sorPlotStyleVal,
	  "useColorFunction",
		{Mesh->None,ColorFunction->(ColorData["Rainbow"][#1]&)},
	  "SoapBubble",
		{Mesh->None,PlotStyle->Directive[Pink,Opacity[0.5],Specularity[White,10]]},
	  "GoldPlated",
		{Mesh->None,PlotStyle->{Orange,Specularity[White,20]}},
	  "xyzMesh",
		{MeshFunctions->{#1 #2 #3&},
		 MeshStyle->None,
		 MeshShading->{Purple,Yellow,Pink,Blue}}
	];
	sorDefaultOpts =
		{BoxRatios->{1, radiusToAxisRatioVal, radiusToAxisRatioVal},
		 PlotPoints->30,
		 Axes->None,
		 RevolutionAxis->{1,0,0}
	};
	RevolutionPlot3Dopts = FilterRules[Flatten[{opts}], RevolutionPlot3DOptionNames];
	allOpts = Flatten[{RevolutionPlot3Dopts,sorPlotStyleOpts,sorDefaultOpts}];
	
	RevolutionPlot3D[f[x], {x,a,b}, Evaluate[Sequence@@allOpts]]
]


(* ::Subsection:: *)
(*end `Private` and packageContext*)


End[]             (* end `Private` Context                              *)


EndPackage[]      (* end package Context                                *)
