I'm trying to rotate two rectangles the same amount around the same point. The point is arbitrary, so for simplicity, I'm using the top-left (0, 0)
Unfortunately, the result seems slightly off, and I'm not sure what's causing it. Here is a full reproduction of the issue:
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
class Rectangle {
constructor(x, y, w, h, theta) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.theta = theta;
}
}
function drawRectangle(r) {
ctx.beginPath();
ctx.rect(r.x, r.y, r.w, r.h);
ctx.stroke();
}
function degreesToRadians(degrees) { return degrees * (Math.PI / 180); }
function rotateCanvas(radians, centerX, centerY) {
ctx.translate(centerX, centerY);
ctx.rotate(radians);
ctx.translate(-centerX, -centerY);
}
function drawRotatedRectangle(r) {
let rXCenter = r.x + (r.w / 2);
let rYCenter = r.y + (r.h / 2);
alert(rXCenter);
rotateCanvas(r.theta, rXCenter, rYCenter);
drawRectangle(r);
rotateCanvas(-r.theta, rXCenter, rYCenter);
}
let r1 = new Rectangle(100, 52, 90, 30, degreesToRadians(-20));
let r2 = new Rectangle(140, 80, 25, 25, degreesToRadians(10));
function simpleRotate(r, theta) {
let transX = Math.cos(theta) * r.x - Math.sin(theta) * r.y;
let transY = Math.sin(theta) * r.x + Math.cos(theta) * r.y;
return new Rectangle(transX, transY, r.w, r.h, r.theta + theta);
}
drawRotatedRectangle(r1);
drawRotatedRectangle(r2);
let r1AABB = simpleRotate(r1, -r1.theta);
let r2Rotate = simpleRotate(r2, -r1.theta);
ctx.strokeStyle = "#ff0000";
drawRotatedRectangle(r1AABB);
drawRotatedRectangle(r2Rotate);
body { margin: 0; overflow: hidden; }
<canvas width="600" height="600"></canvas>
The black rectangles are the two rectangles before being rotated, and the red rectangles are the two rectangles after being rotated.
As you can see, the two black rectangles are touching (colliding) before being rotated. Then, I rotate them both by the same amount around the same point (0, 0). However, afterwards they are no longer touching (as you can see the red rectangles are no longer colliding.
Why is this? I followed this code for rotating a point, but I seem to be getting inaccurate results.
If I take a screenshot of the black rectangles, open it up an image editor, box select them, and rotate them, then they stay together (colliding). How can I emulate this in my code example posted above?
This may let it work as you expect.
function simpleRotate(r, theta) {
let transX = Math.cos(theta) * (r.x + r.w / 2) - Math.sin(theta) * (r.y + r.h / 2) - r.w / 2;
let transY = Math.sin(theta) * (r.x + r.w / 2) + Math.cos(theta) * (r.y + r.h / 2) - r.h / 2;
return new Rectangle(transX, transY, r.w, r.h, r.theta + theta);
}
Other otherwise, if you'd like to change center, use following form.
function simpleRotate(r, theta, centerX, centerY) {
let transX = Math.cos(theta) * (r.x + r.w / 2 - centerX) - Math.sin(theta) * (r.y + r.h / 2 - centerY) - r.w / 2 + centerX;
let transY = Math.sin(theta) * (r.x + r.w / 2 - centerX) + Math.cos(theta) * (r.y + r.h / 2 - centerY) - r.h / 2 + centerY;
return new Rectangle(transX, transY, r.w, r.h, r.theta + theta);
}
Then
let r1AABB = simpleRotate(r1, -r1.theta, 145, 67);
let r2Rotate = simpleRotate(r2, -r1.theta, 145, 67);
How these work
In general, 2d rotational transform with offset consists of translation and rotation. Then, in that setup rotation is to be applied via drawRotatedRectangle() afterwards. Therefore all what you have to do is to compose appropriate translation. Inspecting simpleRotate() function carefully, you'll notice what's actually done there is calculation of translation (transX, transY ... though sin/cos are being used).
Then, as you know, since drawRotatedRectangle() rotates the rectangle around its center, by composing translation to move the center to the appropriate position, you can obtain rotational transform you need.
Initial state → Translate each item by simpleRotate() → Rotate each item by drawRotatedRectangle() → Final state.
Related
I want to make a gradient that covers the whole canvas whatever the angle of it.
So I used a method found on a Stack Overflow post which is finally incorrect. The solution is almost right but, in fact, the canvas is not totally covered by the gradient.
It is this answer: https://stackoverflow.com/a/45628098/5594331
(You have to look at the last point named "Example of best fit.")
In my code example below, the yellow part should not be visible because it should be covered by the black and white gradient. This is mostly the code written in Blindman67's answer with some adjustments to highlight the problem.
I have drawn in green the control points of the gradient. With the right calculations, these should be stretched to the edges of the canvas at any angle.
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
function bestFitGradient(angle){
var dist = Math.sqrt(w * w + h * h) / 2; // get the diagonal length
var diagAngle = Math.asin((h / 2) / dist); // get the diagonal angle
// Do the symmetry on the angle (move to first quad
var a1 = ((angle % (Math.PI *2))+ Math.PI*4) % (Math.PI * 2);
if(a1 > Math.PI){ a1 -= Math.PI }
if(a1 > Math.PI / 2 && a1 <= Math.PI){ a1 = (Math.PI / 2) - (a1 - (Math.PI / 2)) }
// get angles from center to edges for along and right of gradient
var ang1 = Math.PI/2 - diagAngle - Math.abs(a1);
var ang2 = Math.abs(diagAngle - Math.abs(a1));
// get distance from center to horizontal and vertical edges
var dist1 = Math.cos(ang1) * h;
var dist2 = Math.cos(ang2) * w;
// get the max distance
var scale = Math.max(dist2, dist1) / 2;
// get the vector to the start and end of gradient
var dx = Math.cos(angle) * scale;
var dy = Math.sin(angle) * scale;
var x0 = w / 2 + dx;
var y0 = h / 2 + dy;
var x1 = w / 2 - dx;
var y1 = h / 2 - dy;
// create the gradient
const g = ctx.createLinearGradient(x0, y0, x1, y1);
// add colours
g.addColorStop(0, "yellow");
g.addColorStop(0, "white");
g.addColorStop(.5, "black");
g.addColorStop(1, "white");
g.addColorStop(1, "yellow");
return {
g: g,
x0: x0,
y0: y0,
x1: x1,
y1: y1
};
}
function update(timer){
var r = bestFitGradient(timer / 1000);
// draw gradient
ctx.fillStyle = r.g;
ctx.fillRect(0,0,w,h);
// draw points
ctx.lineWidth = 3;
ctx.fillStyle = '#00FF00';
ctx.strokeStyle = '#FF0000';
ctx.beginPath();
ctx.arc(r.x0, r.y0, 5, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.arc(r.x1, r.y1, 5, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.fill();
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
border : 2px solid red;
}
<canvas id="canvas" width="300" height="200"></canvas>
In this fiddle there is a function that calculates the distance between a rotated line and a point:
function distanceToPoint(px, py, angle) {
const cx = width / 2;
const cy = height / 2;
return Math.abs((Math.cos(angle) * (px - cx)) - (Math.sin(angle) * (py - cy)));
}
Which is then used to find the maximum distance between the line and the corner points (only two points are considered, because the distances to the other two points are mirrored):
const dist = Math.max(
distanceToPoint(0, 0, angle),
distanceToPoint(0, height, angle)
);
Which can be used to calculate offset points for the end of the gradient:
const ox = Math.cos(angle) * dist;
const oy = Math.sin(angle) * dist;
const gradient = context.createLinearGradient(
width / 2 + ox,
height / 2 + oy,
width / 2 - ox,
height / 2 - oy
)
I can easily draw/rotate a line of given length around z-axis, y-axis or x-axis.
const ctx = document.getElementById("drawing").getContext("2d");
ctx.scale(1, -1); ctx.translate(0, -ctx.canvas.height); // flip canvas
const length = 50;
let t = 0;
//x = r sin(q) cos(f)
//y = r sin(q) sin(f)
//z = r cos(q)
function rotate_around_zaxis() {
const x1=50; const y1=50;
const line_angle = 20 * Math.PI/180;
const angle = 0;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x1 + length * Math.sin(line_angle) * Math.cos(angle + t),
y1 + length * Math.sin(line_angle) * Math.sin(angle + t));
ctx.stroke();
}
function rotate_around_yaxis() {
const x1=150; const y1=50;
const line_angle = 20 * Math.PI/180;
const angle = 0;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x1 + length * Math.sin(line_angle) * Math.cos(angle + t),
y1 + length /*Math.sin(angle + t)*/ * Math.cos(line_angle) );
ctx.stroke();
}
function rotate_around_xaxis() {
const x1=250; const y1=50;
const line_angle = 20 * Math.PI/180;
const angle = 0;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x1 + length /**Math.sin(angle + t)*/ * Math.cos(line_angle),
y1 + length * Math.sin(line_angle) * Math.sin(angle + t));
ctx.stroke();
}
function line(x1, y1, x2, y2) {
ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke();
}
function animate() {
ctx.clearRect(0,0,300,100);
line(0, 50, 100, 50);line(50, 0, 50, 100);rotate_around_zaxis();
line(105, 50, 200, 50);line(150, 0, 150, 100);rotate_around_yaxis();
line(205, 50, 300, 50);line(250, 0, 250, 100);rotate_around_xaxis();
t+=Math.PI/180;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
<canvas id="drawing" width=300 height=100></canvas>
However I can only do this around straight up/down y-axis degree or straight x-axis. I can not figure out rotation around an arbitrary line in space. In other words I don't know how to move it to any point in 3d space between x,y and z/.
I couldn't grasp rotation matrices. Rotation calculation on many places is given like this.
x' = x * cos(angle) - y * sin(angle);
y' = x * sin(angle) + y * cos(angle);
I don't understand where this equation fits in what I am trying to do.
I want to be able to rotate the line in cone like shape around any axis. How do I achieve this?
Well, theoretically, if the first example works, then to get the second one, you can apply the first one, and then just translate everything -60°
let newX2 = Math.cos(-60 / Math.PI/180) * x2
let newY2 = Math.sin(-60 / Math.PI/180) * y2
same for x1 and y1
I realize this is a simple Trigonometry question, but my high school is failing me right now.
Given an angle, that I have converted into radians to get the first point. How do I figure the next two points of the triangle to draw on the canvas, so as to make a small triangle always point outwards to the circle. So lets say Ive drawn a circle of a given radius already. Now I want a function to plot a triangle that sits on the edge of the circle inside of it, that points outwards no matter the angle. (follows the edge, so to speak)
function drawPointerTriangle(ctx, angle){
var radians = angle * (Math.PI/180)
var startX = this.radius + this.radius/1.34 * Math.cos(radians)
var startY = this.radius - this.radius/1.34 * Math.sin(radians)
// This gives me my starting point on the outer edge of the circle, plotted at the angle I need
ctx.moveTo(startX, startY);
// HOW DO I THEN CALCULATE x1,y1 and x2, y2. So that no matter what angle I enter into this function, the arrow/triangle always points outwards to the circle.
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
}
Example
You don't say what type of triangle you want to draw so I suppose that it is an equilateral triangle.
Take a look at this image (credit here)
I will call 3 points p1, p2, p3 from top right to bottom right, counterclockwise.
You can easily calculate the coordinate of three points of the triangle in the coordinate system with the origin is coincident with the triangle's centroid.
Given a point belongs to the edge of the circle and the point p1 that we just calculated, we can calculate parameters of the translation from our main coordinate system to the triangle's coordinate system. Then, we just have to translate the coordinate of two other points back to our main coordinate system. That is (x1,y1) and (x2,y2).
You can take a look at the demo below that is based on your code.
const w = 300;
const h = 300;
function calculateTrianglePoints(angle, width) {
let r = width / Math.sqrt(3);
let firstPoint = [
r * Math.cos(angle),
r * Math.sin(angle),
]
let secondPoint = [
r * Math.cos(angle + 2 * Math.PI / 3),
r * Math.sin(angle + 2 * Math.PI / 3),
]
let thirdPoint = [
r * Math.cos(angle + 4 * Math.PI / 3),
r * Math.sin(angle + 4 * Math.PI / 3),
]
return [firstPoint, secondPoint, thirdPoint]
}
const radius = 100
const triangleWidth = 20;
function drawPointerTriangle(ctx, angle) {
var radians = angle * (Math.PI / 180)
var startX = radius * Math.cos(radians)
var startY = radius * Math.sin(radians)
var [pt0, pt1, pt2] = calculateTrianglePoints(radians, triangleWidth);
var delta = [
startX - pt0[0],
startY - pt0[1],
]
pt1[0] = pt1[0] + delta[0]
pt1[1] = pt1[1] + delta[1]
pt2[0] = pt2[0] + delta[0]
pt2[1] = pt2[1] + delta[1]
ctx.beginPath();
// This gives me my starting point on the outer edge of the circle, plotted at the angle I need
ctx.moveTo(startX, startY);
[x1, y1] = pt1;
[x2, y2] = pt2;
// HOW DO I THEN CALCULATE x1,y1 and x2, y2. So that no matter what angle I enter into this function, the arrow/triangle always points outwards to the circle.
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.closePath();
ctx.fillStyle = '#FF0000';
ctx.fill();
}
function drawCircle(ctx, radius) {
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = '#000';
ctx.fill();
}
function clear(ctx) {
ctx.fillStyle = '#fff';
ctx.fillRect(-w / 2, -h / 2, w, h);
}
function normalizeAngle(pointCoordinate, angle) {
const [x, y] = pointCoordinate;
if (x > 0 && y > 0) return angle;
else if (x > 0 && y < 0) return 360 + angle;
else if (x < 0 && y < 0) return 180 - angle;
else if (x < 0 && y > 0) return 180 - angle;
}
function getAngleFromPoint(point) {
const [x, y] = point;
if (x == 0 && y == 0) return 0;
else if (x == 0) return 90 * (y > 0 ? 1 : -1);
else if (y == 0) return 180 * (x >= 0 ? 0: 1);
const radians = Math.asin(y / Math.sqrt(
x ** 2 + y ** 2
))
return normalizeAngle(point, radians / (Math.PI / 180))
}
document.addEventListener('DOMContentLoaded', function() {
const canvas = document.querySelector('canvas');
const angleText = document.querySelector('.angle');
const ctx = canvas.getContext('2d');
ctx.translate(w / 2, h / 2);
drawCircle(ctx, radius);
drawPointerTriangle(ctx, 0);
canvas.addEventListener('mousemove', _.throttle(function(ev) {
let mouseCoordinate = [
ev.clientX - w / 2,
ev.clientY - h / 2
]
let degAngle = getAngleFromPoint(mouseCoordinate)
clear(ctx);
drawCircle(ctx, radius);
drawPointerTriangle(ctx, degAngle)
angleText.innerText = Math.floor((360 - degAngle)*100)/100;
}, 15))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
<canvas width=300 height=300></canvas>
<div class="angle">0</div>
reduce the radius, change the angle and call again cos/sin:
function drawPointerTriangle(ctx, angle)
{
var radians = angle * (Math.PI/180);
var radius = this.radius/1.34;
var startX = this.center.x + radius * Math.cos(radians);
var startY = this.center.y + radius * Math.sin(radians);
ctx.moveTo(startX, startY);
radius *= 0.9;
radians += 0.1;
var x1 = this.center.x + radius * Math.cos(radians);
var y1 = this.center.y + radius * Math.sin(radians);
radians -= 0.2;
var x1 = this.center.x + radius * Math.cos(radians);
var y1 = this.center.y + radius * Math.sin(radians);
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(startX, startY);
}
the resulting triangle's size is proportional to the size of the circle.
in case you need an equilateral, fixed size triangle, use this:
//get h by pythagoras
h = sqrt( a^2 - (a/2)^2 );)
//get phi using arcustangens:
phi = atan( a/2, radius-h );
//reduced radius h by pythagoras:
radius = sqrt( (radius-h)^2 + (a/2)^2 );
radians += phi;
...
radians -= 2*phi;
...
I created some Functions, these will draw Rectangles, Circles, Hexagons etc.
One of them looks like this:
rotation = 45;
function hex(hex_sides, hex_size, hex_color){
x = ctx.canvas.width/2;
y = ctx.canvas.height/2;
ctx.save();
ctx.rotate(rotation*Math.PI/180);
ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
ctx.moveTo(x + hex_size * Math.cos(0), y + hex_size * Math.sin(0));
ctx.restore();
for (i = 0; i < hex_sides+1; i++) {
ctx.lineTo(x + hex_size * Math.cos(i * 2 * Math.PI / hex_sides), y + hex_size * Math.sin(i * 2 * Math.PI / hex_sides));
}
ctx.strokeStyle = hex_color;
ctx.stroke();
}
Now i call the Functions to Draw the Shapes inside my animation loop.
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
circle(200);
circle(220);
hex(6, 180, "#fff");
rotation += 0.4;
requestAnimationFrame(loop);
}
I'm incrementing the var rotation inside the loop but it does not rotate the whole shape but just one line of it instead. Other Shapes i cant get to rotate at all.
I think i got a wrong approach, maybe because of my confusion about .save() and .restore() or .beginPath() and .closePath().
In General the behaviour is very strange when i start to use .translate() and .rotate()
The entire Code is here.
UPDATE
It is definitely something about this line:
ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
Somehow it does not translate correctly. The rotation now happens almost around the middle but i want the Object to rotate around its own axis.
I changed the hex() function to:
ctx.save();
ctx.translate(ctx.canvas.width/2, ctx.canvas.height/2);
ctx.rotate(rotation*Math.PI/180);
ctx.moveTo(x + hex_size * Math.cos(0), y + hex_size * Math.sin(0));
for (i = 0; i < hex_sides+1; i++) {
ctx.lineTo(x + hex_size * Math.cos(i * 2 * Math.PI / hex_sides), y + hex_size * Math.sin(i * 2 * Math.PI / hex_sides));
}
ctx.strokeStyle = hex_color;
ctx.stroke();
ctx.restore();
I'm trying to re-size a div element while dragging from top right
or bottom left corners.
In order to calculate the new width and height, i need to know the other
two points on the rectangle
how can I get this values given only two point and the rotation degree?
please view the image I've added to fully understand this issue
plus, the div can be also rotated (centered origin)
to clarify my question:
the aim is to resize a div by dragging the cursor of the mouse from top right corner to bottom left. and then to resize the image so the width will be the distance between mouseX to left side. and the height will be from mouseY to the bottom side. for this i nedd to calculate both top left corner and bottom right corner as the mouse cursor moves along.
thank you.
Knowing two opposite corner points as absolute coordinates, and the angle. The (x1,y1)-(x3,y3) is essentially a rotated line representing the diagonal of the rectangle, so we can do:
Find its midpoint and length of segment (midpoint to a corner)
"Unrotate" the two points around the midpoint
Use abs() with the diffs to get the width and height
The essential code
// find center point (origin) using linear interpolation
var mx = x1 + (x3 - x1) * 0.5,
my = y1 + (y3 - y1) * 0.5,
cos = Math.cos(-angle), sin = Math.sin(-angle);
// unrotate known points (using negative of known angle)
var x1u = cos * (x1-mx) - sin * (y1-my) + mx,
y1u = sin * (x1-mx) + cos * (y1-my) + my,
x3u = cos * (x3-mx) - sin * (y3-my) + mx,
y3u = sin * (x3-mx) + cos * (y3-my) + my;
// Get width and height:
var width = Math.abs(x3u - x1u),
height = Math.abs(y3u - y1u);
To get the points for the missing corners, just rotate the new points made from a mix of the unrotated points:
cos = Math.cos(angle);
sin = Math.sin(angle);
// Use known coordinates for the new points:
var x2u = x1u,
y2u = y3u,
x4u = x3u,
y4u = y1u;
// rotate new points using angle
var x2 = cos * (x2u-mx) - sin * (y2u-my) + mx,
y2 = sin * (x2u-mx) + cos * (y2u-my) + my,
x4 = cos * (x4u-mx) - sin * (y4u-my) + mx,
y4 = sin * (x4u-mx) + cos * (y4u-my) + my;
Demo with plotting
The demo will calculate the "missing" points, width and height, and show the result for each step. Input angle is to verify that it works regardless.
var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#e00";
document.querySelector("input").addEventListener("change", update);
function update() {
// Test rect: 50,25 - 350, 175, center: 200,200, W: 300, H: 150
// generate x1,y1 - x3,y3 known points so we have something to work with:
var value = typeof this.value !== "undefined" ? +this.value : 30,
angle = value * Math.PI / 180,
x1 = Math.cos(angle) * (50-200) - Math.sin(angle) * (275-200) + 200,
y1 = Math.sin(angle) * (50-200) + Math.cos(angle) * (275-200) + 200,
x3 = Math.cos(angle) * (350-200) - Math.sin(angle) * (125-200) + 200,
y3 = Math.sin(angle) * (350-200) + Math.cos(angle) * (125-200) + 200;
// Initial Visuals: rotated rect, known corner points
ctx.clearRect(0,0,400,400);
ctx.strokeStyle = "#000";
ctx.translate(200,200);
ctx.rotate(angle);
ctx.translate(-200,-200);
ctx.strokeRect(50, 125, 300, 150);
ctx.setTransform(1,0,0,1,0,0);
ctx.fillStyle = "#e00";
ctx.fillRect(x1-2, y1-2, 4, 4); ctx.fillText("x1,y1", x1+5, y1);
ctx.fillRect(x3-2, y3-2, 4, 4); ctx.fillText("x3,y3", x3+5, y3);
// Step 1: find center point (origin)
var mx = x1 + (x3 - x1) * 0.5,
my = y1 + (y3 - y1) * 0.5;
ctx.fillRect(mx-2, my-2, 4, 4); // draw center point
// unrotate known points (negative angle)
var x1u = Math.cos(-angle) * (x1-mx) - Math.sin(-angle) * (y1-my) + mx,
y1u = Math.sin(-angle) * (x1-mx) + Math.cos(-angle) * (y1-my) + my,
x3u = Math.cos(-angle) * (x3-mx) - Math.sin(-angle) * (y3-my) + mx,
y3u = Math.sin(-angle) * (x3-mx) + Math.cos(-angle) * (y3-my) + my;
ctx.fillStyle = "#00c";
ctx.fillRect(x1u-2, y1u-2, 4, 4); ctx.fillText("x1u,y1u", x1u+5, y1u-5);
ctx.fillRect(x3u-2, y3u-2, 4, 4); ctx.fillText("x3u,y3u", x3u+5, y3u);
// To get width and height:
var width = Math.abs(x3u - x1u),
height = Math.abs(y3u - y1u);
ctx.fillText("Size: " + ((width+0.5)|0) + " x " + ((height+0.5)|0), 0, 10);
// Mix known coordinates
var x2u = x1u, y2u = y3u,
x4u = x3u, y4u = y1u;
// show unrotated points
ctx.fillStyle = "#0c0";
ctx.fillRect(x2u-2, y2u-2, 4, 4); ctx.fillText("x2u,y2u", x2u+5, y2u-5);
ctx.fillRect(x4u-2, y4u-2, 4, 4); ctx.fillText("x4u,y4u", x4u+5, y4u);
// draw lines between unrotated points to show we have an actual rectangle
ctx.strokeStyle = "#777"; ctx.beginPath();
ctx.moveTo(x1u, y1u); ctx.lineTo(x2u, y2u);
ctx.lineTo(x3u, y3u); ctx.lineTo(x4u, y4u);
ctx.closePath(); ctx.stroke();
// rotate new points using angle
var x2 = Math.cos(angle) * (x2u-mx) - Math.sin(angle) * (y2u-my) + mx,
y2 = Math.sin(angle) * (x2u-mx) + Math.cos(angle) * (y2u-my) + my,
x4 = Math.cos(angle) * (x4u-mx) - Math.sin(angle) * (y4u-my) + mx,
y4 = Math.sin(angle) * (x4u-mx) + Math.cos(angle) * (y4u-my) + my;
// show new coordinates
ctx.fillStyle = "#f0f";
ctx.fillRect(x2-2, y2-2, 4, 4); ctx.fillText("x2,y2", x2+5, y2);
ctx.fillRect(x4-2, y4-2, 4, 4); ctx.fillText("x4,y4", x4+5, y4);
}
update();
<script src="https://cdn.rawgit.com/epistemex/slider-feedback/master/sliderfeedback.min.js"></script>
Angle: <input type=range min=0 max=360 value=30><br><canvas width=400 height=400></canvas>
I think you should use Trigo for that, but since I'm terrible with those, here is a dumb way without any Maths, to get the absolute positioning of your points.
var tl= document.querySelector('#tl').getBoundingClientRect();
var tr= document.querySelector('#tr').getBoundingClientRect();
var br= document.querySelector('#br').getBoundingClientRect();
var bl= document.querySelector('#bl').getBoundingClientRect();
var pointsList = {
tl:[tl.left, tl.top],
tr:[tr.left, tr.top],
br:[br.left, br.top],
bl:[bl.left, bl.top],
};
for(var p in pointsList){
document.querySelector('#r').innerHTML+=p+' '+pointsList[p].join(' , ')+'<br>';
}
#main{background-color:#CCC;height: 120px; width: 70px; position: relative; transform: rotate(30deg)}
.dot{ width: 1px; height: 1px; position: absolute; background-color:#000;}
#tl{top:0; left:0;}
#tr{top:0; right:0;}
#br{bottom:0; right:0;}
#bl{bottom:0; left:0;}
<div id="main">
<div id="tl" class="dot"></div>
<div id="tr" class="dot"></div>
<div id="br" class="dot"></div>
<div id="bl" class="dot"></div>
</div>
<div id="r">
Ken's comments are a good starting point actually. You can take the tangent inverse of the slope of the diagonal and add the degrees rotated to find the angle between the diagonal and a side.
m = (y3-y1)/(x3-x1)
diag_angle = arctan(m)
diag_angle_adjusted = diag_angle + rotation
This will give you the angle between the diagonal and the bottom left side. Then, you can use the distance formula to get the diagonal length.
diag_length = (y3 - y1)^2 + (x3-x1)^2
To find the length of the bottom left side you would use the cos formula, and for the bottom right you would use sin.
bot_left = diag_length*cos(diag_angle_adjusted)
This would let you get the lengths of the sides and proceed to calculate the other x and y. For example,
sin(rotation) = (y2 - y4)/bot_left
After solving for y4, it should be fairly simple to solve for x4 using cos.
I am answering from my phone and have not formally tested this, but that approach should work. Hopefully tomorrow I will have time to diagram the answer if it's not clear.
Good luck! And make sure to keep your signs correct for rotation.
Naming point (x1,x2) p1 etc.,
naming the rotation angle rot (minus 30deg in the example),
naming the distance frop p1 to p4 d14 etc.
Using the fact that the length op the projection of a vector on an axis is the absolute value of the dot-product of that vector on the ubit vector in that direction,
the length of p1-p4 is the dot product of (cos(rot), sin(rot)) with (x3 - x1, y3 - y1).
d14 = abs((x3 - x1)*cos(rot) + (y3 - y1)*sin(rot))
d12 = abs((x3 - x1)*cos(rot + 90) + (y3 - y1)sin(rot +90))
If you need the coordinates of p2 and p4
x4 = x1 + d14 * cos(rot)
y4 = y1 + d14 * sin(rot)
x2 = x1 + d12 * cos(rot + 90)
y2 = y1 + d12 * sin(rot + 90)
( created on my tablet, to be reviewed when I work on my laptop)