(** isoglass.m -- function for an iso glass rendering.
 * @author Eric Laroche
 * @version @(#)$Id: isoglass.m,v 1.4 1998/01/17 14:42:38 laroche Exp $
 **)

(* package isoglass` *)
BeginPackage["isoglass`"]

(* exported variables and functions *)
isoglasslength::usage =
	"isoglasslength[]: iso glass length"
isoglassradius::usage =
	"isoglassradius[]: iso glass maximal radius"
isoglassprofile::usage =
	"isoglassprofile[z]: iso glass radius profile function"
isoglassplot::usage =
	"isoglassplot[]: plot of the iso glass"

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

(* the parameters for the glass rendering functions are:
 * footradius -- maximum foot radius
 * stemradius -- minimum stem radius
 * stemlength -- stem length
 * cupradius -- maximum cup radius
 * lowercuplength -- length below cupradius
 * openingradius -- opening radius
 * uppercuplength -- length above cupradius
 *)
(* glass profile function *)
glassprofile[z_,
	{footradius_, stemradius_, stemlength_,
	cupradius_, lowercuplength_, openingradius_, uppercuplength_}] :=

	Module[{stem, lowercup, uppercup},

	(* stem: negative exponential for now, approximate stemradius *)
	stem[zz_] = (footradius-stemradius) Exp[
		-25 zz/stemlength]+stemradius;

	(* lower part: quarter of an ellipsoid: horizontal at lowest,
		vertical at lowercuplength *)
	lowercup[zz_] = r /.
		(* solve to r[zz], get the positive solution *)
		Solve[(zzz/lowercuplength)^2+(r/cupradius)^2 == 1,
			r][[-1, 1]] /.
		(* inverse zz *)
		zzz->lowercuplength-zz;
	(* assertions *)
	(* (D[lowercup[zz], zz] /. zz->0) == ComplexInfinity; *)

	(* upper part: paraboloid, vertical at 0 *)
	uppercup[zz_] = p zz^2 + cupradius /.
		(* solve the parable parameter *)
		Solve[(p zz^2 + cupradius == r) /.
			{zz->uppercuplength, r->openingradius},
			p][[1, 1]];
	(* assertions *)
	(uppercup[zz] /. zz->0) == (lowercup[zz] /. zz->lowercuplength);
	(D[uppercup[zz], zz] /. zz->0) ==
		(D[lowercup[zz], zz] /. zz->lowercuplength);

	(* putting it all together *)
	Which[
		z < 0,
			0,
		z < stemlength,
			stem[z],
		z < stemlength+lowercuplength,
			Max[stem[z], lowercup[z-stemlength]],
		z <= stemlength+lowercuplength+uppercuplength,
			uppercup[z-(stemlength+lowercuplength)],
		True,
			0]];

(* total glass length *)
glasslength[{footradius_, stemradius_, stemlength_,
	cupradius_, lowercuplength_, openingradius_, uppercuplength_}] :=
	stemlength+lowercuplength+uppercuplength;

(* maximal glass radius *)
glassradius[{footradius_, stemradius_, stemlength_,
	cupradius_, lowercuplength_, openingradius_, uppercuplength_}] :=
	Max[footradius, stemradius, cupradius, openingradius];

(* rotated profile plot *)
rotateplot[profile_, {zbegin_, zend_}] :=
	ParametricPlot3D[{
		Cos[phi] profile[z],
		Sin[phi] profile[z],
		z},
		{z, zbegin, zend}, {phi, 0, 2 Pi},
		DisplayFunction->Identity];

(* iso glass parameters, millimeters *)
isoglassparameters = {
	32.5, (* foot radius *)
	4.5, (* stem radius *)
	55, (* stem length *)
	32.5, (* maximal cup radius (==foot radius) *)
	32.5, (* lower cup length (==maximal cup radius) *)
	23, (* opening radius *)
	67.5}; (* upper cup length (==100-lower cup length) *)

(* function instances for the iso glass *)
isoglassprofile[z_] = glassprofile[z, isoglassparameters];
isoglasslength[] = glasslength[isoglassparameters];
isoglassradius[] = glassradius[isoglassparameters];

(* iso glass plot function *)
isoglassplot[] := rotateplot[isoglassprofile, {0, isoglasslength[]}];

(* package end *)
End[]
EndPackage[]

(* draw the profile *)
Plot[isoglassprofile[z], {z, 0, isoglasslength[]},
	PlotRange->{{0, isoglasslength[]}, {0, isoglassradius[]}},
	AxesOrigin->{0, 0},
	AspectRatio->isoglassradius[]/isoglasslength[]];

(* draw the glass *)
Show[isoglassplot[],
	Boxed->False,
	AxesEdge->None,
	DisplayFunction->$DisplayFunction];
