I have this simple function to set an angle for a vector. It effectively gets the vector's current magnitude (length), calulates the angle and converts the angle from radians to degrees. Then I apply the angle to X and Y, lastly multiplying the vector by it's original magnitude.
this.setAngle = function(degree){
var l = this.length(); //magnitude of vector
var angle = degree*Math.PI/180; //degress converted to radians
this.x=Math.cos(angle);
this.y=Math.sin(angle);
this.multiply(l); //original magnitude
return;
}
However I am unsure how to obtain (get) an angle from a Vector. Below is my attempt:
this.getAngle = function(){
var angle = Math.atan(this.y/this.x); //radians
var degrees = angle/(180*Math.PI); //degrees
return Math.floor(degrees); //round number, avoid decimal fragments
}
This attempt doesn't return any value except 0 or -1.
Any suggestions?
Edit:
Correct method:
this.getAngle = function(){
var angle = Math.atan2(this.y, this.x);
var degrees = 180 * angle / Math.PI;
return (360 + Math.round(degrees)) % 360;
}
this.getAngle = function(){
var angle = Math.atan2(this.y, this.x); //radians
// you need to devide by PI, and MULTIPLY by 180:
var degrees = 180*angle/Math.PI; //degrees
return (360+Math.round(degrees))%360; //round number, avoid decimal fragments
}
Related
I have a 2D equirectangular depth map that is a 1024 x 512 array of floats, each ranging between 0 to 1. Here example (truncated to grayscale):
I want to convert it to a set of 3D points but I am having trouble finding the right formula to do so - it's sort of close - pseudocode here (using a vec3() library):
for(var y = 0; y < array_height; ++y) {
var lat = (y / array_height) * 180.0 - 90.0;
var rho = Math.cos(lat * Math.PI / 180.0);
for(var x = 0; x < array_width; ++x) {
var lng = (x / array_width) * 360.0 - 180.0;
var pos = new vec3();
pos.x = (r * Math.cos(lng * Math.PI / 180.0));
pos.y = (Math.sin(lat * Math.PI / 180.0));
pos.z = (r * Math.sin(lng * Math.PI / 180.0));
pos.norm();
var depth = parseFloat(depth[(y * array_width) + x] / 255);
pos.multiply(depth);
// at this point I can plot pos as an X, Y, Z point
}
}
What I end up with isn't quite right and I can't tell why not. I am certain the data is correct. Can anyone suggest what I am doing wrong.
Thank you.
Molly.
Well looks like the texture is half-sphere in spherical coordinates:
x axis is longitude angle a <0,180> [deg]
y axis is latitude angle b <-45,+45> [deg]
intensity is radius r <0,1> [-]
So for each pixel simply:
linearly convert x,y to a,b
in degrees:
a = x*180 / (width -1)
b = -45 + ( y* 90 / (height-1) )
or in radians:
a = x*M_PI / (width -1)
b = -0.25*M_PI + ( 0.5*y*M_PI / (height-1) )
apply spherical to cartesian conversion
x=r*cos(a)*cos(b);
y=r*sin(a)*cos(b);
z=r* sin(b);
Looks like you have wrongly coded this conversion as latitude angle should be in all x,y,z not just y !!! Also you should not normalize the resulting position that would corrupt the shape !!!
store point into point cloud.
When I put all together in VCL/C++ (sorry do not code in javascript):
List<double> pnt; // 3D point list x0,y0,z0,x1,y1,z1,...
void compute()
{
int x,y,xs,ys; // texture positiona and size
double a,b,r,da,db; // spherical positiona and angle steps
double xx,yy,zz; // 3D point
DWORD *p; // texture pixel access
// load and prepare BMP texture
Graphics::TBitmap *bmp=new Graphics::TBitmap;
bmp->LoadFromFile("map.bmp");
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
xs=bmp->Width;
ys=bmp->Height;
/*
// 360x180 deg
da=2.0*M_PI/double(xs-1);
db=1.0*M_PI/double(ys-1);
b=-0.5*M_PI;
*/
// 180x90 deg
da=1.0*M_PI/double(xs-1);
db=0.5*M_PI/double(ys-1);
b=-0.25*M_PI;
// proces all its pixels
pnt.num=0;
for ( y=0; y<ys; y++,b+=db)
for (p=(DWORD*)bmp->ScanLine[y],a=0.0,x=0; x<xs; x++,a+=da)
{
// pixel access
r=DWORD(p[x]&255); // obtain intensity from texture <0..255>
r/=255.0; // normalize to <0..1>
// convert to 3D
xx=r*cos(a)*cos(b);
yy=r*sin(a)*cos(b);
zz=r* sin(b);
// store to pointcloud
pnt.add(xx);
pnt.add(yy);
pnt.add(zz);
}
// clean up
delete bmp;
}
Here preview for 180x90 deg:
and preview for 360x180 deg:
Not sure which one is correct (as I do not have any context to your map) but the first option looks more correct to me ...
In case its the second just use different numbers (doubled) for the interpolation in bullet #1
Also if you want to remove the background just ignore r==1 pixels:
simply by testing the intensity to max value (before normalization) in my case by adding this line:
if (r==255) continue;
after this one
r=DWORD(p[x]&255);
In your case (you have <0..1> already) you should test r>=0.9999 or something like that instead.
A typical random walk does not care about direction changes. Each iteration generates a new direction. But if you imagine a point animated on a random walk, it will mostly jump around. So, the goal is to have a smoother curve depending on the previously calculated points.
How to adjust a random walk function to have smoother directional changes?
My main idea is to have a method that generates a new point with x and y coordinates, but looks after the previous step and decreases the size of the next step (const radius), if the rotation (directional change) comes closer to 180°.
Therefore, I am using D3js to randomly take a new step in any x and y direction. At the end I'll get an array of all past steps limited by the maximum amount of steps. The radius gives an orientation how long an average step should be taking on the x and y axis'.
const history = [];
const steps = 10;
const radius = 1;
let point = {
x: 0,
y: 0,
radians: null
};
for (let i = 0; i < steps; i++) {
console.log(point);
history.push(point);
const previousPoint = Object.assign({}, point);
point.x += radius * d3.randomNormal(0, 1)();
point.y += radius * d3.randomNormal(0, 1)();
point.radians = Math.atan2(
point.y - previousPoint.y,
point.x - previousPoint.x
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.js"></script>
Instead of using a coordinates based random walk, I decided to randomly generate each iteration a new radians. So the new and previous radians can be compared to each others to decide with velocity the new point will get. Depending on the minimum range between these radians' the volicity will be set. Afterwards a simple sine and cosine calculation have to be down to generate the coordinates of the new point.
At least I've achieved my final goal: https://beta.observablehq.com/#nextlevelshit/gentlemans-random-walk-part-3
const steps = 10;
const stepSize = 10;
let point = {
x: 0,
y: 0,
radians: randomRadians(),
velocity: 0
};
for (let i = 0; i < steps; i++) {
console.log(point);
const radians = randomRadians();
const velocity = 1 - minimumDifference(radians, point.radians) / Math.PI;
point = {
// Coordinates calculated depending on random radians and velocity
x: Math.sin(radians * Math.PI) * stepSize * velocity + point.x,
y: Math.cos(radians * Math.PI) * stepSize * velocity + point.y,
radians: radians, // Randomly generated radians
velocity: velocity // Velocity in comparison to previous point
};
}
function randomRadians() {
return randomFloat(- Math.PI, Math.PI);
}
function randomFloat(min, max) {
return Math.random() * (max - min) + min;
}
function minimumDifference(x, y) {
return Math.min((2 * Math.PI) - Math.abs(x - y), Math.abs(x - y));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.js"></script>
Math.acos(Math.cos(30)) will not return 30, but Math.acos(Math.cos(0.7)) will return 0.7... How can I do it correctly?
It is because the input/parameter to the cos function should be in radians not in degrees.
From MDN docs:
Parameters
x : A number given in unit of radians.
So, before making call to the function, convert the input to radians.
Make use of formula Radians = Degrees * ( Pi / 180)
Convert 30 degrees to radians
var radians = 30 * Math.PI / 180;
document.write(radians);
var result = Math.cos(radians);
var andBackToRadians = Math.acos(result);
document.write('<p>'+result+'</p>');
document.write('<p>' + andBackToRadians + '</p>');
So on my canvas I have a large ellipse, and when the user clicks on the canvas a small ellipse should be created on the edge of the large ellipse in the direction of where the click was. The angles are off, and I'm not very confident in the calculations, plus I think the fact that this coordinate system has y increasing when it goes down is screwing it up. Can anyone help me get the desired result?
HTML
<html>
<head>
<script src='processing-1.4.1.min.js'></script>
<script src='jquery-1.9.1.min.js'></script>
</head>
<body>
<canvas id="gamecanvas" data-processing-sources="canvas.pde"></canvas>
</body>
<script>
var gamecanvas = document.getElementById("gamecanvas");
var projectiles = [];
$("#gamecanvas").click(function(e) {
var x = e.clientX - gamecanvas.offsetLeft;
var y = e.clientY - gamecanvas.offsetTop;
var pindex = projectiles.length;
projectiles[pindex] = [];
projectiles[pindex]['angle'] = Math.atan2(y - 200, x - 300) * 180 / Math.PI;
projectiles[pindex]['x'] = 300 + 10 * Math.cos(projectiles[pindex]['angle']);
projectiles[pindex]['y'] = 200 + 10 * Math.sin(projectiles[pindex]['angle']);
});
</script>
</html>
Processing.js Canvas Sketch (Reference)
void draw() {
size(600,400);
background(255,255,255);
fill(#FF0000);
ellipse(300,200,15,15);
for(i = 0;i < projectiles.length;i++) {
ellipse(projectiles[i]['x'],projectiles[i]['y'],2,2);
}
}
You mix radians and degrees here. The JavaScript Math functions that deals with angles needs radian values:
From MDN:
The atan2 method returns a numeric value between -pi and pi
representing the angle theta of an (x,y) point. This is the
counterclockwise angle, measured in radians, between the positive X
axis, and the point (x,y).
And for Math.cos and Math.sin:
A number given in unit of radians.
so you could try with this instead:
/// keep radians, don't convert to degrees
projectiles[pindex]['angle'] = Math.atan2(y - 200, x - 300); // * 180 / Math.PI;
projectiles[pindex]['x'] = 300 + 10 * Math.cos(projectiles[pindex]['angle']);
projectiles[pindex]['y'] = 200 + 10 * Math.sin(projectiles[pindex]['angle']);
Unless you want to keep degrees which in case you need to do this:
projectiles[pindex]['angle'] = Math.atan2(y - 200, x - 300) * 180 / Math.PI;
/// convert degrees back to radians
projectiles[pindex]['x'] =
300 + 10 * Math.cos(projectiles[pindex]['angle'] * Math.PI / 180);
projectiles[pindex]['y'] =
200 + 10 * Math.sin(projectiles[pindex]['angle'] * Math.PI / 180);
Is there a way to translate into javascript a piece of code that will allow me to show map pins around a point taking in consideration a radius ?
var data=[
{long:3,lat:2},
{long:5,lat:2},
{long:2,lat:3}
];
aCoord={long:1,lat:2};
for(var i=0;i<data.length;i++){
if (data[i] is 30 kms far from aCoord)
myMap.addPin(data[i]);
}
myMap.autozoom();
Thank you,
Regards
I came up with this example so you have an idea on how to calculate the points. You'll need to figure out how to do any necessary conversions for lat/lon.
/**
* Returns coordinates for N points around a circle with a given radius from
* the center.
*
* center: array [x, y]
* radius: int
* num_points: int
*/
function get_points_on_circle(center, radius, num_points) {
if (!num_points) num_points = 10;
var interval = Math.PI * 2 / num_points;
points = [];
i = -1;
while (++i < num_points) {
var theta = interval * i,
point = [Math.cos(theta) * radius + center[0], Math.sin(theta) * radius + center[1]];
points.push(point);
}
return points;
}
// Sample usage
var center = [250, 250],
radius = 100,
num_points = 10;
var points = get_points_on_circle(center, radius, num_points);
Test it out (uses Raphael for plotting)
If you are interested in learning a little about the logic:
A radian is a unit of measure for angles. There are a total of 2*PI radians in a circle. Using that fact, you can calculate the angle interval of any number of points on a circle by performing 2*PI/num_points.
When you know the angle interval, you can calculate the angle (theta) of a point on a circle. Once you have theta (the angle), you have polar coordinates (radius,angle). For that to be of any use to us in this problem, you need to convert the polar coordinates into Cartesian coordinates (x,y). You can do that by using the following formulas:
x = cos(theta) * radius
y = sin(theta) * radius
That's pretty much it in a nutshell.