Determine the bullet / particle reflection off of a circle in node js using SAT lib functions

I can see how the calculation should occur from other examples such as: How to bounce a 2d point particle off of a circular edge.

But I could use some help with the specifics for my inputs given during a game loop that is progressing / moving the bullet each game loop tick, and then detecting and handling collisions.

I've tried to mock up an example nodejs script using the SAT lib http://requirebin.com/?gist=4a5c208dc39d3c3be262abb05fa791a3 .. with the console logs tracing a few variables, including the output target vector.

My question essentially lies in understanding how I can use the SAT vector functions like dot() or reflect() to produce the reflection?

https://github.com/jriecken/sat-js#classes

Even pseudo code would help me understand what exactly the operation needed to determine the reflection point (not just the angle, but the bullets new target vector) given my inputs of a bullet x,y position colliding with and a circular wall.

No need to use dot() or reflect(). Use the conservation of energy to calculate the new velocity of the ball after collision with a stationary object.

A ball hitting a circle acts as if it hit a line tangent to the point on the circle. We actually use the normal vector to this tangent line (the collision normal), so there's no need to even calculate the tangent line!

The collision normal is simply the normalized vector that starts at the center of the circle and extends to the point of collision (on the circle.)

Given the collision normal n, we can calculate the new velocity of the ball using this pseudo-code:

function resolveFixedCollision(obj, n)
set c to componentVector(obj.velocity, n)
set obj.velocity to v-2*c
end function


And explained in words:

...split the vector v into two components, one in the direction n(the normal component) and the other in the direction of the wall (the tangential component). Because there are no forces acting on the ball in the tangential direction, its velocity in that direction remains unchanged. In the other direction, the velocity is simply reversed. This way, the energy of the ball, the only moving object in the collision, remains unchanged. The end result of this is that you can find the new velocity by simply subtracting twice the normal component.

Finally, I have implemented the above algorithm using SAT vector functions, as requested. Click "Run code snippet" to see how the bullet moves. The meat of the logic is inside reflectBullet(). I also modified the representations of a_beam_of_light and a_curved_mirror:

• velocity and position changed to SAT.Vectors with x and y components
• moved the initial position of the bullet so its path before collision is more visible
• Used SAT.Circle instead of creating new SAT.Circles each time testCircleCollision() is called

var reflectBullet = function(bullet, wall, sat_response) {

function resolveFixedCollision(obj, n) {
// Set c to componentVector(obj.velocity, n).
var c = new SAT.Vector();
c.copy(obj.v);
c.project(n);

//set obj.velocity to v-2*c
c.scale(2, 2);
obj.v.sub(c);
}

// The collision normal
var n = new SAT.Vector(wall.c.pos.x - bullet.c.pos.x,
wall.c.pos.y - bullet.c.pos.y).normalize();

// Calculate new velocity of bullet after collision
resolveFixedCollision(bullet, n);

// Move bullet to point of collision (if it went through wall)
bullet.c.pos.sub(sat_response.response.overlapV);
}

var testCircleCollision = function(a, b) {
var response = new SAT.Response();
var collided = SAT.testCircleCircle(a.c, b.c, response);

return {
collided: collided,
response: response
};
};

var a_beam_of_light = {
c: new SAT.Circle(new SAT.Vector(25 - 15, 40), 1),
// x: 25,
// y: 40,
// r: 1,

v: new SAT.Vector(1, 0)
// v: 1,
// origX: 0,
// origY: 40,
// targetX: 50,
// targetY: 40,
}

var a_curved_mirror = {
c: new SAT.Circle(new SAT.Vector(50, 50), 25)
// x: 50,
// y: 50,
// r: 25
}

var step_world = function() {
// step the world forward, moving the bullet towards it's target
// a_beam_of_light.x = a_beam_of_light.x + a_beam_of_light.v;

// console.log(a_beam_of_light, a_curved_mirror)
var test_results = testCircleCollision(a_beam_of_light, a_curved_mirror);
if (test_results.collided) {
reflectBullet(a_beam_of_light, a_curved_mirror, test_results);
}
}

var el = document.getElementById('c');
var ctx = el.getContext('2d');

// draw a SAT.Circle
function renderCircle(c) {
ctx.beginPath();
ctx.arc(c.pos.x, c.pos.y, c.r, 0, 2 * Math.PI);
ctx.stroke();
}

renderCircle(a_curved_mirror.c);
renderCircle(a_beam_of_light.c);
for (var x = 0; x < 40; x++) {
step_world();
if (x % 5 == 0) {
renderCircle(a_beam_of_light.c);
}
}
canvas {
border: 1px solid #ccc
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/sat/0.6.0/SAT.min.js"></script>
<canvas id="c" width="500" height="150"></canvas>