How to convert x,y coordinates to an angle? - javascript

Microsoft provide an excellent SVG gradient maker so IE9 can also have "CSS3" gradients (click Custom).
I currently utilise their logic for my Fireworks and Dreamweaver extensions to convert gradients to SVG, but I only know how to do it for standard top, bottom, left, right directions. If you enter an angle, I don't do the conversion, because I'm not sure how I would convert x1, x2, y1, y2 to CSS3 angle degrees.
The gradient generator provides values like this: x1="0%" y1="0%" x2="56.262833675564686%" y2="68.29999651227678%"
I'm not great with mathematics or trigonometry, so could somebody help me out? I'd also like to use the same math in a Sass mixin to do a similar thing, if possible.

If you get deltaX and deltaY from your coordinates then Math.atan2 returns the arctangent of the quotient of its arguments. The return value is in radians.
var deltaX = x2 - x1;
var deltaY = y2 - y1;
var rad = Math.atan2(deltaY, deltaX); // In radians
Then you can convert it to degrees as easy as:
var deg = rad * (180 / Math.PI)
Edit
There was some bugs in my initial answer. I believe in the updated answer all bugs are addressed. Please comment here if you think there is a problem here.

The currently accepted answer is incorrect. First of all, Math.tan is totally wrong -- I suspect Mohsen meant Math.atan and this is just a typo.
However, as other responses to that answer state, you should really use Math.atan2(y,x) instead. The regular inverse tangent will only return values between -pi/2 and pi/2 (quadrants 1 and 4) because the input is ambiguous -- the inverse tangent has no way of knowing if the input value belongs in quadrant 1 vs 3, or 2 vs 4.
Math.atan2, on the other hand, can use the xy values given to figure out what quadrant you're in and return the appropriate angle for any coordinates in all 4 quadrants. Then, as others have noted, you can just multiply by (180/Math.pi) to convert radians to degrees, if you need to.

Instead of using Math.tan function You should use Math.atan2:
Here is an example of use:
deltaX = x2 - x1;
deltaY = y2 - y1;
deg = Math.atan2(deltaY, deltaX)*180.0/Math.PI;
and this will return a degree from <-180;180>.

If you in a Quadrant
P1=(X0,Y0)
P2=(X1,Y1)
a=(X0-X1)
b=(Y0-Y2)
deltaX=((a)**2)**0.5
deltaY=((b)**2)**0.5
rad=math.atan2(deltaY, deltaX)
deg = rad * (360 / math.pi)
print deg
the deg will between 0 ~ 180

This function takes 2 elements and returns the degree between the middle of the elements.
For example, I used it on a world map, to make the image of plane rotate in the direction of a city.
function degFromTwoElements(el1,el2){
var x1,x2,y1,y2,cx1,xy1,cx2,cy2,deltaX,deltaY,dx,dy,rad,deg,shortest,number;
x1 = el1.position().left;
y1 = el1.position().top;
x2 = el2.position().left;
y2 = el2.position().top;
cx1 = x1 - (el1.width() / 2);
cy1 = y1 - (el1.height() / 2);
cx2 = x2 - (el2.width() / 2);
cy2 = y2 - (el2.height() / 2);
deltaX = cx2 - cx1;
deltaY = cy2 - cy1;
y1 = Math.sqrt((Math.abs(deltaY)*Math.abs(deltaY))+(Math.abs(deltaX)*(Math.abs(deltaX))));
x1 = 0;
dy = deltaY-y1;
dx = deltaX-x1;
rad = Math.atan2(dy, dx);
deg = rad * (360 / Math.PI);
shortest;
number = Math.abs(deg);
if ((360 - number ) < number){
shortest = 360 - number;
console.log('shorter degree: ' + shortest);
return shortest;
}
else console.log('Angle is: ' + deg);
return deg;
}

var x,x1,x2,y,y1,y2;
var cells = 'cell0';
var h,w;
var cx,cy;
var dx,dy;
var derajat;
var deg;
var ang;
var light;
var control;
function mouse_watch(event){
x = event.clientX;
y = event.clientY;
cell_data(cells);
koordinat(x2,y2);
busur(derajat);
}
function koordinat(x2,y2){
x2 = x-cx;
y2 = y-cy;
yk = y2;
xk = x2;
}
function busur(derajat){
y1 = Math.sqrt((Math.abs(yk)*Math.abs(yk))+(Math.abs(xk)*(Math.abs(xk))));
x1 = 0;
dy = yk-y1;
dx = xk-x1;
rad = Math.atan2(dy, dx);
derajat = rad * (360 / Math.PI);
cell = document.getElementById(cells);
ang = cell.getElementsByClassName('angle0')[0];
ang.style.transform = 'rotate('+derajat+'deg)';
light = ang.getElementsByClassName('points')[0];
light.style.height = y1+'px';
}
function cell_data(cells){
cell = document.getElementById(cells);
h = Number(cell.style.height.replace('px',''));
w = Number(cell.style.width.replace('px',''));
cy = Number(cell.style.top.replace('px',''))+h/2;
cx = Number(cell.style.left.replace('px',''))+w/2;
}
.preview_engine{
position: absolute;
top: 0;
left: 0;
padding: 10px;
background-color: #2E8AE6;
color: white;
}
body{
cursor: default;
width: 100%;
height: 100%;
font-family: Arial;
font-size: 12px;
}
.fieldwork{
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
}
.cell{
position: relative;
transition : width 2s, height 2s, top 2s, left 2s;
background-color: red;
}
.angle0{
width: 200px;
height: 200px;
position: absolute;
top: -75px;
left: -75px;
background-color: green;
border-radius: 50%;
opacity: 0.5;
transition : width 2s, height 2s, top 2s, left 2s;
}
.points{
width: 10px;
height: 10px;
position: absolute;
left: 95px;
top: 95px;
background-color: red;
border-radius: 1em;
opacity: none;
}
<div class="fieldwork" onmousemove="mouse_watch(event)">
<div class='cell' id="cell0" style="width:50px;height:50px;top:200px;left:400px;">
<div class="angle0">
<div class="points"></div>
</div>
</div>
</div>

Related

Calculate position (X, Y) on cirlcle base on height

I think the title is a little bit confusing so I will try to say it better.
Image you have this code:
#NadrzFrontView:before {
--beginHeight: var(--startHeight);
--endHeight: var(--finishHeight);
animation: 2s fillin ease forwards;
content: "";
position: absolute;
bottom: 0;
width: 300px;
height: 0;
background-color: #00FFF5;
display: inline-block;
}
#NadrzFrontView {
width: 300px;
height: 300px;
border-radius: 50%;
border: 3px solid black;
position: relative;
overflow: hidden;
}
<div id="NadrzFrontView" style="--startHeight: 0%; --finishHeight: 50%;"> </div>
It looks like this:
Now, you can determent left, top position and height of an element using this script:
function getOffset(el) {
var rect = el.getBoundingClientRect();
return {
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset,
width: rect.width || el.offsetWidth,
height: rect.height || el.offsetHeight
};
}
Ok, when we are able to find these then we go to our problem:
function SomeFunction() {
var thickness = 1;
var color = '#000000';
var off_nadrz = getOffset(document.getElementById('NadrzFrontView'));
var elem = document.getElementById('NadrzFrontView');
var pseudoStyle = window.getComputedStyle(elem, ':before');
var off_pseudo_elem = getOffset(elem);
off_pseudo_elem.top += parseInt(pseudoStyle.top, 10);
off_pseudo_elem.left += parseInt(pseudoStyle.left, 10);
off_pseudo_elem.height = parseInt(pseudoStyle.height, 10);
//finding middle of the circle
var x1 = off_nadrz.left + (off_nadrz.width / 2);
var y1 = off_nadrz.top + (off_nadrz.height / 2);
//draw point in the middle of circle
document.getElementById("AllLines").innerHTML += CreateHtmlPoint(color, x1, y1);
//fincding point on the side of an element
var x2; //I need to find this -- see more in examples
var y2; //I need to find this -- see more in examples
document.getElementById("AllLines").innerHTML += CreateHtmlPoint(color, x2, y2); //This does not work
}
function CreateHtmlPoint(color, cx, cy) {
cx -= 5; // - 5, because width of point is 10
cy -= 5; // - 5, because height of point is 10
return "<div style='padding:0px; z-index: 2; margin:0px; height: 10px; width: 10px; background-color:" + color + "; line-height:1px; position:absolute; left:" + cx + "px; top:" + cy + "px; border-radius: 50%;' />";
}
So, now example of an x2 and y2 would look like this:
Basically, we know on what coordinades is top of circle (off_nadrz.top), bottom of circle (off_nadrz.top + (off_nadrz.height / 2)) and height of an circle (off_nadrz.height). We also know same things of an psuedo_element (blue thing in the circle). From this we need to calculate x2 and y2.
Thanks for every suggestion because I am fighting with this problem for 2 days now...
you have only to apply the circumference formula:
var y2 = y1 - ( off_pseudo_elem.height - off_nadrz.height / 2 );
once you have y2, calculate x2:
var r = off_nadrz.height / 2; // if the figure is a circle, not an ellipse
var x2 = x1 + Math.sqrt( r * r - ( y2 - y1 ) * ( y2 - y1 ) );

How do I calculate the angle between two points?

I am attempting to create a 2d tile based game in Javascript, and I need to be able to calculate the angle between two points. I am using the atan2 function to find the angle between two points like so:
function getAngleDegrees(fromX, fromY, toX, toY, force360 = true) {
let deltaX = toX - fromX;
let deltaY = toY - fromY;
let radians = Math.atan2(deltaY, deltaX);
let degrees = (radians * 180) / Math.PI;
if (force360) {
while (degrees >= 360) degrees -= 360;
while (degrees < 0) degrees += 360;
}
return degrees;
}
However, this isn't providing me with the correct result. I have checked the code for logic or math errors and can't find any. No matter what points I input to this function the result will be off by many degrees.
I have created a JS fiddle to visualize the problem:
https://jsfiddle.net/fa6o7wdy/40/
If anyone knows how I can fix my angle function to provide the correct result please help!
Edit:
Here is a picture of the problem:
https://imgur.com/a/OXDCOux
Based on the photo sample you provide, for getting the desired angle you want with current Math.atan() function, you want to reverse first and then rotate the angle by 90 degrees couter clockwise
function getAngleDegrees(fromX,fromY,toX,toY,force360 = true) {
let deltaX = fromX-toX;
let deltaY = fromY-toY; // reverse
let radians = Math.atan2(deltaY, deltaX)
let degrees = (radians * 180) / Math.PI - 90; // rotate
if (force360) {
while (degrees >= 360) degrees -= 360;
while (degrees < 0) degrees += 360;
}
console.log('angle to degree:',{deltaX,deltaY,radians,degrees})
return degrees;
}
or simply + 90 degrees to this line without changing deltaX and deltaY
let degrees = (radians * 180) / Math.PI + 90; // rotate
Note: I haven't test out all possible edge cases
const inBlk = document.createElement('i')
, getXY = (p,xy) => Number(p.split('-')[xy==='x'?0:1])
;
for(let i=0;i<100;i++) // build Grid
{
let nI = inBlk.cloneNode()
, u1 = i%10
;
nI.textContent = u1+'-'+(i-u1)/10
grid.appendChild(nI)
}
let points = [ {x:0, y:0, old:null}, {x:0, y:0, old:null}]
, pN = 0
;
grid.onclick=e=>
{
if (!e.target.matches('i')) return
let elm = e.target.textContent
points[pN].x = getXY(elm, 'x')
points[pN].y = getXY(elm, 'y')
if (points[pN].old ) points[pN].old.classList.remove('color_0', 'color_1')
points[pN].old = e.target
points[pN].old.classList.add(`color_${pN}` )
pN = ++pN %2
if (pN==0) angle.textContent = ` angle: ${getAngleDegrees(points[0],points[1])}°`
}
function getAngleDegrees( from, to, force360 =true)
{
let deltaX = from.x - to.x
, deltaY = from.y - to.y // reverse
, radians = Math.atan2(deltaY, deltaX)
, degrees = (radians * 180) / Math.PI - 90 // rotate
;
if (force360)
{
while (degrees >= 360) degrees -= 360;
while (degrees < 0) degrees += 360;
}
return degrees.toFixed(2)
}
:root { --sz-hw: 26px; }
#grid {
font-family: 'Courier New', Courier, monospace;
font-size : 10px;
margin : calc( var(--sz-hw) /2);
}
#grid i {
display : block;
float : left;
width : var(--sz-hw);
height : var(--sz-hw);
border : 1px solid grey;
text-align : center;
margin : 2px;
line-height: var(--sz-hw);
cursor : pointer;
}
#grid i:nth-child(10n-9) {clear: both; }
.color_0 { background-color: lightcoral; }
.color_1 { background-color: lightgreen; }
#angle { padding: calc( var(--sz-hw) /2) 0 0 calc( var(--sz-hw) *13.4); }
<p id="grid"></p>
<h4 id="angle">angle: ?</h4>
DeltaX toX and fromX needed to be swapped around, same goes for DeltaY. Also, I've subtracted 90 to angle, in order to make 0 degree being North.
The % (mod) operator does same job as your 2 x while loop.
function getAngleDegrees(fromX,fromY,toX,toY,force360 = true) {
let deltaX = fromX - toX;
let deltaY = fromY - toY;
let radians = Math.atan2(deltaY, deltaX)
let degrees = ((radians * 180) / Math.PI) - 90;
if (force360) {
degrees = (degrees + 360) % 360;
}
console.log('angle to degree:',{deltaX,deltaY,radians,degrees})
return degrees;
}
Your code is working correctly, it's just that you have a bit of confusion in your coordinate system.
The angle between 2 points is relative to where you measure from. By subtracting P2 from P1, you're making the angle relative to your starting point. Thus, atan2 is giving you the clockwise angle relative to the X axis.
Traditionally, the X axis is the starting point for rotations, so a horizontal line has an angle of 0:
x = 1;
y = 0;
angle = atan2(y, x) // Equals 0
You've got your grid with Y+ going down, so as Y becomes positive, you'll get clockwise angles from the x-axis.
x = 0;
y = 1;
angle = atan2(y, x) // Equals PI/2, or 90deg
If this is confusing with Y+ going down, you may want to rethink your grid so that Y+ goes up instead.
PS: Good luck on your game!

Move a coordinate along an angled line using mouse movement

Please run the code snippet below - you will see an angled line derived from the corners of a box. Given mouse movement, I would like to move the marker along this line - where either:
movement in x will move along the line, and additional movement in y will accelerate
movement in x will move along the line and you can get from start to finish with it (whatever is simplest)
So far I haven't got anywhere but I'll update if I do and many thanks for any help.
PS doesn't really matter if it doesn't move exactly along the line - it's the algorithm to move along an angle I'm interested in.
Fiddle: http://jsfiddle.net/ngr3dbhx/3/
var boxEl = document.getElementById('box');
var lineEl = document.getElementById('line');
var markerEl = document.getElementById('marker');
var rad, deg;
// Draw an angled line for demonstration purposes.
function getElementOffset (el) {
var rect = el.getBoundingClientRect();
var docEl = document.documentElement;
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
return {
top: rectTop,
left: rectLeft
};
}
function calcAndDrawAngle () {
var boxOffset = getElementOffset(boxEl);
var x1 = boxOffset.left;
var y1 = boxOffset.top;
var x2 = boxOffset.left + boxEl.offsetWidth;
var y2 = boxOffset.top + boxEl.offsetHeight;
var deltaX = x2 - x1;
var deltaY = y2 - y1;
rad = Math.atan2(deltaY, deltaX);
deg = rad * (180 / Math.PI);
lineEl.style.transform = 'rotate(' + deg + 'deg) translate(-50%, 0)';
}
// On mouse move I want to move the marker along the anged line..
// Do something with rad or deg?
document.addEventListener('mousemove', function (e) {
markerEl.style.top = e.clientY + 'px';
markerEl.style.left = e.clientX + 'px';
});
calcAndDrawAngle();
window.addEventListener('resize', calcAndDrawAngle);
html,
body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#box {
position: absolute;
top: 20%;
left: 30%;
width: 35%;
height: 30%;
border: 1px solid #999;
}
#line {
position: absolute;
top: 0;
left: 0;
transform-origin: top left;
height: 1px;
width: 9999em;
background-color: black;
}
#marker {
position: absolute;
width: 8px;
height: 8px;
background-color: red;
}
<div id="box">
<div id="line"></div>
</div>
<div id="marker"></div>
you mean this?
https://jsfiddle.net/2q1nLh3q/1/
The formula is basic mathematics:
y = k * x + d
Where k is deltaY / deltaX and d is the point where the line crosses the y axis.
So your function could look like this:
document.addEventListener('mousemove', function (e) {
window.requestAnimationFrame(function() {
var boxOffset = getElementOffset(boxEl);
var k = boxEl.offsetHeight / boxEl.offsetWidth;
var d = boxOffset.top - boxOffset.left * k;
var mouseY = k * e.clientX + d;
markerEl.style.top = mouseY + 'px';
markerEl.style.left = e.clientX + 'px';
});
});
Using fairly simple math, calculate y for any x within limited range (subtracted 4 to place rectangle at center):
//var canvas = document.getElementById('canvas');
var boxEl = document.getElementById('box');
var lineEl = document.getElementById('line');
var markerEl = document.getElementById('marker');
var rad, deg;
var coords;
// Draw an angled line for demonstration purposes.
function getElementOffset (el) {
var rect = el.getBoundingClientRect();
var docEl = document.documentElement;
var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;
return {
top: rectTop,
left: rectLeft
};
}
function calcAndDrawAngle () {
var boxOffset = getElementOffset(boxEl);
var x1 = boxOffset.left;
var y1 = boxOffset.top;
var x2 = boxOffset.left + boxEl.offsetWidth;
var y2 = boxOffset.top + boxEl.offsetHeight;
coords = [{x:x1,y:y2},{x:x2,y:y1}];
var deltaX = x2 - x1;
var deltaY = y2 - y1;
rad = Math.atan2(deltaY, deltaX);
deg = rad * (180 / Math.PI);
lineEl.style.transform = 'rotate(' + deg + 'deg) translate(-50%, 0)';
}
// On mouse move I want to move the marker along the anged line..
// Do something with rad or deg?
document.addEventListener('mousemove', function (e) {
var x = Math.min(Math.max(e.clientX, coords[0].x), coords[1].x) - 4;
var y = coords[0].y + ((coords[1].y - coords[0].y) * (coords[1].x - x) / (coords[1].x - coords[0].x));
markerEl.style.top = y + 'px';
markerEl.style.left = x + 'px';
});
calcAndDrawAngle();
window.addEventListener('resize', calcAndDrawAngle);
html,
body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#box {
position: absolute;
top: 20%;
left: 30%;
width: 35%;
height: 30%;
border: 1px solid #999;
}
#line {
position: absolute;
top: 0;
left: 0;
transform-origin: top left;
height: 1px;
width: 9999em;
background-color: black;
}
#marker {
position: absolute;
width: 8px;
height: 8px;
background-color: red;
}
<div id="box">
<div id="line"></div>
</div>
<div id="marker"></div>

How to draw a lines in circle in different angles by using for loop

I used below code to draw a line in circle,Now I want to draw 12 lines in different angles with same space & lines should be touched to the circle.
<!DOCTYPE html>
<html>
<head>
<style>
#straight{
height: 30px;
border-right: 1px solid blue;
-webkit-transform: rotate(**" for loop value must be displayed"** deg);
transform: rotate(**" for loop value must be displayed"** deg);
position: absolute;
top:40px;
left:400px;
}
#circle {
height: 30px;
width: 31px;
margin-left: 81px;
margin-top: 0px;
background-color: #fff;
border: 2px solid blue;
border-radius: 65px;
position:absolute;
}
</style>
</head>
<body>
<div>
<div id="circle">
<div style="position:relative; top:-40px; left:-385px;">
<div id="straight"></div>
</div>
</div>
</body>
</html>
Please help me & thanks in advance
Check this fiddle.
It uses a function DrawLine(x1,y1,x2,y2) to draw a line between the given co-ordinates.
Basically, it creates divs with thin width and rotate them according the slope.
Looks like a wheel with spokes.
Here is a wheel in action if you need.
Here is the snippet.
drawNLines(12, 40, 40, 40);
function drawNLines(N, centreX, centreY, radius) {
for (i = 0; i < N; i++) {
angle = 360 / N;
x2 = centreX + radius * Math.cos(Math.PI * angle * i / 180);
y2 = centreY + radius * Math.sin(Math.PI * angle * i / 180);
DrawLine(centreX, centreY, x2, y2);
}
}
function DrawLine(x1, y1, x2, y2) {
if (y1 < y2) {
var pom = y1;
y1 = y2;
y2 = pom;
pom = x1;
x1 = x2;
x2 = pom;
}
var a = Math.abs(x1 - x2);
var b = Math.abs(y1 - y2);
var c;
var sx = (x1 + x2) / 2;
var sy = (y1 + y2) / 2;
var width = Math.sqrt(a * a + b * b);
var x = sx - width / 2;
var y = sy;
a = width / 2;
c = Math.abs(sx - x);
b = Math.sqrt(Math.abs(x1 - x) * Math.abs(x1 - x) + Math.abs(y1 - y) * Math.abs(y1 - y));
var cosb = (b * b - a * a - c * c) / (2 * a * c);
var rad = Math.acos(cosb);
var deg = (rad * 180) / Math.PI
htmlns = "http://www.w3.org/1999/xhtml";
div = document.createElementNS(htmlns, "div");
div.setAttribute('style', 'border:1px solid black;width:' + width + 'px;height:0px;-moz-transform:rotate(' + deg + 'deg);-webkit-transform:rotate(' + deg + 'deg);position:absolute;top:' + y + 'px;left:' + x + 'px;');
document.getElementById("circle").appendChild(div);
}
#circle {
height: 80px;
width: 80px;
margin-left: 30px;
margin-top: 30px;
background-color: #fff;
border: 2px solid blue;
border-radius: 80px;
position: absolute;
}
<div id="circle"></div>
Hope this helps. :)

Position DIV elements in circle

I need to position these 5 elements in circle. And my trigonometry knowledge is too poor to come up with a solution.
http://jsbin.com/acOSeTE/1/edit
Whoever helps please give some explanation on the math formula or forward me to a link with info about it.
I do not need a jQuery solution, since I want to learn what is behind the frameworks.
Thanks.
You'll almost certainly need CSS to position the divs with that kind of precision.
The general formula you're looking for is that for any given radian position on the circle (there are 2π radians in a circle), the x and y are:
x = originX + (Math.cos(radians) * radius);
y = originY + (Math.sin(radians) * radius);
Example: Live Copy | Live Source
CSS (you could use inline styles, I suppose):
#target {
position: absolute;
border: 1px solid black;
width: 1px;
height: 1px;
}
HTML:
<input type="button" id="theButton" value="Start/Stop">
<div id="target"></div>
JavaScript:
(function() {
var radians, maxRadians, target, radius, originX, originY, inc, timer;
radius = 50;
originX = 100;
originY = 100;
radians = 0;
maxRadians = 2 * Math.PI;
inc = 10 / 360;
target = document.getElementById("target");
positionTarget();
function positionTarget() {
var x, y;
x = originX + (Math.cos(radians) * radius);
y = originY + (Math.sin(radians) * radius);
console.log(x + "," + y);
target.style.left = x + "px";
target.style.top = y + "px";
radians += inc;
if (radians > maxRadians) {
radians -= maxRadians;
}
timer = setTimeout(positionTarget, 30);
}
document.getElementById("theButton").onclick = function() {
if (timer) {
clearTimeout(timer);
timer = 0;
}
else {
timer = setTimeout(positionTarget, 30);
}
};
})();

Categories