Need help with a math issue:
i need to get the true angle from 0 degrees using x and y cordinates
im using this at the moment:
Math.atan((x2-x1)/(y1-y2))/(Math.PI/180)
but /(Math.PI/180) limits results from -90 to 90
i need 0-360
note: I'm using the angle to indicate direction:
0=up
90=right
135=45 degree right+down
180=down
270=left
etc
The atan function only gives half the unit circle between -pi/2 and +pi/2 (0 on x axis), there is another library function that can give the whole unit circle between -pi and + pi, atan2
I would think you are better of using atan2 to get the right quadrant rather than branching yourself, then just scale as you have been, something like
Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI + 180
The multiply by 180 over pi is just the scale from radians to degrees as in the question (but with the division by a division simplified), the +180 makes sure its always positive i.e. 0-360 deg rather than -180 to 180 deg
Math.atan limits you to the two rightmost quadrants on the unit circle. To get the full 0-360 degrees:
if x < 0 add 180 to the angle
else if y < 0 add 360 to the angle.
Your coordinate system is rotated and inverted compared to mine (and compared to convention). Positive x is to the right, positive y is up. 0 degrees is to the right (x>0, y=0, 90 degrees is up (x=0,y>0) 135 degrees is up and to the left (y>0, x=-y), etc. Where are your x- and y-axes pointing?
The answers of #jilles de wit and #jk. led me on the right path but for some reason did not provide the right solution for my problem that i think is very similar to the original question.
I wanted to get up = 0°, right = 90°, down = 180°, left = 270° as in aeronautical navigation systems.
Presuming the question was referring to canvas drawing i reached this solution:
I first translated the canvas origin using ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2). I also halved e.offsetX and e.offsedY i got from a mouse event on the canvas to get x and y with the same coordinate system as the canvas.
let radianAngle = Math.atan2(y, x); // x has the range [-canvas.width/2 ... +canvas.width/2], y is similar
let northUpAngle = radianAngle * 180 / PI + 90; // convert to degrees and add 90 to shift the angle counterclockwise from it's default "left" = 0°
if (x < 0 && y < 0) { // check for the top left quadrant
northUpAngle += 360; // add 360 to convert the range of the quadrant from [-90...0] to [270...360] (actual ranges may vary due to the way atan2 handles quadrant boundaries)
}
northUpAngle.toFixed(2) // to avoid getting 360° near the "up" position
There might be a more concise solution using the modulo operation but i could't find it.
Also note:
if (y1==y2) {
if (x1>x2)
angle = 90;
else if (x1<x2)
angle = 270;
else
angle = 0;
}
This should do the trick:
If y2
If < 0, add 360.
Examples:
(x1,y1) = 0
(x2,y2) = (-1,1), atan() = -45, [add 360], 270
(x2,y2) = (1,1), atan() = 45
(x2,y2) = (1,-1), atan() = -45, [add 180], 135
(x2 ,y2) = (-1,-1), atan() = 45, [add 180], 225
angle = Math.atan(this.k) * 180 / Math.PI;
angle = 180 - (angle < 0 ? 180 + angle : angle);
angle = p2.Y > p1.Y || (p2.Y == p1.Y && p2.X > p1.X) ? 180 + angle : angle;
Here's two solutions, one with Math.atan (which takes a FRACTION opposite/adjacent) and one with Math.atan2 (which takes TWO ARGUMENTS)
solutions are written in ES6 (ES2015+) syntax, ironically, because the question predates this javascript.
note that 'to the right' is 0° (=0 Radians); up is 90° (= PI/2); left is 180° (PI), and down is 270° (PI*1.5)
angleGivenCoords(coord1,coord2) {
// given two coords {x,y}, calculate the angle in radians with
// right being 0° (0 radians)
if (coord1.x === coord2.x) return (coord1.y > coord2.y ? Math.PI * 0.5 : Math.PI * 1.5)
if (coord1.y === coord2.y) return (coord1.x > coord2.x ? Math.PI : 0 )
let opposite = coord2.x - coord1.x
let adjacent = coord1.y - coord2.y
let adjustor = ((coord2.x < coord1.x && coord2.y < coord1.y) || (coord2.x < coord1.x && coord2.y > coord1.y)) ? Math.PI : 0
let res = Math.atan(opposite/adjacent) + adjustor
if (res < 0) { res = res + Math.PI*2 }
return res ;
}
now with Math.atan2. notice that with this solution the guard clauses at the top (coord1.x === coord2.x, (coord1.y === coord2.y)) are unneeded
angleGivenCoords(coord1,coord2) {
// given two coords {x,y}, calculate the angle in radians with
// left being 0° (0 radians)
let opposite = coord2.x - coord1.x
let adjacent = coord1.y - coord2.y
let res = Math.atan2(adjacent, opposite)
if (res < 0) { res = res + Math.PI*2 }
return res ;
}
(I tried to keep the 'opposite' and 'adjacent' variable names in deference to the Trigonometry)
please note that here is my test suite written in Jest. Also note my function above returns radians and my code (not shown here) has a simple Trig.degreesToRadians() as you would expect
it('for right 0°', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 600, y: 500}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(0))
})
it('for up-right 45°', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 600, y: 400}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(45))
})
it('for 90° up', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 500, y: 400}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(90))
})
it('for 135° up to left', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 400, y: 400}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(135))
})
it('for 180° to left', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 400, y: 500}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(180))
})
it('for 225° to to bottom left', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 400, y: 600}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(225))
})
it('for 270° to the bottom', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 500, y: 600}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(270))
})
it('for 315° to the bottom', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 600, y: 600}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(315))
})
For 0=up,90=right,180=down,270=left etc (x=x2-x1,y=y2-y1)
you can use the formula:
f(x,y)=180-90*(1+sign(y))* (1-sign(x^2))-45*(2+sign(y))*sign(x)
-(180/pi())*sign(x*y)*atan((abs(y)-abs(x))/(abs(y)+abs(x)))
so with a single decesion (i hope this ugly basiclike notation explains it):
IF x = 0 THEN
360degree = 270 - (SIGN(x) + 1) * 90
ELSE
360degree = MOD(180 + (SIGN(y) + 1) * 90 + ATAN(x/y) , 360)
ENDIF
to draw a full circle from north 0degree to 360degree clockwise:
x=SIN(0to360) y=COS(0to360)
cheers, Lev
function angle(x1,y1,x2,y2)
{
eangle = Math.atan((x2-x1)/(y1-y2))/(Math.PI/180)
if ( angle > 0 )
{
if (y1 < y2)
return angle;
else
return 180 + angle;
} else {
if (x1 < x2)
return 180 + angle;
else
return 360 + angle;
}
}
Related
I'm trying to spawn enemies just outside the bounds of a rectangle. Here's a picture:
That is, the grey area is the playing area that the user can see, and the green is outside the rendering bounds. I'm looking for a way to calculate a spawn position in this green area.
I have a tentative solution, but it's pretty long and involves a bunch of if statements. Is there a more efficient or elegant way of calculating this?
function calcEnemySpawnPos(r) {
const roll = Math.random();
const left = -r;
const right = canvas.width + r;
const top = -r;
const bottom = canvas.height + r;
if (roll <= 0.25) {
return { x: left, y: getRandomInt(top, bottom) };
} else if (roll <= 0.5) {
return { x: right, y: getRandomInt(top, bottom) };
} else if (roll < 0.75) {
return { x: getRandomInt(left, right), y: top };
} else {
return { x: getRandomInt(left, right), y: bottom };
}
}
I have a slight improvement, but still not amazingly elegant. This is pseudocode since I'm not sure of the js syntax:
const rollLeft = Math.random() - 0.5;
const rollTop = Math.random() - 0.5;
if (rollLeft > 0){
x = getRandomInt(-r, 0)
} else {
x = getRandomInt(canvas.width, canvas.width + r)
}
if (rollRight > 0){
y = getRandomInt(-r, 0)
} else {
y = getRandomInt(canvas.height, canvas.height + r)
}
return {x, y}
There are 200,000 possible positions. You can generate just one random number and map it to a valid coordinate. You can specify four valid ranges, defined by top-left and bottom-right corners, then use your random number to get a range (weighted by area) and then convert the number to a point in that range.
function startPos(ranges, totalSize) {
let n = Math.trunc(Math.random() * totalSize);
const {x: j, y: k, w} = ranges.find(r => n < r.size || void(n -= r.size));
const x = n % w, y = (n - x) / w; // remainder/quotient of dividing by width
return [x + j, y + k]; // translate to start of range
}
[x, y] = startPos([
{x: -100, y: -100, w: 600, h: 100, size: 600 * 100},
{x: 500, y: -100, w: 100, h: 400, size: 100 * 400},
{x: 0, y: 300, w: 600, h: 100, size: 600 * 100},
{x: -100, y: 0, w: 100, h: 400, size: 100 * 400},
], 200_000);
The ranges.find(...) predicate is a little hard to read. Could also be written like this:
ranges.find(({size}) => {
if (n < size) return true;
else n -= size;
});
Note that this algorithm gives every pixel equal probability of being the spawn point, in contrast with your solution where each quadrant has equal probability of containing the spawn point, so pixels on the shorter sides have higher probability than pixels on the longer sides.
I'm having some issues calculating the corners of a rotated rectangle within a rotated container with both having offset x/y co-ords.
The pivot is off but I'm not sure of the solution. The following scenarios work:
(x, y, rotation)
image = 0, 0, 45
container = 100, 100, 45
image = 200, 0, 45
container = 100, 100, 0
however setting the rotation of the container, and the image co-ords messes up the pivot e.g.
image = 200, 0, 45
container = 100, 100, 45
Below is the code for calculating the corners of the image in global co-ordinate space:
public get corners() {
const worldData = this.worldData;
//Get angle of object in radians;
const radAngle = worldData.rotation * Math.PI / 180;
const pivotX = worldData.pivotX;
const pivotY = worldData.pivotY;
const width = this.sourceWidth * worldData.scaleX;
const height = this.sourceHeight * worldData.scaleY;
const x = worldData.x;//this.x;
const y = worldData.y;//this.y;
//Get the corners
const c1 = this.getCorner(pivotX, pivotY, x, y, radAngle);
const c2 = this.getCorner(pivotX, pivotY, x + width, y, radAngle);
const c3 = this.getCorner(pivotX, pivotY, x + width, y + height, radAngle);
const c4 = this.getCorner(pivotX, pivotY, x, y + height, radAngle);
return {c1, c2, c3, c4};
}
public get worldData() {
let x = this.x;
let y = this.y;
let pivotX = this.x;
let pivotY = this.y;
let rotation = this.rotation;
let scaleX = this.scaleX;
let scaleY = this.scaleY;
let parent = this.parent;
while(parent) {
x += parent.x;
y += parent.y;
pivotX += parent.x;
pivotY += parent.y;
rotation += parent.rotation;
scaleX *= parent.scaleX;
scaleY *= parent.scaleY;
parent = parent.parent;
}
return {x, y, scaleX, scaleY, rotation, pivotX, pivotY}
}
protected getCorner(pivotX:number, pivotY:number, cornerX:number, cornerY:number, angle:number) {
let x, y, distance, diffX, diffY;
/// get distance from center to point
diffX = cornerX - pivotX;
diffY = cornerY - pivotY;
distance = Math.sqrt(diffX * diffX + diffY * diffY);
/// find angle from pivot to corner
angle += Math.atan2(diffY, diffX);
/// get new x and y and round it off to integer
x = pivotX + distance * Math.cos(angle);
y = pivotY + distance * Math.sin(angle);
return {x, y};
}
Let's suppose that the scenario is as follows:
where the lower left corner of the image (solid line) has coordinates (x_i, y_i) and the lower left corner of the container (dashed line) has coordinates (X_c, Y_c). Moreover, the image (of width w and height h) is rotated counter-clockwise by angle beta with respect to the laboratory frame, while the container itself is rotated (also counter-clockwise) by angle alpha.
Now, let's focus for example on the upper-right corner P. With respect to the laboratory frame (global canvas), its coordinates can be expressed as:
R(beta) . ( w, h ) + ( x_i, y_i )
where . denotes matrix multiplication, and R is a counter-clockwise rotation matrix
R(beta) = [ cos(beta) -sin(beta) ]
[ sin(beta) cos(beta) ]
Now, we need to transform this into a coordinate frame with respect to the container. Formally, this means that we need first to subtract the offset and then to rotate by -alpha (or alpha clock-wise). Thus with everything together:
R(-alpha).( R(beta) . (w, h) + (x_i, y_i) - (X_c, Y_c) )
The other corners can be handled similarly, just by replacing (w, h) with the proper coordinates...
In terms of code, one might implement these formulae as:
//counter-clock-wise rotation by given angle in degrees
function rotateCCWBy(angle, {x, y}) {
const angle_rad = angle * Math.PI / 180;
const cos_a = Math.cos(angle_rad),
sin_a = Math.sin(angle_rad);
return {
x: cos_a * x - sin_a * y,
y: sin_a * x + cos_a * y
};
}
//shift by a multiple fac of an offset {xref, yref}
function offsetBy(fac, {x:xref, y:yref}, {x, y}) {
return {
x: fac*xref + x,
y: fac*yref + y
};
}
const image = {
coords: {x: 200, y: 0}, //lab-frame coordinates
angle: 45, //lab-frame rotation angle
width: 50,
height: 10
};
const container = {
coords: {x: 100, y: 100}, //lab-frame coordinates
angle: 45 //lab-frame rotation angle
};
//calculate the coordinates of the image's top-right corner
//with respect to the container
const corner = rotateCCWBy(-container.angle,
offsetBy(
-1, container.coords,
offsetBy(
+1, image.coords,
rotateCCWBy(image.angle,
{x: image.width, y: image.height}
)
)
)
);
console.log(corner);
EDIT:
In case the y-axis is supposed to point "downwards", the formulas above work as well, one just needs to interpret the angles as clock-wise instead of counter-clockwise (so in principle the function rotateCCWBy should be renamed to rotateCWBy). As an example, let's consider this scenario:
Here, the top-left corner of the container is located at position (2,1) and the container itself is rotated by 15 degrees. The image (black rectangle) of width 4 and height 2 is rotated by 30 degrees and its top-left corner is located at position (3, 3). Now, we want to calculate the coordinates (x, y) of point P with respect to the container.
Using:
const image = {
coords: {x: 3, y: 3}, //lab-frame coordinates
angle: 30, //lab-frame rotation angle
width: 4,
height: 2
};
const container = {
coords: {x: 2, y: 1}, //lab-frame coordinates
angle: 15 //lab-frame rotation angle
};
//calculate the coordinates of the image's top-left corner
//with respect to the container
const corner = rotateCCWBy(-container.angle,
offsetBy(
-1, container.coords,
offsetBy(
+1, image.coords,
rotateCCWBy(image.angle,
{x: image.width, y: image.height}
)
)
)
);
console.log(corner);
yields
{ x: 4.8296291314453415, y: 4.640160440463835 }
which can be (approximately) visually verified from the attached figure.
EDIT2:
After additional clarification, the coordinates of the image are not supposed to be "lab-frame" (i.e., with respect to the canvas), but with respect to the already rotated container. Thus the transformation needs to be adapted as:
const corner =
offsetBy(
+1, container.coords,
rotateCCWBy(container.angle,
offsetBy(
+1, image.coords,
rotateCCWBy(image.angle,
{x: image.width, y: image.height}
)
)
)
);
function rotateCCWBy(angle, {x, y}) {
const angle_rad = angle * Math.PI / 180;
const cos_a = Math.cos(angle_rad),
sin_a = Math.sin(angle_rad);
return {
x: cos_a * x - sin_a * y,
y: sin_a * x + cos_a * y
};
}
I have a set of points introduced into a canvas:
My canvas with set of points
I have to apply this algorithm on:
Algo NoObtuse and example of graph produced by this algo
My problem is to find, starting from the rightmost point, the following point in the counter-clockwise order (point 2 in algo).
How, then, can we find the following point in this direction each time starting from a point?
EDIT: -> Result of the code by Blindman67
//First points (before sort and anti-clockwise)
//(6) [Point, Point, Point, Point, Point, Point]
0: Point {x: 458, y: 249, col: "red"}
1: Point {x: 333, y: 40, col: "red"}
2: Point {x: 138, y: 111, col: "red"}
3: Point {x: 336, y: 209, col: "red"}
4: Point {x: 237, y: 251, col: "red"}
5: Point {x: 60, y: 351, col: "red"}
//Points after sort and anti-clockwise
//(6) [Point, Point, Point, Point, Point, Point]
0: Point {x: 336, y: 209, col: "red", angle: 6.456745983859364}
1: Point {x: 333, y: 40, col: "red", angle: 5.156558533568968}
2: Point {x: 138, y: 111, col: "red", angle: 3.75120843247896}
3: Point {x: 60, y: 351, col: "red", angle: 2.4782921522301162}
4: Point {x: 237, y: 251, col: "red", angle: 1.9481922940313214}
5: Point {x: 458, y: 249, col: "red", angle: 0.26263427391514854}
Sorting points in rotational order
To sort points in some direction starting at the right most and using the spatial center as a reference point.
// Array of points;
const points = [{x:?,y:?},{x:?,y:?},{x:?,y:?},...?];
// Find min max to get center
// Sort from top to bottom
points.sort((a,b)=>a.y - b.y);
// Get center y
const cy = (points[0].y + points[points.length -1].y) / 2;
// Sort from right to left
points.sort((a,b)=>b.x - a.x);
// Get center x
const cx = (points[0].x + points[points.length -1].x) / 2;
// Center point
const center = {x:cx,y:cy};
// Pre calculate the angles as it will be slow in the sort
// As the points are sorted from right to left the first point
// is the rightmost
// Starting angle used to reference other angles
var startAng;
points.forEach(point => {
var ang = Math.atan2(point.y - center.y,point.x - center.x);
if(!startAng){ startAng = ang }
else {
if(ang < startAng){ // ensure that all points are clockwise of the start point
ang += Math.PI * 2;
}
}
point.angle = ang; // add the angle to the point
});
// Sort clockwise;
points.sort((a,b)=> a.angle - b.angle);
UPDATE correction
// ****************************************************
// UPDATE the following code is incorrect
// ****************************************************
// Sort anti clockwise;
// points.sort((a,b)=> b.angle - a.angle);
// ****************************************************
//=====================================================
// the correct way to sort anticlockwise
//=====================================================
// first sort clockwise
points.sort((a,b)=> a.angle - b.angle);
// then reverse the order
const ccwPoints = points.reverse();
// move the last point back to the start
ccwPoints.unshift(ccwPoints.pop());
Example
Click canvas to rerun on a new set of random points sorted in counter clockwise order.
//.......................................................
// support code not part of the answer
const doFor = (count, cb) => { var i = 0; while (i < count && cb(i++) !== true); }; // the ; after while loop is important don't remove
const setOf = (count, cb = (i)=>i) => {var a = [],i = 0; while (i < count) { a.push(cb(i ++)) } return a };
const eachOf = (array, cb) => { var i = 0; const len = array.length; while (i < len && cb(array[i], i++, len) !== true ); };
const randI = (min, max = min + (min = 0)) => (Math.random() * (max - min) + min) | 0;
const rand = (min = 1, max = min + (min = 0)) => Math.random() * (max - min) + min;
//.......................................................
// set up canvas and context
const ctx = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 250;
ctx.font = "12px airal";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// create random points and then sort them in counterclockwise order
// starting at the right most
function doIt() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
function drawPoints() {
eachOf(points, (point, i) => {
ctx.beginPath();
ctx.lineTo(center.x, center.y);
ctx.lineTo(point.x, point.y);
ctx.stroke();
ctx.fillStyle = "white"
ctx.fillText(i, point.x-2, point.y);
ctx.fillText(i, point.x+2, point.y);
ctx.fillText(i, point.x, point.y-2);
ctx.fillText(i, point.x, point.y+2);
ctx.fillStyle = "black"
ctx.fillText(i, point.x, point.y);
})
}
// Array of points;
var points = setOf(8, () => ({
x : rand(20, canvas.width - 40),
y : rand(20, canvas.height - 40),
angle : 0
}));
// Find min max to get center
// Sort from top to bottom
points.sort((a, b) => a.y - b.y);
// Get center y
const cy = (points[0].y + points[points.length - 1].y) / 2;
// Sort from right to left
points.sort((a, b) => b.x - a.x);
// Get center x
const cx = (points[0].x + points[points.length - 1].x) / 2;
// Center point
var center = {
x : cx,
y : cy
};
// Pre calculate the angles as it will be slow in the sort
// As the points are sorted from right to left the first point
// is the rightmost
// Starting angle used to reference other angles
var startAng;
points.forEach(point => {
var ang = Math.atan2(point.y - center.y, point.x - center.x);
if (!startAng) {
startAng = ang
} else {
if (ang < startAng) { // ensure that all points are clockwise of the start point
ang += Math.PI * 2;
}
}
point.angle = ang; // add the angle to the point
});
// first sort clockwise
points.sort((a, b) => a.angle - b.angle);
// then reverse the order
const ccwPoints = points.reverse();
// move the last point back to the start
ccwPoints.unshift(ccwPoints.pop());
drawPoints();
}
doIt()
canvas.onclick = doIt;
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas>
I've been working with phoria.js and have really got a kick out of it. I have a player avatar model and have been using a first person camera. The phoria.js camera is positioned with two coordinates of 3D space: the first: the camera's actual position in the scene graph, the second: the point which the camera looks at. I've been using two angles: angleX and angleY defined in degrees (I know that radians are better) for easy thinking as well as a constant distance of 10 units.
The code for the fp camera goes like
{within an object definition}
setLookAtByAngle: function(camera, angleX, angleY) {
lookAt = {x: camera.position.x, y: camera.position.y, z: camera.position.z};
extent = 10;
lookAt.x += Math.tan(angleX * (Math.PI / 180)) * extent;
lookAt.y += Math.tan(angleY * (Math.PI / 180)) * extent;
lookAt.z += extent;
camera.lookat = lookAt;
},
getAngleByLookAt: function(camera) {
xPlanarLookAt = {x: camera.lookat.x, y: camera.position.y, z: camera.lookat.z};
yPlanarLookAt = {x: camera.position.x, y: camera.lookat.y, z: camera.lookat.z};
hypotX = getDistanceBetweenTwoPoints(camera.position, xPlanarLookAt);
oppX = Math.abs(camera.lookat.x - camera.position.x);
angleX = Math.asin(oppX / hypotX) * (180/Math.PI);
hypotY = getDistanceBetweenTwoPoints(camera.position, yPlanarLookAt);
oppY = Math.abs(camera.lookat.y - camera.position.y);
angleY = Math.asin(oppY / hypotY) * (180/Math.PI);
return {x: angleX, y: angleY};
},
{continue object definition}
. What I have so far of the third person camera uses my own SpaceUtils. It takes the observee's position1 and the inverted x[2],y[3] angles and calls SpaceUtils.translateByAngle([1], [2], [3], 10) with 10 as a distance constant to be modified later along with FOV when I add zooming functionality.
SpaceUtils looks like
SpaceUtils = {
degSin: function(theta) {
return Math.sin(theta * (Math.PI / 180));
},
degCos: function(theta) {
return Math.cos(theta * (Math.PI / 180));
},
degTan: function(theta) {
return Math.tan(theta * (Math.PI / 180));
},
translateByAngle: function(pos, angleX, angleY, distance) {
dest = {x: pos.x, y: pos.y, z: pos.z};
dest.z += distance * SpaceUtils.degSin(angleY);
hyp = distance * SpaceUtils.degCos(angleY);
dest.x += hyp * SpaceUtils.degCos(angleX);
dest.y += hyp * SpaceUtils.degSin(angleX);
return dest;
}
};
My calculator has Pol() and Rec() functions which do the job in 2D.
I supposed I just need the correct one of these for 3D, so I began implementing. It all works in the four x > 0 quadrants, but pushes the camera down as I transition across x == 0 where y > 0 (y is up-down axis) so that when I start with x > 0 and y > 0, I end up with x < 0 and y < 0 when y > 0 should be the case.
In short, I need a 3D Rec(r, theta) function ideally in degrees, but radians is fine if it's not purpose written.
I realise that this is a sizable ask, so I'll bounty 50 or 100 rep if it comes to that.
Phoria uses x as forward-backward, y as upward-downward, and z as sideways, while conventional mathematics swaps the purposes of y and z. You should to. That last function in SpaceUtils should go something more along the lines of the follwing.
translateByAngle: function(pos, angleX, angleY, distance) {
dest = {x: pos.x, y: pos.y, z: pos.z};
dest.y += distance * SpaceUtils.degSin(angleY);
hyp = distance * SpaceUtils.degCos(angleY);
dest.x += hyp * SpaceUtils.degSin(angleX);
dest.z += hyp * SpaceUtils.degCos(angleX);
return dest;
}
http://jsfiddle.net/psycketom/TUyJb/3/
I recently asked a question to calculate the endpoint of a line in canvas based on percentage: Canvas, drawing a line segment
Now, I am stuck with how to calculate X percentage in the line from startpoint to be used as actual startpoint.
In the fiddle above, I have tried to mirror endpoints:
growth = 0.2;
// calculate line endpoint
current.x = start.x + (target.x - start.x) * growth;
current.y = start.y + (target.y - start.y) * growth;
// calculate line startpoint
scurrent.x = start.x + (start.x - target.x) * growth;
scurrent.y = start.y + (start.y - target.y) * growth;
But that does not seem to do what I want it to do.
My real goal is to make a function, that would draw a line:
in boundaries of point x, to point y
with length of n
and starting at position z.
what you are stated as the endpoint is actually your "startpoint":
// calculate line "endpoint"
current.x = start.x + (target.x - start.x) * growth;
current.y = start.y + (target.y - start.y) * growth;
here is a function that returns a waypoint:
// start and end are points with .x and .y
// q is a number between 0.0 and 1.0
var waypoint = function (start, end, q) {
return {
x: start.x + (end.x - start.x) * q,
y: start.y + (end.y - start.y) * q
};
}
now you can calculate waypoint wherever you want:
var start = {x: 10, y: 20},
end = {x: 120, y: 70},
a = waypoint(start, end, 0.2),
b = waypoint(start, end, 0.8);
a and b will be point 20% in from either end of the original line.