【问题标题】:Scaling a rotated item based on top left moves the item根据左上角缩放旋转的项目移动项目
【发布时间】:2015-11-28 03:45:18
【问题描述】:

我希望能够基于QGraphicsItem 的中心旋转它,并根据左上角对其进行缩放。

当我尝试结合旋转和缩放时,项目显然也移动了......

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsTextItem>

void testTransformations(QGraphicsScene* s)
{
    qreal angle = 30, scaleX = 2, scaleY = 1;
    // Reference rotated not scaled
    QGraphicsTextItem* ref = new QGraphicsTextItem("bye world");
    ref->setFont(QFont("Arial", 20));
    ref->setDefaultTextColor(Qt::green);
    s->addItem(ref);
    qreal center0X = ref->boundingRect().center().x();
    qreal center0Y = ref->boundingRect().center().y();
    QTransform t0;
    t0.translate(center0X, center0Y);
    t0.rotate(angle);
    t0.translate(-center0X, -center0Y);
    ref->setTransform(t0);

    // Reference scaled not rotated
    QGraphicsTextItem* ref1 = new QGraphicsTextItem("bye world");
    ref1->setFont(QFont("Arial", 20));
    ref1->setDefaultTextColor(Qt::yellow);
    s->addItem(ref1);
    QTransform t;
    t.scale(scaleX, scaleY);
    ref1->setTransform(t);

    // Rotate around center of resized item
    QGraphicsTextItem* yyy = new QGraphicsTextItem("bye world");
    yyy->setDefaultTextColor(Qt::red);
    yyy->setFont(QFont("Arial", 20));
    s->addItem(yyy);
    qreal center1X = yyy->boundingRect().center().x() * scaleX;
    qreal center1Y = yyy->boundingRect().center().y() * scaleY;
    // in my code I store the item size, either before or after the resize, and use it to determine the center - which is virtually the same thing as this for a single operation
    QTransform t1;
    t1.translate(center1X, center1Y);
    t1.rotate(angle);
    t1.translate(-center1X, -center1Y);
    t1.scale(scaleX, scaleY);
    yyy->setTransform(t1);

    // rotated around center of bounding rectangle
    QGraphicsTextItem* xxx = new QGraphicsTextItem("bye world");
    xxx->setDefaultTextColor(Qt::blue);
    xxx->setFont(QFont("Arial", 20));
    s->addItem(xxx);
    qreal center2X = xxx->boundingRect().center().x();
    qreal center2Y = xxx->boundingRect().center().y();
    QTransform t2;
    t2.translate(center2X, center2Y);
    t2.rotate(angle);
    t2.translate(-center2X, -center2Y);
    t2.scale(scaleX, scaleY);
    xxx->setTransform(t2);
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QGraphicsScene s;
    QGraphicsView view(&s);
    s.setSceneRect(-20, -20, 500, 500);
    view.show();
    testTransformations(&s);
    return app.exec();
}

结果:

  • 绿色旋转,未缩放(或缩放不同的量)
  • 黄色是缩放的,不是旋转的
  • 蓝色按边界矩形的中心进行缩放和旋转(未调整大小)
  • 红色围绕中心缩放和旋转

现在对我来说很明显,转换操作正确 - 如果我调整和旋转一个项目,我首先得到黄色项目,然后是红色项目。

然而,我需要的是,如果一个项目已经旋转(绿色)然后缩放,表现得像蓝色 - 在同一方向拉伸,没有跳跃,而如果一个项目先缩放,然后旋转,表现像红色...更复杂的是,原始项目(绿色)可能已经应用了缩放,所以我使用边界矩形的简单解决方案不起作用。

我试图计算变化......总是得到奇怪的结果。

是否可以根据左上角缩放旋转的项目,而不移动它,同时围绕其中心旋转它?

这可能需要增量转换,并且根据应用的顺序得到不同的结果会很奇怪。

编辑:我一直在尝试调整位置,因为转换失败了,但我无法获得一个转换函数的公式,它可以让我平滑地看到类型的视觉转换:

1) 旋转项目(固定到中心)
2) 在不跳跃的情况下缩放项目(固定在左上角)
3)旋转项目(固定到中心)

其中步骤 2 还包括位置偏移。我只是不知道该怎么做。
在我看来,在“红色”变换的片段中,我将在变换前后添加一个mapToScene(somePoint),并根据结果执行校正(moveBy)。

这不是一个很好的修复,但仍然......如果我知道如何在调整大小后调整项目的位置使其不会跳跃,它仍然是一个修复......

【问题讨论】:

    标签: qt rotation transform scaling qgraphicsitem


    【解决方案1】:

    好的,抱歉编辑晚了,在Qt中似乎无法直接实现。

    但是,您可以使用简单的三角函数来计算绕 0,0 原点旋转时与中心的偏移,并手动移动项目以补偿位移。

    想象一个圆心为 0,0,半径为从圆心到项目边界框中心的直线。当您围绕 0,0 原点旋转项目时,项目的边界框中心将始终位于圆上,因此您可以计算给定旋转角度的偏移量,然后旋转并调整项目的位置以匹配中心之前的状态。

    这是一个小插图,如您所见,旋转后,项目会移动偏移量,以使其看起来好像围绕其中心旋转,而不是围绕左上角旋转。当然,由于原点仍然是 0,0,它会按照您的意图进行缩放。

    编辑:更简单,无需触发,仅使用 Qt 功能,此方法将围绕其中心旋转项目,而变换原点位于左上角:

    static void offsetRotation(qreal angle, QGraphicsItem * i) {
        QPointF c = i->mapToScene(i->boundingRect().center());
        i->setRotation(angle);
        QPointF cNew = i->mapToScene((i->boundingRect()).center());
        QPointF offset = c - cNew;
        i->moveBy(offset.x(), offset.y());
    }
    

    但是,我注意到一些奇怪的事情 - 当原点设置为左上角时,根据我在图形设计软件方面的长期经验,缩放并没有真正将左上角保持在同一个位置。相反,该物品会随着其规模的增加而慢慢飘走。原点似乎确实对缩放有一些影响,但我肯定不会以任何方式称其为足够的。因此,根据您想要实现的具体目标,您可能还必须使用相同的偏移调整技巧进行缩放。随着项目的缩放,它将继续报告相同的位置,但是如果将其映射到场景,您会意识到它实际上发生了变化,因此您可以跟踪该变化并进行补偿,以便使用相应的 offsetScale 产生适当的行为方法。

    所有这些都是有代价的,为了保持视觉输出符合预期,你的坐标最终会变得一团糟。这可能会在以后证明是一个并发症。一种解决方案是为您的项目创建自己的“公共”坐标,并在内部管理所有混乱以规范最终结果。

    希望其他人可以提供更简洁的解决方案,根据我使用 Qt 的经验,从事图形类工作的人似乎对图形工作流程几乎没有经验,毫无疑问他们是优秀的程序员,但最终结果是Qt 的图形类通常是反直觉的,甚至完全无法按照人们对专业图形软件的期望方式工作。也许更务实的头脑可能会为这个问题提供更好的解决方案。

    【讨论】:

    • 这不行,它设置了整个转换的原点。
    • 谢谢,我自己也得出了同样的结论 - 实现缩放旋转项目的平滑视觉效果的唯一方法是同时调整位置 - 不可取但似乎不可避免。不太确定如何处理您还注意到的漂移,如我的示例中所示 - 我尝试映射到场景 (0,0) 但没有发现差异......
    • 我已经在其他情况下实现了提到的偏移量,但我尝试这样做并且它使调整大小发生在中心周围......
    • 奇怪,使用边界矩形中心的校正使缩放发生在中心周围,而使用左上角的边界矩形校正偏移量使旋转发生在左上角。
    • 在编辑之前的图像中,您展示了如何确定旋转偏移 - 但这是由 Qt 变换自动计算的。问题之一是调整旋转项目的大小时 - 我的绿色和红色项目之间的偏移量。
    【解决方案2】:

    这是我解决问题的尝试——它有效,但不是一个很好的解决方案:

    • 在任何转换上,我都会存储m31()m32()
    • 如果转换是缩放的,我会通过新旧m31()m32() 之间的变化来抵消

    【讨论】:

      猜你喜欢
      • 2010-12-24
      • 1970-01-01
      • 2014-09-04
      • 2017-08-16
      • 2013-09-04
      • 2020-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多