With much help from the SO community, I've created a program where a ball is in a triangle with a certain position and velocity. Using parametric equations, I've determined where the ball will hit the triangle. I then found the perpendicular to the side of the triangle where the ball would hit, so that I could reflect the ball off the wall, to plot its "bounce" trajectory. My goal is that I can plot how the ball will bounce off the walls for n iterations. But I've come up against a problem.
Even though I'm using the p5.js A.reflect(B) method to reflect the ball along the perpendicular, it's producing some truly strange results.
I've tried many things (i.e., modifying the getPerp() function, unit testing, etc.), but to no avail. Any help would be greatly appreciated. MWE below and in Codepen.
let angle = 0;
let sides = [];
let vertices = [];
const len = 100;
function setup() {
createCanvas(windowWidth, windowHeight);
angleMode(DEGREES);
angleOne = createSlider(0, 89, 60);
angleOne.position(10, 10);
angleOne.style("width", "80px");
angleTwo = createSlider(0, 89, 60);
angleTwo.position(10, 30);
angleTwo.style("width", "80px");
rotateVel = createSlider(0, 360, 60);
rotateVel.position(10, 50);
rotateVel.style("width", "80px");
// Initial vertice & side setup (these don't change)
let v1 = createVector(width / 2 - len / 2, height / 2);
let v2 = createVector(width / 2 + len / 2, height / 2);
sides[0] = new Side(v1.x, v1.y, v2.x, v2.y, "green");
vertices[0] = new Vertex(v1.x, v1.y);
vertices[1] = new Vertex(v2.x, v2.y);
}
function draw() {
background(255);
let angOne = angleOne.value();
let angTwo = angleTwo.value();
let rotVel = rotateVel.value();
fill(0);
strokeWeight(0);
textSize(15);
text(angOne, 100, 25);
text(angTwo, 100, 45);
text(rotVel, 100, 65);
let v2Offset = createVector(len * cos(-angOne), len * sin(-angOne));
let v3Offset = createVector(-len * cos(angTwo), -len * sin(angTwo));
vertices[2] = new Vertex(
vertices[0].a.x + v2Offset.x,
vertices[0].a.y + v2Offset.y
);
vertices[3] = new Vertex(
vertices[1].a.x + v3Offset.x,
vertices[1].a.y + v3Offset.y
);
// Update the sides
sides[1] = new Side(
vertices[0].a.x,
vertices[0].a.y,
vertices[2].a.x,
vertices[2].a.y
);
sides[3] = new Side(
vertices[1].a.x,
vertices[1].a.y,
vertices[3].a.x,
vertices[3].a.y
);
const m1 =
(vertices[2].a.y - vertices[0].a.y) / (vertices[2].a.x - vertices[0].a.x);
const m2 =
(vertices[3].a.y - vertices[1].a.y) / (vertices[3].a.x - vertices[1].a.x);
// Calculate the y-offset relative to vertices[0]
const b2 = (vertices[1].a.x - vertices[0].a.x) * -m2;
const xInt = b2 / (m1 - m2);
const yInt = xInt * m1;
// Note xInt and yInt are relative to vertices[0]
// draw all the things
// sides.forEach((s) => s.show());
// stroke(0, 255, 0);
// strokeWeight(20);
point(vertices[0].a.x + xInt, vertices[0].a.y + yInt);
vertices[4] = new Vertex(vertices[0].a.x + xInt, vertices[0].a.y + yInt);
sides[4] = new Side(
vertices[1].a.x,
vertices[1].a.y,
vertices[0].a.x + xInt,
vertices[0].a.y + yInt,
"blue"
);
sides[5] = new Side(
vertices[0].a.x,
vertices[0].a.y,
vertices[0].a.x + xInt,
vertices[0].a.y + yInt,
"purple"
);
scale(4); // so I can make the triangle actually *visible*
translate(-width / 3, -height / 3);
sides[0].show();
sides[4].show();
sides[5].show();
vertices[0].show();
vertices[1].show();
vertices[4].show();
strokeWeight(1);
stroke(255, 0, 0);
noFill();
arc(vertices[0].a.x, vertices[0].a.y, 40, 40, -1 * angleOne.value(), 0, PIE);
arc(
vertices[1].a.x,
vertices[1].a.y,
40,
40,
-180,
-(180 - angleTwo.value()),
PIE
);
stroke(0);
// stroke("purple"); // Change the color
strokeWeight(5); // Make the points 10 pixels in size
let P1x = vertices[0].a.x;
let P1y = vertices[0].a.y;
let P0 = createVector(P1x + 60, P1y - 40);
push();
let V0 = createVector(10 * cos(rotVel), 10 * sin(-rotVel));
pop();
point(P0.x, P0.y);
// point(P0.x + V0.x, P0.y + V0.y);
strokeWeight(2);
line(P0.x, P0.y, P0.x + V0.x, P0.y + V0.y);
let hit = wallHit(P0, V0);
// print("hit[0] is", hit[0]);
hitPoint = createVector(P0.x + V0.x * hit[0], P0.y + V0.y * hit[0]);
let A = createVector(
vertices[4].a.x - vertices[1].a.x,
vertices[4].a.y - vertices[1].a.y
);
// let B = p5.Vector.sub(vertices[1].a, vertices[2].a);
// let C = p5.Vector.sub(vertices[2].a, vertices[4].a);
// findNormal(hitPoint, A);
setLineDash([5, 5]);
stroke(0);
// let perpA = getY(vertices[1].a.x - 100, hitPoint);
// let normalA = createVector(vertices[1].a.x - 100, perpA);
// let incVec = createVector(hitPoint.x, hitPoint.y);
// let refVec = normalA.reflect(incVec);
let IOne = createVector(0, 0);
let ITwo = createVector(0, 0);
let IThree = createVector(0, 0);
// line(hitPoint.x, hitPoint.y, normalA.x, normalA.y);
let AOne = createVector(P1x, P1y);
let ATwo = createVector(vertices[1].a.x, vertices[1].a.y + 0.1);
let AThree = createVector(
(P1x + vertices[1].a.x) / 2,
(P1y + vertices[1].a.y) / 2
);
// getPerp(AOne, ATwo, hitPoint);
let BOne = createVector(P1x, P1y);
let BTwo = createVector(vertices[4].a.x, vertices[4].a.y + 0.1);
let BThree = createVector(
(P1x + vertices[4].a.x) / 2,
(P1y + vertices[4].a.y) / 2
);
// getPerp(BOne, BTwo, hitPoint);
let COne = createVector(vertices[1].a.x, vertices[1].a.y);
let CTwo = createVector(vertices[4].a.x, vertices[4].a.y + 0.1);
let CThree = createVector(
(vertices[1].a.x + vertices[4].a.x) / 2,
(vertices[1].a.y + vertices[4].a.y) / 2
);
// getPerp(COne, CTwo, hitPoint);
let incVec = createVector(hitPoint.x, hitPoint.y);
line(P0.x, P0.y, incVec.x, incVec.y);
let perp = getPerp(IOne, ITwo, IThree);
let testOne = createVector(vertices[0].a.x + 20, vertices[0].a.y + 20);
let testTwo = createVector(vertices[1].a.x + 20, vertices[1].a.y + 20);
let refVec = testOne.reflect(testTwo);
// line(testOne.x, testOne.y, testTwo.x, testTwo.y);
if (hit[1] == "t3") {
perp = getPerp(AOne, ATwo, hitPoint);
refVec = perp.reflect(incVec);
} else if (hit[1] == "t1") {
perp = getPerp(BOne, BTwo, hitPoint);
refVec = perp.reflect(incVec);
} else {
perp = getPerp(COne, CTwo, hitPoint);
refVec = perp.reflect(incVec);
}
stroke("black");
line(hitPoint.x, hitPoint.y, refVec.x, refVec.y);
setLineDash([0, 0]);
}
function setLineDash(list) {
drawingContext.setLineDash(list);
}
class Side {
constructor(x1, y1, x2, y2, col = "black") {
this.a = createVector(x1, y1);
this.b = createVector(x2, y2);
this.color = col;
}
show() {
stroke(this.color);
strokeWeight(4);
line(this.a.x, this.a.y, this.b.x, this.b.y);
}
}
class Vertex {
constructor(x1, y1) {
this.a = createVector(x1, y1);
}
show() {
stroke(255, 0, 0);
strokeWeight(10);
point(this.a.x, this.a.y);
}
}
function wallHit(pos, vel) {
let P1x = vertices[0].a.x;
let P1y = vertices[0].a.y;
let P2x = vertices[1].a.x;
let P2y = vertices[1].a.y;
let P3x = vertices[4].a.x;
let P3y = vertices[4].a.y;
let A1 = P3y - P1y;
let B1 = -(P3x - P1x);
let C1 = A1 * P1x + B1 * P1y;
let A2 = -(P3y - P2y);
let B2 = P3x - P2x;
let C2 = A2 * P2x + B2 * P2y;
let A3 = -(P2y - P1y);
let B3 = P2x - P1x;
let C3 = A3 * P2x + B3 * P2y;
let t1 = (C1 - A1 * pos.x - B1 * pos.y) / (A1 * vel.x + B1 * vel.y);
let t2 = (C2 - A2 * pos.x - B2 * pos.y) / (A2 * vel.x + B2 * vel.y);
let t3 = (C3 - A3 * pos.x - B3 * pos.y) / (A3 * vel.x + B3 * vel.y);
let times = [t1, t2, t3];
let posTimes = [];
for (let i = 0; i < times.length; i++) {
times[i] = round(times[i], 2);
}
// console.log("After rounding:", times);
for (let i = 0; i < times.length; i++) {
if (times[i] > 0) {
posTimes.push(times[i]);
}
}
let hitPoint = createVector(0, 1);
// console.log("posTimes:", posTimes);
trueTime = min(posTimes);
if (trueTime == round(t1, 2)) {
// print("Hit Purple");
return [t1, "t1"];
// hitPoint = createVector(P0.x + V0.x * t1, P0.y + V0.y * t1);
// line(P0.x, P0.y, hitPoint.x, hitPoint.y);
} else if (trueTime == round(t2, 2)) {
// print("Hit Blue");
return [t2, "t2"];
// hitPoint = createVector(P0.x + V0.x * t2, P0.y + V0.y * t2);
// line(P0.x, P0.y, hitPoint.x, hitPoint.y);
} else {
// print("Hit Green");
return [t3, "t3"];
// hitPoint = createVector(P0.x + V0.x * t3, P0.y + V0.y * t3);
// line(P0.x, P0.y, hitPoint.x, hitPoint.y);
}
}
function findNormal(pos, wall) {
let perpVector = createVector(-wall.y, wall.x);
strokeWeight(2);
stroke(0);
line(pos.x, pos.y, perpVector.x, perpVector.y);
// line(P0.x, P0.y, hitPoint.x, hitPoint.y);
}
function getY(x, HP) {
let perpY =
-((vertices[4].a.x - vertices[1].a.x) / (vertices[4].a.y - vertices[1].a.y)) *
x +
(HP.y +
((vertices[4].a.x - vertices[1].a.x) / (vertices[4].a.y - vertices[1].a.y)) *
HP.x);
return perpY;
}
function getPerp(p1, p2, p3) {
x1 = p1.x;
y1 = p1.y;
x2 = p2.x;
y2 = p2.y;
x3 = p3.x;
y3 = p3.y;
stroke(0);
strokeWeight(2);
line1 = line(x1, y1, x2, y2);
m1 = (y2 - y1) / (x2 - x1);
m2 = -1 / m1;
b2 = y3 - x3 * m2;
stroke(255, 0, 0);
line(x3, m2 * x3 + b2, x1, m2 * x1 + b2);
let final = createVector(m2 * x1 + b2 - (m2 * x3 + b2), x1 - x3);
return final;
}
html,
body {
margin: 0;
padding: 0;
overflow: hidden;
}
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.min.js"></script>