I've gotten pretty far but something just doesn't seem to work.
A = 50.88259382849774,6.003988087177277
B = 50.88269282423443,6.0036662220954895
C = 50.882530369581545,6.003847271203995
The C coordinate is a little off from the 90 degree line (x) and this function I made should position C on the closest way to the x line.
this.snapCoords = function(a, b, c){
var result = (b.x-a.x)*(c.x-b.x)+(b.y-a.y)*(c.y-b.y);
var negative = false;
if(result < 0){
result = result*-1;
negative = true;
}
result = Math.sqrt(result);
result = result/2;
if(negative === false){
var d = {x: c.x+result, y: c.y-result};
}
else{
var d = {x: c.x-result, y: c.y+result};
}
console.log(d); // returns : 50.88246729610898,6.003910344676565
}
It does get the the 90 degree (x) line but not the closest way. Something must still be wrong in my function but I can't figure it out.
EDIT:
So this is my problem
My function puts the third coordinate on C which is 90 degrees but not where it should be (the red spot) it somehow extends to a further point.
I think the OP is trying to project the point C onto the line passing thru point B and is perpendicular to line AB. If this is the case, the math for the projection is not correct. You can find the projected point D as
D= C - dot(vec(BC), vec(AB)) * vec(AB)/|vec(AB)|^2
By this calculation, the projected point D will be (50.8825952820492, 6.00363622113846).
The following is a picture for points A, B, C and D :
Related
For a camera movement in three.js I need to calculate point C so to move the camera from point A to a certain distance dist to point B.
three.js has methods to do that very easily.
Assuming a, b, and c are instances of THREE.Vector3(),
a.set( 2, 1, 4 );
b.set( 9, 4, 2 );
c.subVectors( a, b ).setLength( dist ).add( b );
three.js r.91
So you need to calculate the coordinates of point C, given that it lies on the line between B and A at the given distance from B? This is pretty straightforward using the following steps:
Calculate the vector from B to A (this will just be A - B).
Normalize that vector (make it's length 1).
Multiply that by the distance you want.
Add that vector to the point B.
So a short javascript example:
const A = [2, 1, 4];
const B = [9, 4, 2];
const dist = 3;
function addVectors(v1, v2) {
return v1.map((x, i) => x + v2[i]);
}
function scalarMultiply(v, c) {
return v.map(x => x*c);
}
function getLength(v) {
return Math.hypot(...v);
}
const vecB2A = addVectors(A, scalarMultiply(B, -1)); // Step 1
const normB2A = scalarMultiply(vecB2A, 1/getLength(vecB2A)); // Step 2
const distB2A = scalarMultiply(normB2A, dist); // Step 3
const C = addVectors(B, distB2A); // Final step
console.log(C);
Point C is equal to point B minus 'dist' times a unit vector whose direction is AB. So it is quite easy:
vector v from A to B is equal (xB-xA, yB-yA, zB-zA) / distance(AB)
Then C = B - d*v where d is the distance from B you want C to be.
I am trying to make my own raytracer in Javascript. So far the spheres work very well. I now want to expand the capabilities to include triangles, and from there I can go to squares, cubes and beyond. The code I have for finding intersections with triangles is as follows
function triangleIntersection(t, r) {
var norm = triangleNormal(t);
var dist = triangleDistance(t);
var a = Vector.dotProduct(r.vector, norm);
if (a === 0) {
return -1;
} else {
var b = Vector.dotProduct(norm, Vector.add(r.point, Vector.negative(Vector.multiply(norm, dist))));
var d = -1 * b / a;
var qx = Vector.scale(r.vector, d).x + r.point.x;
var qy = Vector.scale(r.vector, d).y + r.point.y;
var qz = Vector.scale(r.vector, d).z + r.point.z;
var q = new Vector(qx, qy, qz);
var ca = Vector.subtract(t.points[2], t.points[0]);
var qa = Vector.subtract(q, t.points[0]);
var t1 = Vector.dotProduct(Vector.crossProduct(ca, qa), norm);
var bc = Vector.subtract(t.points[1], t.points[2]);
var qc = Vector.subtract(q, t.points[2]);
var t2 = Vector.dotProduct(Vector.crossProduct(bc, qc), norm);
var ab = Vector.subtract(t.points[0], t.points[1]);
var qb = Vector.subtract(q, t.points[1]);
var t3 = Vector.dotProduct(Vector.crossProduct(ab, qb), norm);
if ((t1 >= 0) && (t2 >= 0) && (t3 >= 0)) {
return 1 * b / a;
} else {
return -1;
}
}
}
Triangle objects have a point array (points[]) and item 0 is point A, item 1 is point B and item 2 is point C. The parameter t is one of these triangles. The parameter r is a ray object, with properties point which is the origin, and vector, which is the direction.
I also have these functions for finding normal and distance of a triangle.
function triangleNormal(s) {
var ca = Vector.subtract(s.points[2], s.points[0]);
var ba = Vector.subtract(s.points[1], s.points[0]);
var norm = Vector.unitVector(Vector.crossProduct(ca, ba));
return norm;
}
function triangleDistance(t) {
return Vector.dotProduct(triangleNormal(t, 0), t.points[0]);
}
When I render my scene the triangle I use in my scene is red colored. No matter how far I move my camera back the triangle fills the whole scene red. I do not know why this happens.
An important error in your plane intersection code is this comparison operation:
a === 0
There are two things wrong with it:
For ray tracing you want the ray to hit planes in front of its source, not behind it, so you need a < 0.
Even if you did want the ray to hit planes behind it, you must never do equality operations between floating point values, because floating point calculations are not exact. (You must do something like abs(a) < 1e-6f or some small value)
I have these conditions:
Point A, B and C are created.
Point A to Point B will create a line.
Parallel lines are created depending on the position of Point A, B and C (refer to the figure below).
If you move Point A, the lines will also move but Points B and C remain on their respective positions.
They can be moved at any position.
What I want is to create this:
Consider the figure 1 below (I'm sure you already know this basic 2D geometry but without this my answer would be incomplete):
Coordinates for points A and B are known and we want to find function that can be used to calculate y-coordinate whenever x-coordinate is known, in such a way that point (x,y) lies on the line. From the figure 1:
k = tan(alpha) = (y2 - y1) / (x2 - x1) - the slope of line
Putting coordinates of either A or B into well known line equation y = kx + m we can calculate m to make the equation complete. Having this equation, for any coordinate x we can calculate coordinate y using this equation. The good thing about it is that it doesn't depend on the position of point A and B or slop (angle) of the line - you will have to take care of the special case of vertical/horizontal lines where y/x will be infinite according to this equation.
Back to your question. Take a look at figure 2 below:
We have very similar situation here, there is a line between points A and C, and line between points B and D. I assumed that point A is at the center of the coordinate system! This generally won't be the case but this is really not a restriction as you can perform translation that will put A in the center, then make your calculations and then translate everything back.
Using the technique described at the beginning, you can find the line equation for the line that connects A and C points and for the line that connects B and D points (D coordinates can be easily calculated). Let's assume you did just that:
A-C: y = k1*x (m is zero as line goes through the center A)
B-D: y = k2*x + m2 (m2 is not zero as line doesn't go through the center A)
Finally the algorithm you could use to draw these parallel lines:
Choose a space with which you want to take x-coordinates between x1 and x3. For example, if you want 4 lines this space will be s = (x3 - x1) / 4 and so on.
Set value x_start = x1 + s (and later x_start += s), and calculate y-coordinate using the equation for A-C line y_end = k1*x_start. This will give you point that lies on the line A-C and this is the start of your line.
Similarly, calculate the end point that will lie on the line that connects B and D:
x_end = x2 + s (later x_end += s)
y_end = k2*x_end + m2
Using these equations calculate points (x_start,y_start) and (x_end,y_end) for all lines that you want to draw (there is |x3 - x1| / desired_num_of_lines of them).
You'll have to form new equations each time point A moves out of the current A-C line, as every time this happens the slop of the A-C (and B-D) line changes invalidating the current equations.
I'm not going to write any JS code, but having the logic behind the possible solution should give you more then enough information to move forward with you own implementation.
Always think, when using the Context2D, that using the transforms (translate, rotate, scale), can spare you some math.
With those transforms you can think of your drawing like you would do with a pen : where do you put the pen ? where do you move next (translate) ? do you rotate the page ? do you get closer or further from the page (scale) ?
Here you want to start at A, then move along AC.
Each step on the way, you want to draw the AB vector.
Here's how you could code it, as you see, just simple vector math here, so if you remember that AB vector has (B.x-A.x, B.y-A.y) coordinates, you know most of the math you'll need.
// boilerPlate
var ctx = document.getElementById('cv').getContext('2d');
ctx.strokeStyle = '#000';
// params : Points : {x,y}
var A, B, C;
A = { x: 20, y: 170 };
B = { x: 80, y: 60 };
C = { x: 140, y: 120 };
// param : number of lines to draw.
var stepCount = 5;
// ----------
// compute AB vector = B - A
var AB = { x: B.x - A.x, y: B.y - A.y };
// compute step : ( C - A ) / stepCount
var step = { x: (C.x - A.x) / stepCount, y: (C.y - A.y) / stepCount };
// -- start draw
ctx.save();
// Move pen to A
ctx.translate(A.x, A.y);
for (var i = 0; i <= stepCount; i++) {
// draw AB vector at current position
ctx.lineWidth= ( i==0 || i==stepCount ) ? 2 : 1 ;
drawVector(AB);
// move pen one step further
ctx.translate(step.x, step.y);
}
ctx.restore();
// --
// draws vector V at the current origin ((0,0)) of the context.
function drawVector(V) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(V.x, V.y);
ctx.stroke();
}
// ----------
// legend
drawPoint(A, 'A');
drawPoint(B, 'B');
drawPoint(C, 'C');
function drawPoint(P, name) {
ctx.beginPath();
ctx.arc(P.x, P.y, 3, 0, 6.28);
ctx.fill();
ctx.strokeText(name, P.x + 6, P.y + 6);
}
<canvas id='cv' width=300 height=200></canvas>
Džanan has it right, and in simple terms, you need the X and Y offsets between the starting points of the two lines, i'e' point A and point C. When drawing the line that starts at C, and assuming that it ends at D, you will need to add the same X and Y offsets, e.g., if you draw AB with starting coordinates (100, 150) as follows:
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.stroke();
And if C has to start at (150, 200), the offset here would be
X: 50, Y:50
so CD would be drawn as
context.beginPath();
context.moveTo(150, 200);
context.lineTo((450+50), (50+50));
context.stroke();
Now this assumes that the length of both the lines are going to be same. If they are to differ, the equation will be slightly more complex.
Given an array of circles (x,y,r values), I want to place a new point, such that it has a fixed/known Y-coordinate (shown as the horizontal line), and is as close as possible to the center BUT not within any of the existing circles. In the example images, the point in red would be the result.
Circles have a known radius and Y-axis attribute, so easy to calculate the points where they intersect the horizontal line at the known Y value. Efficiency is important, I don't want to have to try a bunch of X coords and test them all against each item in the circles array. Is there a way to work out this optimal X coordinate mathematically? Any help greatly appreciated. By the way, I'm writing it in javascript using the Raphael.js library (because its the only one that supports IE8) - but this is more of a logic problem so the language doesn't really matter.
I'd approach your problem as follows:
Initialize a set of intervals S, sorted by the X coordinate of the interval, to the empty set
For each circle c, calculate the interval of intersection Ic of c with with the X axis. If c does not intersect, go on to the next circle. Otherwise, test whether Ic overlaps with any interval(s) in S (this is quick because S is sorted); if so, remove all intersecting intervals from S, collapse Ic and all removed intervals into a new interval I'c and add I'c to S. If there are no intersections, add Ic to S.
Check whether any interval in S includes the center (again, fast because S is sorted). If so, select the interval endpoint closest to the center; if not, select the center as the closest point.
Basically the equation of a circle is (x - cx)2 + (y - cy)2 = r2. Therefore you can easily find the intersection points between the circle and X axis by substituting y with 0. After that you just have a simple quadratic equation to solve: x2 - 2cxx + cx2 + cy2 - r2 = 0 . For it you have 3 possible outcomes:
No intersection - the determinant will be irrational number (NaN in JavaScript), ignore this result;
One intersection - both solutions match, use [value, value];
Two intersections - both solutions are different, use [value1, value2].
Sort the newly calculated intersection intervals, than try merge them where it is possible. However take in mind that in every program language there approximation, therefore you need to define delta value for your dot approximation and take it into consideration when merging the intervals.
When the intervals are merged you can generate your x coordinates by subtracting/adding the same delta value to the beginning/end of every interval. And lastly from all points, the one closest to zero is your answer.
Here is an example with O(n log n) complexity that is oriented rather towards readability. I've used 1*10-10 for delta :
var circles = [
{x:0, y:0, r:1},
{x:2.5, y:0, r:1},
{x:-1, y:0.5, r:1},
{x:2, y:-0.5, r:1},
{x:-2, y:0, r:1},
{x:10, y:10, r:1}
];
console.log(getClosestPoint(circles, 1e-10));
function getClosestPoint(circles, delta)
{
var intervals = [],
len = circles.length,
i, result;
for (i = 0; i < len; i++)
{
result = getXIntersection(circles[i])
if (result)
{
intervals.push(result);
}
}
intervals = intervals.sort(function(a, b){
return a.from - b.from;
});
if (intervals.length <= 0) return 0;
intervals = mergeIntervals(intervals, delta);
var points = getClosestPoints(intervals, delta);
points = points.sort(function(a, b){
return Math.abs(a) - Math.abs(b);
});
return points[0];
}
function getXIntersection(circle)
{
var d = Math.sqrt(circle.r * circle.r - circle.y * circle.y);
return isNaN(d) ? null : {from: circle.x - d, to: circle.x + d};
}
function mergeIntervals(intervals, delta)
{
var curr = intervals[0],
result = [],
len = intervals.length, i;
for (i = 1 ; i < len ; i++)
{
if (intervals[i].from <= curr.to + delta)
{
curr.to = Math.max(curr.to, intervals[i].to);
} else {
result.push(curr);
curr = intervals[i];
}
}
result.push(curr);
return result;
}
function getClosestPoints(intervals, delta)
{
var result = [],
len = intervals.length, i;
for (i = 0 ; i < len ; i++)
{
result.push( intervals[i].from - delta );
result.push( intervals[i].to + delta );
}
return result;
}
create the intersect_segments array (normalizing at x=0 y=0)
sort intersectsegments by upperlimit and remove those with upperlimit<0
initialize point1 = 0 and segment = 0
loop while point1 is inside intersectsegment[segment]
4.1. increment point1 by uppper limit of intersectsegment[segment]
4.2. increment segment
sort intersectsegments by lowerlimit and remove those with loerlimit>0
initialize point2 = 0 and segment = 0
loop while point2 is inside intersectsegments[segment]
7.1. decrement point2 by lower limit of segment
7.2. decrement segment
the point is minimum absolute value of p1 and p2
I'm well acquainted with basic math and know the equations, but when I try to implement them in canvas they fail.
Here's the relevant code:
function distance(one,two){
var a, b
if(one[0] > one[1]){
a = one[1] - one[0]
}else{
a = one[0] - one[1]
}
if(two[0] > two[1]){
b = two[1] - two[0]
}else{
b = two[0] - two[1]
}
var c = a^2 + b^2;
return Math.sqrt(c);
}
radius = distance([centerX,e.clientX], [centerY,e.clientY])
context.fillCircle(centerX, centerY, radius, "white");
Here's the code in action
As you can see, the circle is far too short and I don't know why... when I remove the sqrt, it becomes out of whack very quickly.
^ does not do what you think it does.
js> 8^2
10
js> Math.pow(8,2)
64