tbarbot
tbarbot

Reputation: 257

Correct algorithm to resize a rectangle on mouse drag

I am using paper.js to make a level editor. I am currently bugging on how to resize a rectangle properly

Currently, I am doing something like :

 rect.onMouseDrag = event => {
    let selectedNode = rect.selectedNode;
      selectedNode.point.x += event.delta.x;
      selectedNode.point.y += event.delta.y;
      switch (rect.selectedNode.index) {
        case 0:
          rect.segments[1].point.x += event.delta.x;
          rect.segments[3].point.y += event.delta.y;
          break;
        case 1:
          rect.segments[0].point.x += event.delta.x;
          rect.segments[2].point.y += event.delta.y;
          break;
        case 2:
          rect.segments[3].point.x += event.delta.x;
          rect.segments[1].point.y += event.delta.y;
          break;
        case 3:
          rect.segments[selectedNode.index - 1].point.x += event.delta.x;
          rect.segments[selectedNode.index - 3].point.y += event.delta.y;
          break;
  };

So I just check the adjacent points and move them accordingly to the mouse event. It works fine for AABB's

enter image description here

But as soon as the rectangle is rotated, everything breaks enter image description here

Can anyone explain or just link me what's the correct algorithm to resize a rectangle and keep it rectangle ? I think this question has already been adressed but I can't find anything useful.

Thanks :)

Upvotes: 2

Views: 676

Answers (2)

tbarbot
tbarbot

Reputation: 257

OK, my good friend of the compote zrugvanoudu35 speedrunned this problem:

     switch (rect.selectedNode.index) {
        case 0:
          rect.segments[1].point.x +=
            event.delta.x * Math.cos(rad) * Math.cos(rad) +
            event.delta.y * Math.cos(rad) * Math.sin(rad);
          rect.segments[1].point.y +=
            event.delta.x * Math.sin(rad) * Math.cos(rad) +
            event.delta.y * Math.sin(rad) * Math.sin(rad);
          rect.segments[3].point.x +=
            event.delta.x * Math.sin(rad) * Math.sin(rad) +
            -event.delta.y * Math.sin(rad) * Math.cos(rad);
          rect.segments[3].point.y +=
            -event.delta.x * Math.sin(rad) * Math.cos(rad) +
            event.delta.y * Math.cos(rad) * Math.cos(rad);
          break;
        case 1:
          rect.segments[0].point.x +=
            event.delta.x * Math.cos(rad) * Math.cos(rad) +
            event.delta.y * Math.cos(rad) * Math.sin(rad);
          rect.segments[0].point.y +=
            event.delta.x * Math.sin(rad) * Math.cos(rad) +
            event.delta.y * Math.sin(rad) * Math.sin(rad);
          rect.segments[2].point.x +=
            event.delta.x * Math.sin(rad) * Math.sin(rad) +
            -event.delta.y * Math.sin(rad) * Math.cos(rad);
          rect.segments[2].point.y +=
            -event.delta.x * Math.sin(rad) * Math.cos(rad) +
            event.delta.y * Math.cos(rad) * Math.cos(rad);
          break;
        case 2:
          rect.segments[3].point.x +=
            event.delta.x * Math.cos(rad) * Math.cos(rad) +
            event.delta.y * Math.cos(rad) * Math.sin(rad);
          rect.segments[3].point.y +=
            event.delta.x * Math.sin(rad) * Math.cos(rad) +
            event.delta.y * Math.sin(rad) * Math.sin(rad);
          rect.segments[1].point.x +=
            event.delta.x * Math.sin(rad) * Math.sin(rad) +
            -event.delta.y * Math.sin(rad) * Math.cos(rad);
          rect.segments[1].point.y +=
            -event.delta.x * Math.sin(rad) * Math.cos(rad) +
            event.delta.y * Math.cos(rad) * Math.cos(rad);
          break;
        case 3:
          rect.segments[2].point.x +=
            event.delta.x * Math.cos(rad) * Math.cos(rad) +
            event.delta.y * Math.cos(rad) * Math.sin(rad);
          rect.segments[2].point.y +=
            event.delta.x * Math.sin(rad) * Math.cos(rad) +
            event.delta.y * Math.sin(rad) * Math.sin(rad);
          rect.segments[0].point.x +=
            event.delta.x * Math.sin(rad) * Math.sin(rad) +
            -event.delta.y * Math.sin(rad) * Math.cos(rad);
          rect.segments[0].point.y +=
            -event.delta.x * Math.sin(rad) * Math.cos(rad) +
            event.delta.y * Math.cos(rad) * Math.cos(rad);
          break;
      }

He's a man of wise.

2D rectangle resizing algorithm

Obviously this needs some refactoring but it's easier to post the comprehensive answer this way.

Upvotes: 1

MBo
MBo

Reputation: 80187

Make projection of mouse move vector onto rectangle side vectors (they depends on rotation angle) and apply corresponding changes to rectangle side lengths.

One rectangle side has unit direction vector (cos(fi), sin(fi)), another (neighbor) side - (-sin(fi), cos(fi)) and for mouse shift (mx, my) applied to vertex between these sides:

delta_width = mx * cos(fi) + my * sin(fi)
delta_height = -mx * sin(fi) + my * cos(fi)

Note that signs depend on which vertex is moved

Upvotes: 0

Related Questions