Get pixel position from bearing - javascript

I need to position a dot on a coordinate system based on its bearing and radius.
Using the following code i can position a dot at the correct distance from the center, but this only works horizontally.
What i need is both top & left position of the dot based on bearing & radius
setTimeout(function() {
var km2show = 100;
var testDistance = 50;
var width = $('#radar').width(); // Width & height of radar
var center = width/2; // Radar center
var px2km = center/km2show; // Pixels per km
var radius = radius2coor((px2km*testDistance),center);
var bearing = 45;
// Set height of radar based on the width
$('#radar').height(width);
// Set dot in center of radar
$('#centerDot').css({top: center, left: center});
$('#container').append("<div style='position:absolute;top:0px;left:"+radius+"px;color:white;'>*</div>");
},100);
function radius2coor(radius,center) {
var res = radius-center;
return res;
}
Please see
jsFiddle
So how would i go about getting bot top and left position of the dot ?
The end result should position the dot at the red marker having a bearing of 45 degrees:

The main issue you were encountering was that the angle wasn't in radians so first thing we want is to convert the 45 degrees to pi/4.
Also, when going from regular angular coordinates to x,y coordinates you multiply the radius by sine of the angle to find y coordinate and you multiply the radius by the cosine of the angle to get the x coordinate. Just think about the unit circle and it will make sense.
var bearing = parseInt(prompt("enter angle in degrees", "0"));
if(!isNaN(bearing)){
setTimeout(function() {
var km2show = 100;
var testDistance = 50;
var width = $('#radar').width(); // Width & height of radar
var center = width/2; // Radar center
var px2km = center/km2show; // Pixels per k
//not sure what this is doing so I set a random radius
//(called it distanceFromCenter). If you need this to be
//the distance between two cartesian points then you can
//just implement the distance formula.
//var radius = radius2coor((px2km*testDistance),center);
var radius = 100;
var radianBearing = (bearing/180)*Math.PI
// Set height of radar based on the width
$('#radar').height(width);
// Set dot in center of radar
$('#centerDot').css({top: center, left: center});
//the main issue you were encountering was that the angle wasn't in radians so I converted it.
positionDot(radius, radianBearing);
},100);
}
function positionDot(distanceFromCenter, bearing, width)
{
//when going from regular angular coordinates to x,y coordinates you multiply the radius by sine of the angle to find y coordinate and you multiply the radius by the cosine of the angle to get the x coordinate.
$('#container').append("<div style='position:absolute;top:"+(-distanceFromCenter*Math.sin(bearing)).toString()+"px;left:"+distanceFromCenter*Math.cos(bearing)+"px;color:white;'>*</div>");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id='radar' style='width:100%;max-width:400px;height:400px;border:1px black solid;border-radius:400px;background-color:#3c3c3c;position:relative;'>
<div id='centerDot' style='position:absolute;color:white;'>
<div style='position:relative;' id='container'></div>
<b>*</b>
</div>
</div>

To get both coordinates, you need coordinates of center, radius and bearing.
Note that trigonometric functions usually work with argument in radians, not degrees (don't know about javascript math library)
P.X = Center.X + Radius * Math.Cos(bearing)
P.Y = Center.Y + Radius * Math.Sin(bearing)

You can get it with:
x = center + radius * cos(bearing)
y = center + radius * sin(bearing)
and as MBo said, you have to convert bearing into radians
rad = deg * PI / 180
https://upload.wikimedia.org/wikipedia/sr/8/85/Trig-funkcije1.gif

Related

Dynamically compute SVG Path for a full circle with JavaScript

I'm trying to draw the perimeter of a circle depending on the angle inputed by the user. The angle determines the perimeter completion : 360° being the full circle, 180 half of the circle, and so on.
My problem is : given the radius, the angle and the center coordinates of the circle, how can I dynamically compute the path of the perimeter ?
I know it's probably basic math but everything I tried so far didn't work.
Here is my fiddle : https://jsfiddle.net/Hal_9100/L311qq88/
My problem is finding the right formula for the x and y coordinates of the path :
var x = i * (radius * Math.cos(angle)) + centerX;
var y = i * (radius * Math.sin(angle)) + centerY;
Am I going all wrong here ?
Here is an example of what I'm trying to do : please note that only the black perimeter should be drawn : I just used the dotted red lines to give a visual example of how the perimeter should be drawn depending on the value given by the user.
Yes, the problem is your maths. Here is the correct way to calculate the x,y coordinate pairs (note that the iteration is from zero to the required angle, not from zero to the radius):
for (var i = 0; i <= angle; i++) {
var x = (radius * Math.cos((i-90)*Math.PI/180)) + centerX;
var y = (radius * Math.sin((i-90)*Math.PI/180)) + centerY;
Your fiddle works fine if you substitute these three lines.

Trigonometry Issue causing distortion when drawing floor textures in raycaster

I'm creating a game with raycasted 3D graphics like Wolfenstein 3D but using line segments instead of a grid of blocks for walls. Everything is fine when drawing the floors until rotating the player view.
the floor should be aligned against the walls
Here is the view in 2D, with each pixel on the floor on the screen rendered as a blue point:
In the top image is when the player's rotation is Math.PI. In the bottom image it is rotated slightly.
A significant feature of this is the beginning of the cone of points is aligned along the y axis. It should look like a frustrum.
Here is the code I am using to find the x and y coordinates of each point where a texture is drawn on the floor. This code is run for each x value on the screen.
The variable "projPlane" is the projection plane, which is the size of the screen.
projDistance is the distance from the player to the projection plane so that it fits within the field of view, or (projPlane.width/2)/Math.tan(VectorMath.toRadians(fov/2))
pHeight is the players height.
The variable "x" is the x value of the row being rendered on the screen.
//FLOOR TEXTURE
var floorSize = Math.floor((projPlane.height-wallSize)/2); //draw the floor from under the wall
var floorTextureIndex = 1;
//for texture y
if(floorSize > 0){ // values need to be positive
//find the point on the floor
var textureWidth = textures[floorTextureIndex].textureImage.width;
var textureHeight = textures[floorTextureIndex].textureImage.height;
//console.log(coordX);
for (var ty = 0; ty < floorSize; ty++){
//angle is tan
var yAngle = projPlane.distance / (ty + wallSize/2); //ty + wallSize/2 is the point on the projection plane
var yDistance = yAngle * pHeight; //pHeight is player height
var worldY = player.y + Math.sin(player.vector)*yDistance;
var coordY = Math.floor(worldY % (textureHeight));
var xAngle = Math.atan((projPlane.width/2 - x)/projPlane.distance);
/*if(x < projPlane.width/2){//tangent of the angle in the projectionPlane
xAngle = (x) / projPlane.distance;
}
else{
xAngle = (x-projPlane.width) / projPlane.distance;
}*/
var xDistance = yDistance/Math.cos(xAngle);
var worldX = player.x + Math.cos(player.vector - xAngle)*xDistance;
//console.log(xDistance);
var coordX = Math.floor(worldX % (textureWidth));//disable until I can get y
floorPoints.push(new Point(worldX,worldY));
var tempTexture = textures[floorTextureIndex];
if(tempTexture.textureData[coordX] != undefined){
// a different function drawns these to the screen in descending order
floorTextureColors.push(tempTexture.textureData[coordX][coordY]);
}
};
}
It doesn't seem to be an issue with the y value since the y coordinates of the floor texture seem to appear where they should.(EDIT: it actually was to do with the y value. Adding the xAngle to the player.vector when finding the y position returns a correct y position but there is still a "curved" distortion. I hope one of you can propose a more concrete solution.)
What I do to find the X coordinate is form a triangle with the distance from the player to the floor point as the opposite side the angle that the point makes with the player. The hypotenuse should be the magnitude of the distance to the point's x coordinate.
Then I multiply the cosine of the angle by the magnitude to get the x value.
It works whenever the character isn't pointing west and east. What is causing all the first points to have the same y value? I think that's the biggest clue on the distortion occurring here.

Parametric equation to place a leaflet marker on the circumference of a circle is not precise?

I am working on an application where I have the center of a circle and the radius and I am plotting the circle with the help of Leaflet.
I placed a marker on the north most end of the circumference and made it draggable.
var circle = L.circle(coords, radius).addTo(map);
convertRadiusToLatitude = parseInt(response.radius)/111111;
var coordsOnRadius = [parseFloat(response.lat) + convertRadiusToLatitude, parseFloat(response.long)];
var markerOnRadius = L.marker(coordsOnRadius, {draggable: true}).addTo(map);
Now, this adds the marker to the circumference and now I wanted it to be draggable only on the circumference itself for which I used the parametric equation.
Parametric equation
x = Xc + R * cos(theta)
y = Yc + R * sin(theta)
Code for dragging
markerOnRadius.on('drag', function(e){
bearing = marker.getLatLng().bearingTo(markerOnRadius.getLatLng());
var markerOnRadiusX = parseFloat(response.lat) + ((0.000009 * parseFloat(response.radius)) * Math.cos( toRad(bearing) ));
var markerOnRadiusY = parseFloat(response.long) + ((0.000009 * parseFloat(response.radius)) * Math.sin( toRad(bearing) ));
markerOnRadius.setLatLng([markerOnRadiusX, markerOnRadiusY]);
});
The bearingTo method:
L.LatLng.prototype.bearingTo = function(other) {
var d2r = L.LatLng.DEG_TO_RAD;
var r2d = L.LatLng.RAD_TO_DEG;
var lat1 = this.lat * d2r;
var lat2 = other.lat * d2r;
var dLon = (other.lng-this.lng) * d2r;
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
var brng = Math.atan2(y, x);
brng = parseInt( brng * r2d );
brng = (brng + 360) % 360;
return brng;
};
Issue
When I start dragging the marker, this code is working fine and brings it back to the circumference at the bearing at which the marker is dragged to. But there is one problem, the coords on the circumference are slightly off and in terms of longitude. When the bearing is 0 (north), the coords are perfect, but when it is 90 (east), the longitude is slightly less that it should for the marker to be at the circumference.
Again at 180 (south), coords are perfect, but at 270 (west), the longitude calculated is slightly less and the marker tends towards the radius again.
So basically if you visualize the marker being dragged, it starts perfectly on the north end and starts coming inside the circle slightly increasing with the bearing till it reacher 90 and then starts going towards the circumference again till 180 when it is perfect again.
It forms more like a ellipse if you get the gist of it.
Could anyone tell me why is longitude coming a little off and why the marker moves in an elliptical path. Has it something to do with the world coordinates and window coordinates. Or are my equations slightly off somewhere?
It does look like a projection issue. In your dragging code you are basically doing
lat = a + r cos(baring)
long = b + r sin(baring)
giving a circle in the Lat-Long coordinates. This would work fine if you were at the equator with Mercator projection. You will get more distortion as you move further towards the polls.
Assume you are using the defaults for Leaflet reference doc You have the EPSG3857 Web Mercator coordinates.
If you want to ensure you have a exact circle it will be better to work using screen coordinates. You can get these using methods on the ICRS objects. First get the coordinate system L.CRS.EPSG3857 and use the latLngToPoint and pointToLatLng methods.
var crs = L.CRS.EPSG3857;
var zoom = ...; // how you calculate your zoom factor
markerOnRadius.on('drag', function(e){
var markerLL = marker.getLatLng()
var morLL = markerOnRadius.getLatLng();
var markerP = crs.latLngToPoint(markerLL,zoom);
var morP = crs.latLngToPoint(morLL,zoom);
// get the distance between the two points
var dist = markerP.distanceTo(morP);
// Get the vector from center to point
var A = morP.subtract(markerP);
// scale so its of the desired length
var B = A. multiplyBy( factor / dist);
// Add on the center
var C = markerP.add(B);
// Convert back to LatLong
var D = crs.pointToLatLng(C,zoom);
markerOnRadius.setLatLong(D);
});

mercator projection is not working fine?

I need use mercator projection to point the places by latitude and logitude in my svg application.I have serached a lot and i got these links,
http://en.wikipedia.org/wiki/Mercator_projection
Covert latitude/longitude point to a pixels (x,y) on mercator projection
CODE
//this lat and long is for chicago
var latitude = 41.850033; // (φ)
var longitude = -87.65005229999997; // (λ)
var PI = 3.14;
var mapWidth = 750;
var mapHeight = 380;
// get x value
var x = (mapWidth * (180+longitude) / 360) % mapWidth + (mapWidth / 2);
// convert from degrees to radians
var latRad = latitude * PI / 180;
// get y value
var mercN = Math.log(Math.tan((PI / 4) + (latRad / 2)));
var y = (mapHeight / 2) - (mapWidth * mercN / (2 * PI));
I've used this code in my application, but it doesn't work for me.
Please help to get x and y position from the latitude and longitude.
Any Suggestions should be appreciated.
You forgot the top left and bottom right corner of the map and the factor to multiply the x,y coordinates to give the correct map projection. You can use a fixed map coordinate, i.e. a factor or you can compute the bounding box:Convert lat/lon to pixel coordinate? and then compute the world width and height that fits the map's width and height.

Rotating canvas around a point and getting new x,y offest

I am writing a multitouch jigsaw puzzle using html5 canvas in which you can rotate the pieces around a point. Each piece has their own canvas the size of their bounding box. When the rotation occurs, the canvas size must change, which I am able to calculate and is working. What I can't figure out, is how to find the new x,y offsets if I am to have this appear to be rotating around the pivot (first touch point).
Here is an image to better explain what I'm trying to achieve. Note the pivot point is not always the center, otherwise I could just halve the difference between the new bounds and the old.
So I know the original x, y, width, height, rotation angle, new bounds(rotatedWidth, rotatedHeight), and the pivot X,Y relating to original object. What I can't figure out how to get is the x/y offset for the new bounds (to make it appear that the object rotated around the pivot point)
Thanks in advance!
First we need to find the distance from pivot point to the corner.
Then calculate the angle between pivot and corner
Then calculate the absolute angle based on previous angle + new angle.
And finally calculate the new corner.
Snapshot from demo below showing a line from pivot to corner.
The red dot is calculated while the rectangle is rotated using
translations.
Here is an example using an absolute angle, but you can easily convert this into converting the difference between old and new angle for example. I kept the angles as degrees rather than radians for simplicity.
The demo first uses canvas' internal translation and rotation to rotate the rectangle. Then we use pure math to get to the same point as evidence that we have calculated the correct new x and y point for corner.
/// find distance from pivot to corner
diffX = rect[0] - mx; /// mx/my = center of rectangle (in demo of canvas)
diffY = rect[1] - my;
dist = Math.sqrt(diffX * diffX + diffY * diffY); /// Pythagoras
/// find angle from pivot to corner
ca = Math.atan2(diffY, diffX) * 180 / Math.PI; /// convert to degrees for demo
/// get new angle based on old + current delta angle
na = ((ca + angle) % 360) * Math.PI / 180; /// convert to radians for function
/// get new x and y and round it off to integer
x = (mx + dist * Math.cos(na) + 0.5)|0;
y = (my + dist * Math.sin(na) + 0.5)|0;
Initially you can verify the printed x and y by seeing that the they are exact the same value as the initial corner defined for the rectangle (50, 100).
UPDATE
It seems as I missed the word in: offset for the new bounds... sorry about that, but what you can do instead is to calculate the distance to each corner instead.
That will give you the outer limits of the bound and you just "mix and match" the corner base on those distance values using min and max.
New Live demo here
The new parts consist of a function that will give you the x and y of a corner:
///mx, my = pivot, cx, cy = corner, angle in degrees
function getPoint(mx, my, cx, cy, angle) {
var x, y, dist, diffX, diffY, ca, na;
/// get distance from center to point
diffX = cx - mx;
diffY = cy - my;
dist = Math.sqrt(diffX * diffX + diffY * diffY);
/// find angle from pivot to corner
ca = Math.atan2(diffY, diffX) * 180 / Math.PI;
/// get new angle based on old + current delta angle
na = ((ca + angle) % 360) * Math.PI / 180;
/// get new x and y and round it off to integer
x = (mx + dist * Math.cos(na) + 0.5)|0;
y = (my + dist * Math.sin(na) + 0.5)|0;
return {x:x, y:y};
}
Now it's just to run the function for each corner and then do a min/max to find the bounds:
/// offsets
c2 = getPoint(mx, my, rect[0], rect[1], angle);
c2 = getPoint(mx, my, rect[0] + rect[2], rect[1], angle);
c3 = getPoint(mx, my, rect[0] + rect[2], rect[1] + rect[3], angle);
c4 = getPoint(mx, my, rect[0], rect[1] + rect[3], angle);
/// bounds
bx1 = Math.min(c1.x, c2.x, c3.x, c4.x);
by1 = Math.min(c1.y, c2.y, c3.y, c4.y);
bx2 = Math.max(c1.x, c2.x, c3.x, c4.x);
by2 = Math.max(c1.y, c2.y, c3.y, c4.y);
to rotate around the centre point of the canvas you can use this function:
function rotate(context, rotation, canvasWidth, canvasHeight) {
// Move registration point to the center of the canvas
context.translate(canvasWidth / 2, canvasHeight/ 2);
// Rotate 1 degree
context.rotate((rotation * Math.PI) / 180);
// Move registration point back to the top left corner of canvas
context.translate(-canvasWidth / 2, -canvasHeight/ 2);
}
Here is the way that worked best for me. First I calculate what is the new width and height of that image, then I translate it by half of that amount, then I apply the rotation and finally I go back by the original width and height amount to re center the image.
var canvas = document.getElementById("canvas")
const ctx = canvas.getContext('2d')
drawRectangle(30,30,40,40,30,"black")
function drawRectangle(x,y,width,height, angle,color) {
drawRotated(x,y,width,height,angle,ctx =>{
ctx.fillStyle = color
ctx.fillRect(0,0,width,height)
})
}
function drawRotated(x,y,width,height, angle,callback)
{
angle = angle * Math.PI / 180
const newWidth = Math.abs(width * Math.cos(angle)) + Math.abs(height * Math.sin(angle));
const newHeight = Math.abs(width * Math.sin(angle)) + Math.abs(height * Math.cos(angle));
var surface = document.createElement('canvas')
var sctx = surface.getContext('2d')
surface.width = newWidth
surface.height = newHeight
// START debug magenta square
sctx.fillStyle = "magenta"
sctx.fillRect(0,0,newWidth,newHeight)
// END
sctx.translate(newWidth/2,newHeight/2)
sctx.rotate(angle)
sctx.translate(-width/2,-height/2)
callback(sctx)
ctx.drawImage(surface,x-newWidth/2,y-newHeight/2)
}
#canvas{
border:1px solid black
}
<canvas id="canvas">
</canvas>

Categories

Resources