tikz.within( '*' )

local cos = math.cos
local sin = math.sin
local pi  = math.pi

-- granularity of calculation
local fine = 10

-- factor the projection length is reduced
local fproj = 0.35

-- strength of the ellipse mediums
local s1 = 1.00
local s2 = 1.50
local s3 = 1.75

local e1 = ellipse{ at=p{ 0.00,  0.00 }, xradius=3.00, yradius=3.00 }
local e2 = ellipse{ at=p{ 0.40,  1.20 }, xradius=0.85, yradius=0.50 }
local e3 = ellipse{ at=p{ 0.00, -0.89 }, xradius=1.00, yradius=0.60 }

draw{ fill='black!08!white', line_width=1, e1 }
draw{ fill='black!16!white', line_width=1, e2 }
draw{ fill='black!24!white', line_width=1, e3 }

-- list of projection angles
local listphi =
{
	-1/4 * pi,
	 2/4 * pi,
	 5/4 * pi,
}

-- length of projection lines
local lenmaintop = 5.0
local lenmainbot = 6.5

for proji, phimain in ipairs( listphi )
do
	local lmain =
		line{
			p{ cos( phimain ), sin( phimain ) } * -lenmaintop,
			p{ cos( phimain ), sin( phimain ) } *  lenmainbot,
		}
	local phinorm = phimain - pi/2

	-- gets a point on the projection.
	-- dp: distance from projection center
	-- dv: projection value
	function getpproj( dp, dv )
		return(
			lmain.p2
			+ p{ cos( phinorm ) * dp,         sin( phinorm ) * dp         }
			+ p{ cos( phimain ) * dv * fproj, sin( phimain ) * dv * fproj }
		)
	end

	-- distance of projection line
	local ddis = 1 / fine

	-- list of all projection points
	local listp = { }

	for i = -3.5 * fine, 3.5 * fine
	do
		local p1i =
			lmain.p1
			+ p{ cos( phinorm ) * ddis * i, sin( phinorm ) * ddis * i }
		local li = line{ p1i, p1i + ( lmain.p2 - lmain.p1 ) }
		local lv = 0
		local is1 = e1.intersectLine( li )

		if is1 and #is1 > 1
		then
			local lis = line{ is1[ 1 ], is1[ 2 ] }
			lv = lv + lis.length * s1
		end

		local is2 = e2.intersectLine( li )
		if is2 and #is2 > 1
		then
			local lis = line{ is2[ 1 ], is2[ 2 ] }
			lv = lv + lis.length * s2
		end

		local is3 = e3.intersectLine( li )
		if is3 and #is3 > 1
		then
			local lis = line{ is3[ 1 ], is3[ 2 ] }
			lv = lv + lis.length * s3
		end

		local pproj = getpproj( ddis * i, lv    )
		if i % ( 0.7 * fine ) == 0
		then
			draw{
				dotted, arrow, line_width=0.5,
				line{ li.p1, getpproj( ddis * i, -0.20 ) },
			}
			draw{
				dotted, line_width=0.5,
				line{ li.p1, pproj },
			}
		end
		table.insert( listp, pproj )
	end

	-- draws the projection screen
	local lenlproj = 8
	draw{
		line{
			getpproj(  lenlproj / 2, 0 ),
			getpproj( -lenlproj / 2, 0 ),
		}
	}

	-- draws the projection curve
	draw{ line_width=1, polyline{ table.unpack( listp ) } }
end
