Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moving a shadow around a circle

I have a circle which orbits every 10 seconds. And i am trying to cast a shadow which is angled towards the orbit origin (the light source) whilst also taking into account the camera angle as well.

The shadow works for some angles but as the camera goes more edge on or more top down, it starts to look less accurate and I have no idea how to correct for it - it seems like a complicated math issue that I am struggling to figure out how to solve.

This is the animation: http://jsfiddle.net/8y2bm88w/

And my draw code for the shadow:

ctx.beginPath();

//rotate shadow with the planet
ctx.translate(originX + obj[i].x, originY + obj[i].y);
ctx.rotate(obj[i].angle); //rotate around origin
ctx.translate(-(originX + obj[i].x), -(originY + obj[i].y));


var offsetX = -(10 * Math.sin(obj[0].angle)); //i feel this is the issue
var offsetY = 0; //this too

ctx.rect(originX + obj[i].x + offsetX, originY + obj[i].y + offsetY - 10, 20, 20);
ctx.fillStyle = 'rgba(213,0,0,0.9)'; //red shadow - easier to see
ctx.fill();

The code makes more sense via the JSFiddle as it puts the code into more context.

So i think this is to do with the maths for offsetX and offsetY variables, as the user changes the camera angle the offset's need to accommodate and change the way in which the shadow moves. But, this is really confusing how to solve.

like image 624
Sir Avatar asked Nov 03 '15 04:11

Sir


1 Answers

You're hitting this because your obj[i].angle is based on the ellipse coördinates and not the x-y coördinates of the top-down view (which is actually what you're generating everything else from so we don't need to do calculations for it).

You will need both angles, so I've added a second property for the other angle. The ellipse angle is needed for the rotation of the context so you can be perpendicular to the vector from the origin as viewed. The new angle is for the calculations. You can think of it as a non-2D-mapped version.

obj[i].trueAngle = angle;

Now we have the value to work with, I suggest drawing a semi-circle for the "dark side" and then adding to or cutting out from this region as required using a curve of your choice to complete the shadow, e.g.

// centre on planet
ctx.translate(originX + obj[i].x, originY + obj[i].y);
ctx.rotate(obj[i].angle - Math.PI / 2);
// semicircle dark side
ctx.arc(0, 0, 12, 0, Math.PI, false);
// path along terminator
var offset_terminator = -20 * Math.sin(obj[0].trueAngle) * (1 - camera.angle);
ctx.bezierCurveTo(-7, offset_terminator, 7, offset_terminator, 12, 0);
//fill
ctx.fillStyle = 'rgba(213,0,0,0.9)'; //red shadow - easier to see
ctx.fill();

Here I also made the shadow a couple of px bigger than the planet, I just played with numbers until I found some that looked good.

Please note I set up rotation so 0, 0 is the middle of the planet and the x-axis moves along the terminator

DEMO

like image 194
Paul S. Avatar answered Oct 19 '22 01:10

Paul S.