# Category: Complex Analysis
# Name: Visualizing complex mappings, wandering ball
#
# Shows a bouncing wandering ball and its image under a complex map.
# If the mouse is within the plot window, then the mouse location is
# used for the ball location.  The ball has a cross inside it, but
# this can be changed into an angle by setting draw_angle to true
# below.
# Source is in blue and the target is in red.
#

#The function to plot
#function f(z) = z+(0.3+0.5i);
#function f(z) = 2*z;
#function f(z) = 1i*z;
#function f(z) = (3+1i)*z;
#function f(z) = (3+1i)*z+(0.3+0.5i);
#function f(z) = z^2;
#function f(z) = z^3;
function f(z) = (z-0.5)*(z+0.5);
#function f(z) = z^2*(z-0.5)
#function f(z) = 0.25/z;
#function f(z) = 0.25/conj(z);
#function f(z) = exp(z);
#function f(z) = sin(3*z);
#function f(z) = 0.1*(z^2+1i*z+sin(5*z^2));

LinePlotDrawLegends = false;
PlotWindowPresent(); # Make sure the window is raised
LinePlotClear(); # make sure we are in the line plot mode

#approximately square grid
LinePlotWindow = [-1.3,1.3,-1.0,1.0];

#confine possibly to a smaller window.
#You can also zoom out during the animation.
#confine_window = [-8,8,-7,7];
confine_window = LinePlotWindow;

#the circle
radius = 0.3;
#step = 0.025;
step = 0.01;

#draw angle instead of cross in the ball
draw_angle = false;

angle1 = 0.05*pi;
angle2 = 0.3*pi;

# precision multiplier.  Increasing this number increases the
# number of points
#mult=1;
mult = 3;
#mult = 10;


circlepts = ApplyOverMatrix((0:(80*mult))',`(k)=radius*exp(k*1i*2*pi/(80*mult)));

#cross are vertical and horizontal line
line1pts = ApplyOverMatrix((0:(30*mult))',`(k)=radius*(2.0*k/(30*mult)-1));
line2pts = ApplyOverMatrix((0:(30*mult))',`(k)=1i*radius*(2.0*k/(30*mult)-1));

# angle has radius 3 times that of the ball
line1anglepts = ApplyOverMatrix((0:(50*mult))',`(k)=(radius*3*k/(30*mult))*exp(1i*angle1));
line2anglepts = ApplyOverMatrix((0:(50*mult))',`(k)=(radius*3*k/(30*mult))*exp(1i*angle2));



function draw_ball(pt) = (
	PlotCanvasFreeze ();
	LinePlotClear ();

	points1 = pt + circlepts;
	if draw_angle then (
		points2 = pt + line1anglepts;
		points3 = pt + line2anglepts;
	) else (
		points2 = pt + line1pts;
		points3 = pt + line2pts;
	);
	LinePlotDrawLine (points1, "color", "blue");
	LinePlotDrawLine (points2, "color", "blue");
	LinePlotDrawLine (points3, "color", "blue");

	LinePlotDrawLine (ApplyOverMatrix (points1, f),
	                  "color", "red");
	LinePlotDrawLine (ApplyOverMatrix (points2, f),
	                  "color", "red");
	LinePlotDrawLine (ApplyOverMatrix (points3, f),
	                  "color", "red");

	PlotCanvasThaw ();
);

dir = exp(1i*rand()*2*pi);
pt = 0;
lastp = null;

while true do (
	p = LinePlotMouseLocation ();
	if IsNull(p) then break;
	

	# if mouse within window, use that
	if confine_window@(1) <= p@(1) <= confine_window@(2) and
	   confine_window@(3) <= p@(2) <= confine_window@(4) then (
		pt = p@(1) + 1i*p@(2);
		# if at the same point, then just try again;
		if pt == lastp then (wait(0.0001);continue);
		lastp = pt
	);

	draw_ball(pt);

	# Now wander around

	pt = pt+step*dir;

	if (Re(pt) < confine_window@(1)+radius) then (
		pt = (confine_window@(1)+radius) + 1i*Im(pt);
		dir = -1i*conj(1i*dir)
	);
	if (Re(pt) > confine_window@(2)-radius) then (
		pt = (confine_window@(2)-radius) + 1i*Im(pt);
		dir = -1i*conj(1i*dir)
	);
	if (Im(pt) < confine_window@(3)+radius) then (
		pt = Re(pt) + 1i*(confine_window@(3)+radius);
		dir = conj(dir)
	);
	if (Im(pt) > confine_window@(4)-radius) then (
		pt = Re(pt) + 1i*(confine_window@(4)-radius);
		dir = conj(dir)
	);

	dir = dir*exp(1i*(rand()*0.2-0.1));

	wait(0.0001)
);
