调整带有OriginX和OriginY值的旋转矩形大小。

huangapple go评论101阅读模式
英文:

Resizing Rotated Rectangle with OriginX and OriginY values

问题

我正在开发一个用于Flutter/Dart的Canvas库,类似于FabricJS,但用于更简单的任务。当我将项目推进到一定阶段时,我将在pub.dev和GitHub上分享它。

我已经为这个问题苦苦挣扎了几天,请帮助我。

基本上,每个绘图对象都是一个类。每个对象都有Left、Top、Width、Height、ScaleX、ScaleY、Angle(旋转角度)、OriginX和OriginY的值。

OriginX和OriginY是枚举类型,包含以下值。

枚举 OriginX { left, right, center }
枚举 OriginY { top, bottom, center }

如果将对象的OriginX设置为"left",则对象将从第100个像素的左侧开始绘制。如果给定值"right",则对象将从第100个像素的右侧开始绘制。

我在对象的4个角和左右中间绘制了小方框,用于改变对象的大小。但我不是通过改变它们的Width和Height来改变对象的大小,而是通过改变它们的ScaleX和ScaleY值来实现的。因此,我正在进行缩放操作。

我为缩放对象编写了一个ActionHandler方法。我的主要问题是,如果对象被旋转,当改变对象的缩放时,对象以无意义的方式移动到不同的位置。这是由于应用于对象的旋转造成的问题。例如,如果对象的角度为0度,当我通过右侧中心手柄来放大对象时,它会平稳地增大,并且宽度会按照鼠标的当前位置增加。如果我通过左侧中心手柄抓住对象并拉动它,没有问题。

如果对象的Angle值为0,无论OriginX和OriginY的值如何,我都可以在任何点抓住它并进行缩放。

但是,例如,如果OriginX = left,并且您给对象一个45度的倾斜度,并且您在右侧中心的缩放点上抓住它,它会平稳增长。因为您是从右侧抓住的,对象始终保持在OriginX.left处。但相反,如果它是从左侧抓住的,而缩放是OriginX.left,对象的位置就会失真。它应该从右侧固定,并以45度向上缩放。

直接更改对象的Left和Top值意味着对象将考虑对象的OriginX和OriginY值,并根据这些Origin值在背景中绘制点。

此外,我正确执行了ScaleX和ScaleY的计算,但涉及到旋转度数时,对象的缩放与我用鼠标抓住的点不完全一致。

我需要将整个计算的结果分配给对象的Left、Top、ScaleX和ScaleY属性。因为一切都是根据对象的值进行渲染的。

我制作了一个简短的视频,请从下面的链接观看视频。

https://streamable.com/sco5de

我还阅读了这里的文章,它解决了我遇到的相同问题。但是我无法将其应用到我的系统中。

https://shihn.ca/posts/2020/resizing-rotated-elements/

缩放处理器代码

void onActionHandler(dynamic details, GestureType gestureType,
      BasicObject object, ObjectControl control) {
    if (gestureType == GestureType.onPanDown) {
      final point = (details as DragDownDetails).localPosition;
      properties.set('firstPoint', point);
    } else if (gestureType == GestureType.onPanUpdate) {
      final lastPoint = (details as DragUpdateDetails).localPosition;

      // 这将LastPoint的值分配给“firstPoint”,覆盖它并在覆盖之前返回“firstPoint”的值。
      // properties变量用于通用目的的值存储。
      Offset delta =
          lastPoint - properties.swap<Offset>('firstPoint', lastPoint);

      // 这些变量指示对象是否在右侧或向下方向缩放。
      // 如果不这样做,当您抓住对象的左侧中心缩放控制并向左拉动它时,对象会缩小而不是增大。
      // 因此,相反的情况将发生,我们要阻止这种情况发生。

      bool positiveHorizontalScale = true;
      bool positiveVerticalScale = true;

      // “option”变量返回枚举值,表示它是从哪个检查点抓住的。
      final option = getControlOption(control)!;

      if (option == ControlsOptions.tl) {
        positiveHorizontalScale = false;
        positiveVerticalScale = false;
      } else if (option == ControlsOptions.mt || option == ControlsOptions.tr) {
        positiveVerticalScale = false;
      } else if (option == ControlsOptions.bl || option == ControlsOptions.ml) {
        positiveHorizontalScale = false;
      }

      if (!positiveHorizontalScale) {
        delta = Offset(-delta.dx, delta.dy);
      }
      if (!positiveVerticalScale) {
        delta = Offset(delta.dx, -delta.dy);
      }

      // 这些变量指定对象是否在两个方向上缩放还是在特定方向上缩放。
      bool adjustHorizontal = false;
      bool adjustVertical = false;

      final scaledWidth = width * scaleX;
      final scaledHeight = height * scaleY;

      // 计算缩放所需的ScaleX和ScaleY值。
      final sX = (scaledWidth + delta.dx) / scaledWidth * scaleX;
      final sY = (scaledHeight + delta.dy) / scaledHeight * scaleY;

      double tempScaleX = scaleX;
      double tempScaleY = scaleY;
      double tempLeft = left;
      double tempTop = top;

      switch (option) {
        case ControlsOptions.tl:
        case ControlsOptions.tr:
        case ControlsOptions.bl:
        case ControlsOptions.br:
          {
           

<details>
<summary>英文:</summary>

I am developing a Canvas library in FLutter/Dart, like FabricJS but for much simpler tasks. When I get the project to a certain stage, I will share it on pub.dev and Github.

I&#39;ve been struggling with this problem for a few days, please help.

Basically every drawing object is a class. Each object simply has values Left, Top, Width, Height, ScaleX, ScaleY, Angle (degree of ration), OriginX and OriginY.

OriginX and OriginY is an Enum and contains the following. 

enum OriginX { left, right, center }
enum OriginY { top, bottom, center }

If you set the OriginX of an object to &quot;left&quot;, the object will be drawn from the left side from the 100th pixel. If you give the value &quot;right&quot;, the object will draw itself from the right side at the 100th pixel.

I draw a small box at the 4 corners of the objects and in the middle of the left and right side. I use these to change the size of the object. But I don&#39;t change the size of the objects by changing their Width and Height, but by changing their ScaleX and ScaleY values. So I am scaling.


I wrote an ActionHandler method for the scaling objects in the corner. My main problem is that if the object is rotated, the object moves to different positions in a meaningless way when changing the scale of the object. The reason for this is the problem caused by the rotation applied to the object. For example, if the object is 0 degrees, when I hold the object by the right center handle and enlarge it, it grows smoothly and the width grows following the current position of the mouse. If I grab the object by the left center handle and pull it, no problem.

If the Angle value of the object is 0 and regardless of the OriginX, OriginY value, I can grab it at any point and scale it.


But for example, if OriginX = left and you have given the object a 45 degree tilt and you hold it at the right center scaling point, it will grow smoothly. Because you scaled by holding it from the right side and the object always keeps itself fixed to OriginX.left. But on the contrary, if it is from the left side and the scaling is OriginX.left, the position of the object is distorted. it should be fixed from the right side and scaled 45 degrees upwards.

Changing the Left and Top values of the object directly means that the object takes into account the OriginX and OriginY values of the object and makes the drawing points of the object in the background according to these Origin values.

In addition, I am doing the ScaleX and ScaleY calculations correctly, but when the degree of rotation is involved, the scale of the object does not exactly coincide with the points I hold with the mouse.

I need to assign the results of the whole calculation to the object&#39;s Left, Top, ScaleX and ScaleY properties. Because everything is rendered according to the object&#39;s values.

I made a short video, please watch the video from the link below.

[https://streamable.com/sco5de](https://streamable.com/sco5de)

I also read the article here and it solves the same problem I have. But I could not apply it to my system.

[https://shihn.ca/posts/2020/resizing-rotated-elements/](https://shihn.ca/posts/2020/resizing-rotated-elements/)

[Screenshot](https://i.stack.imgur.com/oLc8f.png)

**Scale Handler Codes**

void onActionHandler(dynamic details, GestureType gestureType,
BasicObject object, ObjectControl control) {
if (gestureType == GestureType.onPanDown) {
final point = (details as DragDownDetails).localPosition;
properties.set('firstPoint', point);
} else if (gestureType == GestureType.onPanUpdate) {
final lastPoint = (details as DragUpdateDetails).localPosition;

  //This assigns the value of LastPoint to
// &quot;firstPoint&quot;, overwrites it and returns the value of &quot;firstPoint&quot; before overwriting.
//The properties variable is used for general purpose value storage.
Offset delta =
lastPoint - properties.swap&lt;Offset&gt;(&#39;firstPoint&#39;, lastPoint);
//These variables indicate if the object is scaled in the right or
// down direction. If this is not done and you grab the object&#39;s left
// center scaling control and pull it to the left, the object will shrink
// instead of grow. So the opposite will happen and we prevent this.
bool positiveHorizontalScale = true;
bool positiveVerticalScale = true;
//The &quot;option&quot; variable returns the enum value
// of whichever checkpoint it is held from.
final option = getControlOption(control)!;
if (option == ControlsOptions.tl) {
positiveHorizontalScale = false;
positiveVerticalScale = false;
} else if (option == ControlsOptions.mt || option == ControlsOptions.tr) {
positiveVerticalScale = false;
} else if (option == ControlsOptions.bl || option == ControlsOptions.ml) {
positiveHorizontalScale = false;
}
if (!positiveHorizontalScale) {
delta = Offset(-delta.dx, delta.dy);
}
if (!positiveVerticalScale) {
delta = Offset(delta.dx, -delta.dy);
}
//These variables specify whether the object is scaled in both directions
// or in a particular direction.
bool adjustHorizontal = false;
bool adjustVertical = false;
final scaledWidth = width * scaleX;
final scaledHeight = height * scaleY;
//Calculate ScaleX and ScaleY values required for scaling.
final sX = (scaledWidth + delta.dx) / scaledWidth * scaleX;
final sY = (scaledHeight + delta.dy) / scaledHeight * scaleY;
double tempScaleX = scaleX;
double tempScaleY = scaleY;
double tempLeft = left;
double tempTop = top;
switch (option) {
case ControlsOptions.tl:
case ControlsOptions.tr:
case ControlsOptions.bl:
case ControlsOptions.br:
{
tempScaleX = sX;
tempScaleY = sY;
adjustHorizontal = true;
adjustVertical = true;
}
break;
case ControlsOptions.ml:
case ControlsOptions.mr:
{
tempScaleX = sX;
adjustHorizontal = true;
}
break;
case ControlsOptions.mt:
case ControlsOptions.mb:
{
tempScaleY = sY;
adjustVertical = true;
}
break;
case ControlsOptions.mtr:
{}
break;
}
//If scaling in horizontal direction
if (adjustHorizontal) {
if (originX == OriginX.right &amp;&amp; positiveHorizontalScale) {
tempLeft = tempLeft + delta.dx;
}
if (originX == OriginX.center) {
if (positiveHorizontalScale) {
tempLeft = tempLeft + delta.dx / 2;
} else {
tempLeft = tempLeft - delta.dx / 2;
}
}
if (originX == OriginX.left &amp;&amp; !positiveHorizontalScale) {
tempLeft = tempLeft - delta.dx;
}
}
//If scaling in vertical direction
if (adjustVertical) {
if (originY == OriginY.bottom &amp;&amp; positiveVerticalScale) {
tempTop = tempTop + delta.dy;
}
if (originY == OriginY.center) {
if (positiveVerticalScale) {
tempTop = tempTop + delta.dy / 2;
} else {
tempTop = tempTop - delta.dy / 2;
}
}
if (originY == OriginY.top &amp;&amp; !positiveVerticalScale) {
tempTop = tempTop - delta.dy;
}
}
left = tempLeft;
top = tempTop;
scaleX = tempScaleX;
scaleY = tempScaleY;
_controller!.render();
}

}


I developed methods for different calculations many times. I tried to solve my problem in ChatGPT.But I could not get the correct calculation in any way, there were always meaningless values.
</details>
# 答案1
**得分**: 0
我解决了所有的问题!感谢@pskink提供了有关解决方案的宝贵信息。下面您可以看到一个工作代码的完整示例。
```dart
Offset rotateOffset(Offset offset, Offset centerPoint, double degree) {
final radian = degree2Radian(degree);
final o = offset - centerPoint;
final x = o.dx * math.cos(radian) - o.dy * math.sin(radian);
final y = o.dx * math.sin(radian) + o.dy * math.cos(radian);
return Offset(x + centerPoint.dx, y + centerPoint.dy);
}
delta = rotateOffset(delta, Offset.zero, -angle);
final r0 = getRect();
final i = getControlOptionIndex(option)!;
final insets = EdgeInsets.only(
left: pads[i].left * -delta.dx,
top: pads[i].top * -delta.dy,
right: pads[i].right * delta.dx,
bottom: pads[i].bottom * delta.dy);
final r1 = insets.inflateRect(r0);
final point = rotateOffset(r1.topLeft, r0.topLeft, angle);
left = point.dx;
top = point.dy;
width = r1.width;
height = r1.height;
英文:

I solved all the problems! My thanks to @pskink who provided valuable information on the solution. Below you can see a full example of the working code.

  Offset rotateOffset(Offset offset, Offset centerPoint, double degree) {
final radian = degree2Radian(degree);
final o = offset - centerPoint;
final x = o.dx * math.cos(radian) - o.dy * math.sin(radian);
final y = o.dx * math.sin(radian) + o.dy * math.cos(radian);
return Offset(x + centerPoint.dx, y + centerPoint.dy);
}
delta = rotateOffset(delta, Offset.zero, -angle);
final r0 = getRect();
final i = getControlOptionIndex(option)!;
final insets = EdgeInsets.only(
left: pads[i].left * -delta.dx,
top: pads[i].top * -delta.dy,
right: pads[i].right * delta.dx,
bottom: pads[i].bottom * delta.dy);
final r1 = insets.inflateRect(r0);
final point = rotateOffset(r1.topLeft, r0.topLeft, angle);
left = point.dx;
top = point.dy;
width = r1.width;
height = r1.height;

huangapple
  • 本文由 发表于 2023年7月18日 00:18:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76706365.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定