Reputation: 683
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)
Upvotes: 12
Views: 7154
Reputation:
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 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;
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>
Upvotes: 13
Reputation: 3845
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)
Upvotes: 0
Reputation: 53
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.
Upvotes: 0
Reputation: 136708
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">
Upvotes: 0