10

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.

enter image description here

Roey Zada
  • 663
  • 10
  • 30
  • 1
    This is more of a trigonometry question; is there anything in particular that you can't deduce from what you have? – Ja͢ck Apr 11 '15 at 03:19
  • well, not really.. I need to know the width and the height of this element only by draggin the mouse from top right point to bottom left point.. and I cant get my head around how to do this in javascript – Roey Zada Apr 11 '15 at 03:23
  • This question is ambiguous unless you specify more of the desired behavior when dragging. – dting Apr 11 '15 at 03:33
  • i dont understand.. my desire is to re-size the div. while dragging from the top right corner towards to bottom left corner – Roey Zada Apr 11 '15 at 03:42
  • 1
    This may help: http://stackoverflow.com/questions/2219108/knowing-two-points-of-a-rectangle-how-can-i-figure-out-the-other-two –  Apr 11 '15 at 04:25
  • thank you Ken, but that wont do.. over there they have the length of all four sides... – Roey Zada Apr 11 '15 at 04:46
  • if the rotation point is the center of the rectangle that would mean that on rotation all points would move, is this correct? – Mauricio Poppe Apr 11 '15 at 05:58
  • hey @Mauricio, yes it does – Roey Zada Apr 11 '15 at 13:01
  • 1
    hey @GameAlchemist, thank you, but i, guessing it will only work on squares – Roey Zada Apr 11 '15 at 13:58

4 Answers4

13

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>
0

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">
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • thank you for you answer @Kaiido, I'm afraid that wont do the job because the element is rotated, and getBoundingClientRect will give me the rect surrounding my given rectangle and I need the values of the inner one – Roey Zada Apr 11 '15 at 04:17
  • no the trick here is to set 4 divs, of 1px height/width, absolutely positioned in the 4 corners of the main div, and get their position. Try it, I think it does work. But As I said, you should use trigo and certainly not this *"almost a joke"*. – Kaiido Apr 11 '15 at 04:25
  • @RoeyZada look at [Ken's comment](http://stackoverflow.com/questions/29573653/given-two-diagonally-opposite-points-on-a-rectangle-how-to-calculate-the-other/29574033#comment-47297110) – Kaiido Apr 11 '15 at 04:28
  • haha.. i see what you did there.. love you brain, if trigo won't work I'll try your approach :D – Roey Zada Apr 11 '15 at 04:28
0

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.

ryankey721
  • 53
  • 7
0

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)

Dirk Horsten
  • 3,753
  • 4
  • 20
  • 37
  • hey @dirk, im sorry but it doesnt seems to work, its like the distance calculate is from bottom left corner.. thank you for answering! – Roey Zada Apr 11 '15 at 13:35