I am trying to calculate the angle for an arrow on a ball, based on the position where it is going to.
The arrow moves, but in a total unexplainable direction, can anybody give some pointers?
Codepen available: Codepen
I added the full code on here (EDITED based on input):
I added a step to make the difference bigger for the angle calculation, not sure if that is the right way to go, but it seems a bit more functional. Plus added the +/- 90 in the angle method, but that doesnt seem to fix it. It is still feeling odd.
class Throwable {
constructor(){
this.throwObject = null;
this.canDrag = null;
this.initialDiffX = 0;
this.initialDiffY = 0;
this.previousX = 0;
this.previousY = 0;
this.intervalCounter = 0;
}
set x(input) {
this.throwObject.style.left = input + 'px';
}
set y(input) {
this.throwObject.style.top = input + 'px';
}
set rotation(input) {
this.throwObject.style.transform = `rotate(${input}deg)`;
}
init(){
this.throwObject = document.querySelector('.throwable');
this.throwObject.addEventListener('mousedown', this.activateDrag.bind(this));
this.throwObject.addEventListener('mouseup', this.deactivateDrag.bind(this));
document.addEventListener('mousemove', this.drag.bind(this));
}
activateDrag(event) {
this.canDrag = true;
this.initialDiffX = event.clientX - this.throwObject.offsetLeft;
this.initialDiffY = event.clientY - this.throwObject.offsetTop;
}
deactivateDrag() {
this.canDrag = false;
}
drag(event) {
if(this.canDrag === true) {
if(this.intervalCounter >= 30) {
this.intervalCounter = 0;
}
if(this.intervalCounter === 0) {
this.previousX = event.clientX;
this.previousY = event.clientY;
}
this.intervalCounter++;
this.y = event.clientY- this.initialDiffY;
this.x = event.clientX - this.initialDiffX;
this.rotation = this.angle(event.clientX, event.clientY, this.previousX, this.previousY);
}
}
angle(ex, ey, cx, cy) {
var dy = ey - cy;
var dx = ex - cx;
return Math.atan2(dy, dx) * 180 / Math.PI + 90;
}
// Untility
log(logObject) {
let logStr = '';
for(let key in logObject) {
logStr += `${key}: ${logObject[key]}<br>`;
}
document.getElementById('log').innerHTML = logStr;
}
}
let throwable = new Throwable();
throwable.init();
I made a mistake in comparing two different values, I fixed that, it is working way better, still have some odd behavior sometimes, seems like it doesnt know where to go in some points. But working better than before.
Maybe you have some mistakes in your angle function. This works for me:
angle(cx, cy, ex, ey) {
var dy = ey - cy ;
var dx = cx - ex ;
return Math.atan2(dx, dy) * 180 / Math.PI;
}
When you call this.angle() you give it twice this.throwObject.offset..., once directly and once via px and py:
let px = this.throwObject.offsetLeft;
let py = this.throwObject.offsetTop;
this.rotation = this.angle(this.throwObject.offsetLeft, this.throwObject.offsetTop, px, py)
That will result in dx and dy to be 0 in angle() making the result of Math.atan2() unpredictable.
I'm not sure about the rest of your code, but maybe you meant to call angle() like this:
this.rotation = this.angle(this.x, this.y, px, py);
There are a couple small issues that I can see.
First, the angle method is calculating radians in range of -180 to 180 and you want it to be 0 to 360. So after angle calculation you'll want to convert something like this:
angle(ex, ey, cx, cy) {
var dy = ey - cy;
var dx = ex - cx;
var theta = Math.atan2(dy, dx) * 180 / Math.PI;
if (theta < 0) theta += 360; // convert to [0, 360]
return theta;
}
Second, the starting angle of your element at 0 degrees is not the actual 0 degrees calculated by this method due to how js coordinates work. A quick fix is to add 90 degrees to make it match:
set rotation(input) {
this.throwObject.style.transform = `rotate(${input + 90}deg)`;
}
It's still a little janky after these conversion but I think it's a start on the right calculations. My guess is part of the issue is having such close points for calculation.
This happens because there's a difference how angles are measured between Math.atan2() and the CSS rotate transformation.
For us humans it's natural that the 12 o' clock position on an analog clock refers to the angle 0 - same for CSS rotate.
Math.atan2() however measures the angle starting from the horizontal x axis. So depending on your input coordinates it would be the 3 or 9 o' clock position.
There's an easy fix however.
After calculating the angle
Math.atan2(dy, dx) * 180 / Math.PI
just subtract 90 degrees like
Math.atan2(dy, dx) * 180 / Math.PI - 90
What happens when intervalCounter become 0? The previus point moved to the event point, so dy, dx becomes 0 and you have a jitter: -180 + 90, +180 + 90, 0 + 90 as defined in Math.atan2. After that, the previus point is fixed until intervalCounter < 30 and you have some inceasing distance between the previus and event points, so the angle is close to the expected one.
Anyway, this is a bad coordinate filter. You can improve it by implementing simple exponential filtering or by using fixed size (30 in your case) queue for event point.
Related
So I'm stumped. I didn't know trigonometry before this, and I've been learning but nothing seems to be working.
So a few things to note: In html, cartesian origin(0,0) is the top left corner of the screen. DIVS natural rotation is 0deg or ---->this way.
I need to find the x,y point noted by the ? mark in the problem.
$('#wrapper').on('click', function(e){
mouseX = e.pageX;
mouseY= e.pageY;
var angle = getAngle(mouseX,Rocket.centerX,mouseY,Rocket.centerY);
var angleDistance = Math.sqrt((Math.pow((mouseX - (Rocket.left+Rocket.halfX)),2)) + (Math.pow((mouseY-(Rocket.top+Rocket.halfY)),2)));
var cp2Angle = -90 +(angle*2);
var invCP2Angle = 90+ angle;
var cp2Distance = angleDistance*.5;
//Red Line
$(this).append('<div class="line" style="transform-origin:left center;width:'+(Math.round(angleDistance))+'px;top:'+(Rocket.top+Rocket.halfY)+'px;left:'+(Rocket.left+Rocket.halfX)+'px;transform:rotate('+(Math.round(angle))+'deg);"></div>');
//Blue Line
$(this).append('<div class="line" style="background:#0000FF;transform-origin:left center;width:'+Math.round(cp2Distance)+'px;top:'+(mouseY)+'px;left:'+(mouseX)+'px;transform:rotate('+(Math.round(cp2Angle))+'deg);"></div>');
}
function getAngle(x2,x1,y2,y1){
var angle = Math.degrees(Math.atan2(y2-y1,x2-x1));
return angle;
}
Math.degrees = function(radians) {
return (radians * 180) / Math.PI;
};
So this might be confusing. Basically when I click on the page, i calculate the angle between my custom origin and the mouse points using Math.atan2(); I also calculate the distance using Math.sqrt((Math.pow((x2 - x1),2)) + (Math.pow((y2-y1),2)));
The blue line length is half the length of the red line, but the angle changes, based on the angle of the red line.
When the red line angle = 0deg(a flat line), the blue line angle will be -90(or straight up, at red line -45 deg, the blue line will be -180(or flat), and at Red Line -90, the blue line will be -270 deg(or straight down). The formula is -90 +(angle*2)
I need to know the other end point of the blue line. The lines only exist to debug, but the point is needed because I have an animation where I animate a rocket on a bezier curve, and I need to change the control point based on the angle of the mouse click, if there's abetter way to calculate that without trigonometry, then let me know.
I read that the angle is the same as the slope of the line and to find it by using Math.tan(angle in radians). Sometimes the triangle will be a right triangle for instance if the first angle is 0 deg, sometimes it won't be a triangle at all, but a straight line down, for instance if they click -90.
I've also tried polar coordinates thought I wasn't sure which angle to use:
var polarX = mouseX-(cp2Distance * Math.cos(Math.radians(invCP2Angle)));
var polarY = mouseY- (cp2Distance * Math.sin(Math.radians(invCP2Angle)));
I do not know javascript well, so instead of giving you code, I'll just give you the formulae. On the figure below, I give you the conventions used.
x3 = x2 + cos(brownAngle + greenAngle) * d2
y3 = y2 + sin(brownAngle + greenAngle) * d2
If I understand you correctly, you have already d2 = 0.5 * d1, d1, (x2, y2) as well as the angles. This should then just be a matter of plugging these values into the above formulae.
Let A, B and C be the three points.
AB = ( cos(angle1), sin(angle1) ) * length1
B = A + B
BC = ( cos(angle1+angle2), sin(angle1+angle2) ) * length2
C = B + BC
In your case,
A = ( 0, 0 )
angle1 = 31°
length1 = 655
angle2 = 152°
length2 = 328
Then,
C = ( Math.cos(31*Math.PI/180), Math.sin(31*Math.PI/180) ) * 655 +
( Math.cos(152*Math.PI/180), Math.sin(152*Math.PI/180) ) * 328
= ( Math.cos(31*Math.PI/180) * 655 + Math.cos(183*Math.PI/180) * 328,
Math.sin(31*Math.PI/180) * 655 + Math.sin(183*Math.PI/180) * 328 )
= ( 233.8940945603834, 320.1837454184)
I researched google but couldn't find the keywords for search. So I ask here if my algorithm and code is efficient?
http://sketchtoy.com/66429941 (algorithm)
The algoritm is: I have four points which are: north, east, south and west of circle. I check 4 distances (distanceToNorth, distanceToEast, distanceToSouth, distanceToWest). And I find minimum of them so that is the quarter.
Here is the code but it does not seem efficient for me.
(firstQuarter is North, secondQuarter is East and so on..
note: assume that mousemove is inside the circle.
var firstQuarterX = centerX;
var firstQuarterY = centerY - radius;
var secondQuarterX = centerX + radius;
var secondQuarterY = centerY;
var thirdQuarterX = centerX;
var thirdQuarterY = centerY + radius;
var fourthQuarterX = centerX - radius;
var fourthQuarterY = centerY;
var distanceToFirst = Math.sqrt(Math.pow(x-firstQuarterX, 2) + Math.pow(y-firstQuarterY, 2));
var distanceToSecond = Math.sqrt(Math.pow(x-secondQuarterX, 2) + Math.pow(y-secondQuarterY, 2));
var distanceToThird = Math.sqrt(Math.pow(x-thirdQuarterX, 2) + Math.pow(y-thirdQuarterY, 2));
var distanceToFourth = Math.sqrt(Math.pow(x-fourthQuarterX, 2) + Math.pow(y-fourthQuarterY, 2));
var min = Math.min(distanceToFirst, distanceToSecond, distanceToThird, distanceToFourth);
var numbers = [distanceToFirst, distanceToSecond, distanceToThird, distanceToFourth];
var index = numbers.indexOf(min); // it will give 0 or 1 or 2 or 3
var quarter = index + 1;
Observe that the boundaries between your quarters lie along the lines with equations y = x and y = -x, relative to an origin at the center of the circle. You can use those to evaluate which quarter each point falls in.
If your point is (x, y), then its coordinates relative to the center of the circle are xRelative = x - centerX and yRelative = y - centerY. Then
your point is in the first (south in your code) quarter if yRelative < 0 and Math.abs(xRelative) < -yRelative
your point is in the second (east) quarter if xRelative > 0 and Math.abs(yRelative) < xRelative
your point is in the third (north) quarter if yRelative > 0 and Math.abs(xRelative) < yRelative
your point is in the fourth (west) quarter if xRelative < 0 and Math.abs(yRelative) < -xRelative
I leave it to you to determine to which quarter to assign points that fall exactly on a boundary. Also, you can implement a little decision tree based on those criteria if you prefer; that should be a little more efficient then testing each criterion in turn.
Not so sure but I think this might work. Math.atan2(CenterY - y, CenterX - x) * 180 / Math.PI gives the apparent angle between the points. Do the remaining math to figure out the quarter.
What about something like:
return x>centerX?(y>centerY?"Quad 2":"Quad 1"):(y>centerY?"Quad 3":"Quad 4");
Less graceful, more slim.
For more efficient algorithm, you can compute the quadrant just by analyzing the signs of dx + dy and dx - dy quantities (dx, dy being x, y minus centerX, centerY respectively) (I presume that as your animation shows, your quadrants are rotated by 45 degrees against 'standard' quadrants.
I have been wrestling with rendering an animation that fires a projectile accurately from an "enemy" node to a "player" node in a 2D 11:11 grid (0:0 = top-left) in JS/Canvas. After a lot of reading up I've managed to get the shots close, but not quite bang on. I think my velocity function is a little out but I really don't know why. This is the trigonometric function:
this.getVelocityComponents = function(speed){
// loc (location of enemy actor) = array(2) [X_coord, Y_coord]
// des (destination (ie. player in this instance)) = array(2) [X_coord, Y_coord]
var i, sum, hyp, output = [], dis = [];
var higher = false;
for (i in loc) {
sum = 0;
if (loc[i] > des[i])
sum = loc[i] - des[i];
if (loc[i] < des[i])
sum = des[i] - loc[i];
dis.push(sum);
}
hyp = Math.sqrt(Math.pow(dis[X], 2) + Math.pow(dis[Y], 2));
if (dis[X] > dis[Y]) {
output[X] = (speed * Math.cos(dis[X]/hyp))
output[Y] = (speed * Math.sin(dis[Y]/hyp))
} else if (dis[X] < dis[Y]) {
output[X] = (speed * Math.cos(dis[Y]/hyp))
output[Y] = (speed * Math.sin(dis[X]/hyp))
}
return output;
}
and this is the instruction that tells the X and the Y of the projectile frame to advance:
var distance = [];
for (i in loc) {
var sum = 0;
if (loc[i] > des[i])
sum = loc[i] - des[i];
if (loc[i] < des[i])
sum = des[i] - loc[i];
distance.push(sum);
}
if (distance[X] > distance[Y]) {
frm[X] += (loc[X] < des[X]) ? v[X] : -v[X];
frm[Y] += (loc[Y] < des[Y]) ? v[Y] : -v[Y];
} else {
frm[Y] += (loc[Y] < des[Y]) ? v[X] : -v[X];
frm[X] += (loc[X] < des[X]) ? v[Y] : -v[Y];
}
Below is a screenshot. Blue is player, pink enemy and the yellow circles are projectiles
as you can see, it's almost on the mark.
Have I done something wrong? what do I need to do?
To calculate the direction from enemy to player you can simplify the calculations a little.
Find direction angle
var diffX = Player.x - Enemy.x, // difference in position
diffY = Player.y - Enemy.y,
angle = Math.atan2(diffY, diffX); // atan2 will give the angle in radians
Notice also difference for Y comes first for atan2 as canvas is oriented 0° pointing right.
Velocity vector
Then calculate the velocity vector using angle and speed:
// calculate velocity vector
var speed = 8,
vx = Math.cos(angle) * speed, // angle x speed
vy = Math.sin(angle) * speed;
You might want to consider using time as a factor if that is important. You can see my answer from a while back here for an example on this.
Demo
Using these calculations you will be able to always "hit" the player with the projectile (reload demo to change enemy position to random y):
var ctx = document.querySelector("canvas").getContext("2d"),
Player = {
x: 470,
y: 75
},
Enemy = {
x: 100,
y: Math.random() * 150 // reload demo to change y-position
};
// calculate angle
var diffX = Player.x - Enemy.x,
diffY = Player.y - Enemy.y,
angle = Math.atan2(diffY, diffX);
// calculate velocity vector
var speed = 8,
vx = Math.cos(angle) * speed, // angle x speed
vy = Math.sin(angle) * speed,
x = Enemy.x, // projectil start
y = Enemy.y + 50;
// render
(function loop() {
ctx.clearRect(0, 0, 500, 300);
ctx.fillRect(Player.x, Player.y, 30, 100);
ctx.fillRect(Enemy.x, Enemy.y, 30, 100);
ctx.fillRect(x - 3, y -3, 6, 6);
x += vx;
y += vy;
if (x < 500) requestAnimationFrame(loop);
})();
<canvas width=500 height=300></canvas>
The solution is much simpler than that.
What should you do ?
1) compute the vector that leads from you enemy to the player. That will be the shooting direction.
2) normalize the vector : meaning you build a vector that has a length of 1, with the same direction.
3) multiply that vector by your speed : now you have a correct speed vector, with the right norm, aimed at the player.
Below some code to help you understand :
function spawnBullet(enemy, player) {
var shootVector = [];
shootVector[0] = player[0] - enemy[0];
shootVector[1] = player[1] - enemy[1];
var shootVectorLength = Math.sqrt(Math.pow(shootVector[0], 2) + Math.pow(shootVector[1],2));
shootVector[0]/=shootVectorLength;
shootVector[1]/=shootVectorLength;
shootVector[0]*=bulletSpeed;
shootVector[1]*=bulletSpeed;
// ... here return an object that has the enemy's coordinate
// and shootVector as speed
}
Then, since you don't use time in your computations (!! wrooong !! ;-) ) you will make the bullet move with the straightforward :
bullet[0] += bullet.speed[0];
bullet[1] += bullet.speed[1];
Now the issue with fixed-step is that your game will run, say, twice slower on a 30fps device than on a 60fps device. The solution is to compute how much time elapsed since the last refresh, let's call this time 'dt'. Using that time will lead you to an update like :
bullet[0] += dt * bullet.speed[0];
bullet[1] += dt * bullet.speed[1];
and now you'll be framerate-agnostic, your game will feel the same on any device.
I'm using the following functions to track mouse movement and rotate an object:
function getAngle(dx, dy) {
var angle
if (dx != 0) {
var radians = Math.atan(dy / dx) + (dx < 0 ? Math.PI : 0)
angle = radiansToDegrees(radians);
if (angle < 0) angle += 360;
} else {
angle = dy > 0 ? 90 : 270;
}
return angle;
}
function getAngleBetweenPoints(p1, p2) {
var dx = p1.x - p2.x
var dy = p1.y - p2.y
return getAngle(dx, dy)
}
$(document).mousemove(function (e) {
if (selectionBounds) {
var midpoint = new pe.Classes.Point(selectionBounds.Left + (selectionBounds.Width / 2), selectionBounds.Top + (selectionBounds.Height / 2));
var mousepoint = new pe.Classes.Point(e.pageX, e.pageY);
var angle = getAngleBetweenPoints(midpoint, mousepoint);
if (lastAngle) {
var diff = angle - lastAngle;
rotate(degreesToRadians(diff));
}
lastAngle = angle;
}
});
This works well, as long as I move the mouse slowly, and as long as the mouse doesn't get too close to the origin (midpoint). Moving too quickly causes additional spin rotations, and coming close to the origin causes unexpected changes of direction.
How can I fix this code? I really just need to know which direction the mouse is moving in (clockwise or anti-clockwise), as I can get an idea of the speed just from the change in mousepoint and then update the rotation based on that.
There are literally dozens of SO threads on topics related to this (How to get the direction (angle) of rectangle after rotating it from a pivot point, How to get cardinal mouse direction from mouse coordinates, Moving a rotated element in the direction of the rotation in JavaScript) - but I haven't been able to find anything that can answer this question, except one comment referring to this requiring the cross product, which I didn't fully understand.
http://jsfiddle.net/wRexz/3/ (click and drag to rotate the rectangle)
var angle = 0, sp = startpoint, mp = midpoint;
var p = {x:e.offsetX, y:e.offsetY};
var sAngle = Math.atan2((sp.y-mp.y),(sp.x - mp.x));
var pAngle = Math.atan2((p.y-mp.y),(p.x - mp.x));
angle = (pAngle - sAngle) * 180/Math.PI;
$("#display").text(angle);
$('#rotateme').css({ rotate: '+=' + angle });
startpoint = {x:p.x, y:p.y};
The concept here is basic trig. You find the angle from 0 of the "start point" and do the same for the "end point" or "current point". Subtract the first from the second, and that is your "delta angle".
You will still get erratic behavior around the midpoint, due to the nature of how rapidly the angles can change. One solution to this is stopping rotation when within a certain distance of the midpoint.
Basically I'm asking this question for JavaScript: Calculate Bounding box coordinates from a rotated rectangle
In this case:
iX = Width of rotated (blue) HTML element
iY = Height of rotated (blue) HTML element
bx = Width of Bounding Box (red)
by = Height of Bounding Box (red)
x = X coord of Bounding Box (red)
y = Y coord of Bounding Box (red)
iAngle/t = Angle of rotation of HTML element (blue; not shown but
used in code below), FYI: It's 37 degrees in this example (not that it matters for the example)
How does one calculate the X, Y, Height and Width of a bounding box (all the red numbers) surrounding a rotated HTML element (given its width, height, and Angle of rotation) via JavaScript? A sticky bit to this will be getting the rotated HTML element (blue box)'s original X/Y coords to use as an offset somehow (this is not represented in the code below). This may well have to look at CSS3's transform-origin to determine the center point.
I've got a partial solution, but the calculation of the X/Y coords is not functioning properly...
var boundingBox = function (iX, iY, iAngle) {
var x, y, bx, by, t;
//# Allow for negetive iAngle's that rotate counter clockwise while always ensuring iAngle's < 360
t = ((iAngle < 0 ? 360 - iAngle : iAngle) % 360);
//# Calculate the width (bx) and height (by) of the .boundingBox
//# NOTE: See https://stackoverflow.com/questions/3231176/how-to-get-size-of-a-rotated-rectangle
bx = (iX * Math.sin(iAngle) + iY * Math.cos(iAngle));
by = (iX * Math.cos(iAngle) + iY * Math.sin(iAngle));
//# This part is wrong, as it's re-calculating the iX/iY of the rotated element (blue)
//# we want the x/y of the bounding box (red)
//# NOTE: See https://stackoverflow.com/questions/9971230/calculate-rotated-rectangle-size-from-known-bounding-box-coordinates
x = (1 / (Math.pow(Math.cos(t), 2) - Math.pow(Math.sin(t), 2))) * (bx * Math.cos(t) - by * Math.sin(t));
y = (1 / (Math.pow(Math.cos(t), 2) - Math.pow(Math.sin(t), 2))) * (-bx * Math.sin(t) + by * Math.cos(t));
//# Return an object to the caller representing the x/y and width/height of the calculated .boundingBox
return {
x: parseInt(x), width: parseInt(bx),
y: parseInt(y), height: parseInt(by)
}
};
I feel like I am so close, and yet so far...
Many thanks for any help you can provide!
TO HELP THE NON-JAVASCRIPTERS...
Once the HTML element is rotated, the browser returns a "matrix transform" or "rotation matrix" which seems to be this: rotate(Xdeg) = matrix(cos(X), sin(X), -sin(X), cos(X), 0, 0); See this page for more info.
I have a feeling this will enlighten us on how to get the X,Y of the bounding box (red) based solely on the Width, Height and Angle of the rotated element (blue).
New Info
Humm... interesting...
Each browser seems to handle the rotation differently from an X/Y perspective! FF ignores it completely, IE & Opera draw the bounding box (but its properties are not exposed, ie: bx & by) and Chrome & Safari rotate the rectangle! All are properly reporting the X/Y except FF. So... the X/Y issue seems to exist for FF only! How very odd!
Also of note, it seems that $(document).ready(function () {...}); fires too early for the rotated X/Y to be recognized (which was part of my original problem!). I am rotating the elements directly before the X/Y interrogation calls in $(document).ready(function () {...}); but they don't seem to update until some time after(!?).
When I get a little more time, I will toss up a jFiddle with the example, but I'm using a modified form of "jquery-css-transform.js" so I have a tiny bit of tinkering before the jFiddle...
So... what's up, FireFox? That ain't cool, man!
The Plot Thickens...
Well, FF12 seems to fix the issue with FF11, and now acts like IE and Opera. But now I am back to square one with the X/Y, but at least I think I know why now...
It seems that even though the X/Y is being reported correctly by the browsers for the rotated object, a "ghost" X/Y still exists on the un-rotated version. It seems as though this is the order of operations:
Starting with an un-rotated element at an X,Y of 20,20
Rotate said element, resulting in the reporting of X,Y as 15,35
Move said element via JavaScript/CSS to X,Y 10,10
Browser logically un-rotates element back to 20,20, moves to 10,10 then re-rotates, resulting in an X,Y of 5,25
So... I want the element to end up at 10,10 post rotation, but thanks to the fact that the element is (seemingly) re-rotated post move, the resulting X,Y differs from the set X,Y.
This is my problem! So what I really need is a function to take the desired destination coords (10,10), and work backwards from there to get the starting X,Y coords that will result in the element being rotated into 10,10. At least I know what my problem is now, as thanks to the inner workings of the browsers, it seems with a rotated element 10=5!
I know this is a bit late, but I've written a fiddle for exactly this problem, on an HTML5 canvas:
http://jsfiddle.net/oscarpalacious/ZdQKg/
I hope somebody finds it useful!
I'm actually not calculating your x,y for the upper left corner of the container. It's calculated as a result of the offset (code from the fiddle example):
this.w = Math.sin(this.angulo) * rotador.h + Math.cos(this.angulo) * rotador.w;
this.h = Math.sin(this.angulo) * rotador.w + Math.cos(this.angulo) * rotador.h;
// The offset on a canvas for the upper left corner (x, y) is
// given by the first two parameters for the rect() method:
contexto.rect(-(this.w/2), -(this.h/2), this.w, this.h);
Cheers
Have you tried using getBoundingClientRect() ?
This method returns an object with current values of "bottom, height, left, right, top, width" considering rotations
Turn the four corners into vectors from the center, rotate them, and get the new min/max width/height from them.
EDIT:
I see where you're having problems now. You're doing the calculations using the entire side when you need to be doing them with the offsets from the center of rotation. Yes, this results in four rotated points (which, strangely enough, is exactly as many points as you started with). Between them there will be one minimum X, one maximum X, one minimum Y, and one maximum Y. Those are your bounds.
My gist can help you
Bounding box of a polygon (rectangle, triangle, etc.):
Live demo https://jsfiddle.net/Kolosovsky/tdqv6pk2/
let points = [
{ x: 125, y: 50 },
{ x: 250, y: 65 },
{ x: 300, y: 125 },
{ x: 175, y: 175 },
{ x: 100, y: 125 },
];
let minX = Math.min(...points.map(point => point.x));
let minY = Math.min(...points.map(point => point.y));
let maxX = Math.max(...points.map(point => point.x));
let maxY = Math.max(...points.map(point => point.y));
let pivot = {
x: maxX - ((maxX - minX) / 2),
y: maxY - ((maxY - minY) / 2)
};
let degrees = 90;
let radians = degrees * (Math.PI / 180);
let cos = Math.cos(radians);
let sin = Math.sin(radians);
function rotatePoint(pivot, point, cos, sin) {
return {
x: (cos * (point.x - pivot.x)) - (sin * (point.y - pivot.y)) + pivot.x,
y: (sin * (point.x - pivot.x)) + (cos * (point.y - pivot.y)) + pivot.y
};
}
let boundingBox = {
x1: Number.POSITIVE_INFINITY,
y1: Number.POSITIVE_INFINITY,
x2: Number.NEGATIVE_INFINITY,
y2: Number.NEGATIVE_INFINITY,
};
points.forEach((point) => {
let rotatedPoint = rotatePoint(pivot, point, cos, sin);
boundingBox.x1 = Math.min(boundingBox.x1, rotatedPoint.x);
boundingBox.y1 = Math.min(boundingBox.y1, rotatedPoint.y);
boundingBox.x2 = Math.max(boundingBox.x2, rotatedPoint.x);
boundingBox.y2 = Math.max(boundingBox.y2, rotatedPoint.y);
});
Bounding box of an ellipse:
Live demo https://jsfiddle.net/Kolosovsky/sLc7ynd1/
let centerX = 350;
let centerY = 100;
let radiusX = 100;
let radiusY = 50;
let degrees = 200;
let radians = degrees * (Math.PI / 180);
let radians90 = radians + Math.PI / 2;
let ux = radiusX * Math.cos(radians);
let uy = radiusX * Math.sin(radians);
let vx = radiusY * Math.cos(radians90);
let vy = radiusY * Math.sin(radians90);
let width = Math.sqrt(ux * ux + vx * vx) * 2;
let height = Math.sqrt(uy * uy + vy * vy) * 2;
let x = centerX - (width / 2);
let y = centerY - (height / 2);