I'm trying to do a function who take a point position and an axis (a line) and return the projection of the point on the axis.
- Point are simply represented by
{x, y}
coords - Axis are represented by an
f(x) = ax + b
linear function (orf(x) = C
for vertical line) and therad
value for thea
(where 0rad is right, and turn clockwise). Soaxis = {a, b, rad}
oraxis = {c, rad}
.
It should be a simple mathematical problem using Pythagorean but i cannot get the right solution.. Projections are always looking on the other directions (but look at the good distance).
Code demo:
I create a dark point on 25;100
and multiples coloured axis. Every axis have a related point (same color) who represent the projection of the dark point on it. Both grey are vertical and horizontal examples and work well but 3 others axis (Red, Green and Blue) are wrong.
I tried to redo getProjectionOnAxis
function multiple times, and even try to get the good results by testing all possibility (by inverse vars, using another cos/sin/tan function) and try to catch my issue like that but nothing to do I never get the good result.
const point = {x:25, y:100};
const axis = [
{a: 1, b: 25, rad:Math.PI/4, rgb: '255,0,0'}, // 45deg
{a: -.58, b: 220, rad:Math.PI/6, rgb: '0,255,0'}, // -30deg
{a: 3.73, b: -50, rad:5*Math.PI/12, rgb: '0,0,255'}, // 75deg
// Gray
{c: 150, rad:Math.PI/2, rgb: '100,100,100'},
{b: 150, rad:0},
];
const execute = () => {
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
drawPoint({...point, rgb:'0,0,0', ctx});
axis.forEach(_axis => {
drawAxis({axis:_axis, rgb: _axis.rgb, ctx, canvas});
const projected = getProjectionOnAxis({...point, axis: _axis});
drawPoint({...projected, rgb:_axis.rgb, ctx});
});
}
// --- Issue come from here ---
const getProjectionOnAxis = ({x,y,axis}) => {
// On vertical axis |
if (axis.c) {
return { x: axis.c, y };
}
// On horizontal axis _
if (!axis.a) {
return { x, y: axis.b };
}
// Projected on axis but perpendicular to X axis
const projectedOnX = {
x,
y: axis.a * x + axis.b,
}
// The distance between both points is the hypothenus of the triangle:
// point, projectedOnAxis, projectedOnX
// where point <-> projectedOnX is the hypothenus
// and point angle is same that axis
const distCornerToProjectedOnX = Math.abs(y - projectedOnX.y);
const distCornerToProjectedOnAxis = distCornerToProjectedOnX * Math.cos(axis.rad);
const projectedVector = {
x: distCornerToProjectedOnAxis * Math.cos(axis.rad),
y: distCornerToProjectedOnAxis * Math.sin(axis.rad),
};
return {
x: x + projectedVector.x,
y: y + projectedVector.y,
};
}
// --- Draw Functions ---
// Not really important for the issue
const drawPoint = ({x,y,rgb, ctx}) => {
ctx.save();
ctx.translate(x, y);
ctx.beginPath();
ctx.fillStyle = `rgba(${rgb},1)`;
ctx.arc(0, 0, 2, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
ctx.restore();
};
const drawAxis = ({axis, rgb, ctx, canvas}) => {
if (axis.c) {
// Vertical axis
drawLine({
from: {x: axis.c, y:0},
to: {x:axis.c, y:canvas.height},
rgb, ctx
});
}
else if (!axis.a) {
// Horizontal axis
drawLine({
from: {x:0, y:axis.b},
to: {x:canvas.width, y:axis.b},
rgb, ctx
});
}
else {
// ax + b (here a != 0)
let to = {
x: canvas.width,
y: axis.a * canvas.width + axis.b,
};
if (to.y < 0) {
to = {
x: axis.b / - axis.a,
y: 0,
}
}
drawLine({
from: {x:0, y:axis.b},
to,
rgb, ctx
});
}
}
const drawLine = ({
from, to, rgb=null, ctx
}) => {
ctx.save();
ctx.translate(from.x, from.y);
ctx.beginPath();
ctx.strokeStyle = `rgba(${rgb},1)`;
ctx.moveTo(0, 0);
ctx.lineTo(to.x - from.x, to.y - from.y);
ctx.stroke();
ctx.restore();
};
execute();
html, body, canvas { margin: 0; padding: 0;}
<canvas id="canvas" width="500" height="500"></canvas>
This is the positions I should get:
PS: I don't really like the way I manage my axis, maybe they are another (simple) way to do it ?