解决item打组之后,对组进行‘先旋转再缩放’操作的漂移问题

This commit is contained in:
duanshengchao 2024-09-02 17:56:02 +08:00
parent a4b8556a47
commit a45c125cee
4 changed files with 115 additions and 22 deletions

View File

@ -10,6 +10,7 @@ QT_END_NAMESPACE
class DesignerView;
class DesignerScene;
class GraphicsItemGroup;
class DrawingPanel : public QWidget
{
@ -26,6 +27,9 @@ public:
void grahpicsViewZoomOut();
void grahpicsViewZoomFit();
GraphicsItemGroup* createItemGroup();
void destroyItemGroup();
public slots:
void onSignal_addGraphicsItem(GraphicsItemType&);

View File

@ -2,11 +2,13 @@
#include <QGraphicsScene>
#include <QPainter>
#include <QStyleOption>
#include <QTimer>
GraphicsItemGroup::GraphicsItemGroup(QGraphicsItem *parent)
: AbstractShapeType<QGraphicsItemGroup>(parent)
{
m_boundingRect = QRectF();
m_lastBoudingRect = QGraphicsItemGroup::boundingRect();
//初始化缩放操作用的handle
m_vecHanle.reserve(H_left);
@ -52,9 +54,12 @@ void GraphicsItemGroup::paint(QPainter* painter, const QStyleOptionGraphicsItem*
painter->drawRect(m_boundingRect_selected);
//绘制变换原点
// QPointF originPoint = transformOriginPoint();
// painter->setBrush(Qt::red);
// painter->drawEllipse(originPoint, 4, 4);
QPointF originPoint = transformOriginPoint();
//qDebug() << "originPoint:" << originPoint << " boundingRect:" << m_boundingRect;
painter->setBrush(Qt::red);
painter->drawEllipse(QPointF(0,0), 4, 4);
painter->setBrush(Qt::blue);
painter->drawEllipse(originPoint, 4, 4);
}
}
@ -90,23 +95,27 @@ void GraphicsItemGroup::updateCoordinate() //当执行了resie和editShape函数
m_dHeight = m_boundingRect.height();
}
QPointF pt1, pt2, delta;
pt1 = mapToScene(transformOriginPoint());
pt2 = mapToScene(m_boundingRect.center());
delta = pt1 - pt2;
//因为group包含子itemboundingRect会根据子item自动计算生成所以不再通过重设boundingRect中心和原点重合重合的方式因为此种方式需要子item做各种同步计算
//最简单的方式是每次boundingRect发生变化缩放后重新设置变换中心但是会出现漂移因为item只存储旋转角度数据每帧都根据角度进行重绘具体见开发学习笔记消除这个漂移即可
prepareGeometryChange();
//boundingRect发生变化后原点偏离中心将原点设为中心点因为group内含有其它item所以采用transform变换的方式
//m_boundingRect = QRectF(-m_dWidth / 2, -m_dHeight / 2, m_dWidth, m_dHeight);
setTransform(transform().translate(delta.x(), delta.y()));
//具体计算
double dAngle = qDegreesToRadians(rotation());
QPointF centerPt = m_boundingRect.center();
QPointF originPt = transformOriginPoint();
QPointF targetPt = QPointF(0, 0);
targetPt.setX(originPt.x() + qCos(dAngle)*(centerPt.x() - originPt.x()) - qSin(dAngle)*(centerPt.y() - originPt.y()));
targetPt.setY(originPt.y() + qSin(dAngle)*(centerPt.x() - originPt.x()) + qCos(dAngle)*(centerPt.y() - originPt.y()));
QPointF delta = targetPt - centerPt;
//m_boundingRect.adjust(delta.x(), delta.y(), delta.x(), delta.y());
setTransformOriginPoint(m_boundingRect.center());
//更改原点后自身绘制会在新坐标系下进行会有跳转所以需要将item整体做对应的移动
moveBy(-delta.x(), -delta.y());
if(dAngle != 0)
moveBy(delta.x(), delta.y());
//QTimer::singleShot(2000,[&](){moveBy(-delta.x(), -delta.y());});
updateHandles();
}
m_lastBoudingRect = m_boundingRect;
//调用该组内所有item(过滤handle)相关函数
foreach (QGraphicsItem *item, childItems())
{
@ -116,6 +125,7 @@ void GraphicsItemGroup::updateCoordinate() //当执行了resie和editShape函数
baseItem->updateCoordinate();
}
}
m_lastBoudingRect = m_boundingRect;
}
void GraphicsItemGroup::resize(int nHandle,double dSX, double dSY, const QPointF& basePoint)
@ -135,7 +145,7 @@ void GraphicsItemGroup::resize(int nHandle,double dSX, double dSY, const QPointF
}
QTransform trans;
//缩放是以图元原点(中心)位置为基准,所以每帧都先移动移动到想要的基准点,缩放之后再移回
//缩放是以图元变换原点为基准,所以每帧都先移动移动到想要的基准点,缩放之后再移回
trans.translate(basePoint.x(), basePoint.y());
trans.scale(dSX, dSY);
trans.translate(-basePoint.x(), -basePoint.y());

View File

@ -74,15 +74,16 @@ void GraphicsRectItem::updateCoordinate() //当执行了resie和editShape函数
delta = pt1 - pt2;
prepareGeometryChange();
//boundingRect发生变化后原点偏离中心将原点设为中心点
//将boundingRect设置成中心点和原点也是默认变换原点这样三点重合有助于简化计算
m_boundingRect = QRectF(-m_dWidth / 2, -m_dHeight / 2, m_dWidth, m_dHeight);
//setTransformOriginPoint(m_boundingRect.center()); //变换中心默认为item的(0,0)点,所以不执行这句话也没有问题
//更改原点后自身绘制会在新坐标系下进行会有跳转所以需要将item整体做对应的移动
//更新bouondingRect后重回会显示位置会有变化需要做对应的移动
moveBy(-delta.x(), -delta.y());
updateHandles();
}
m_lastBoudingRect = m_boundingRect;
//qDebug() << "itemPos:" << mapToParent(pos());
}
void GraphicsRectItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
@ -114,6 +115,7 @@ void GraphicsRectItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*
//绘制变换原点
QPointF originPoint = transformOriginPoint();
//qDebug() << "originPoint:" << originPoint;
painter->setBrush(Qt::red);
painter->drawEllipse(originPoint, 4, 4);
}

View File

@ -1,4 +1,5 @@
#include "operationCommand.h"
#include "graphicsItem/graphicsItemGroup.h"
#include <QGraphicsItem>
AddItemCommand::AddItemCommand(QGraphicsItem* item, QGraphicsScene* scene, QUndoCommand* parent)
@ -39,8 +40,8 @@ void DeleteItemCommand::undo()
{
foreach(QGraphicsItem* item, m_listItem)
{
QGraphicsItemGroup* group = dynamic_cast<QGraphicsItemGroup*>(item->parentItem());
if(!group)
// QGraphicsItemGroup* group = dynamic_cast<QGraphicsItemGroup*>(item->parentItem());
// if(!group)
{
m_pGraphicsScene->addItem(item);
}
@ -52,8 +53,8 @@ void DeleteItemCommand::redo()
{
foreach(QGraphicsItem* item, m_listItem)
{
QGraphicsItemGroup* group = dynamic_cast<QGraphicsItemGroup*>(item->parentItem());
if(!group)
// QGraphicsItemGroup* group = dynamic_cast<QGraphicsItemGroup*>(item->parentItem());
// if(!group)
{
m_pGraphicsScene->removeItem(item); //remove即可不要delete因为会影响撤回undo操作
}
@ -62,3 +63,79 @@ void DeleteItemCommand::redo()
m_pGraphicsScene->update();
}
CreateItemGoupCommand::CreateItemGoupCommand(GraphicsItemGroup* group, QGraphicsScene* scene, QUndoCommand* parent)
: QUndoCommand(parent)
{
m_pGroup = group;
m_pGraphicsScene = scene;
m_listItem = group->getItems();
}
CreateItemGoupCommand::~CreateItemGoupCommand()
{
}
void CreateItemGoupCommand::undo()
{
m_pGroup->setSelected(false);
foreach (QGraphicsItem *item, m_listItem)
{
//item->setSelected(true);
m_pGroup->removeFromGroup(item);
AbstractShape *ab = qgraphicsitem_cast<AbstractShape*>(item);
ab->updateCoordinate();
ab->setSelected(true);
}
m_pGraphicsScene->removeItem(m_pGroup);
m_pGraphicsScene->update();
}
void CreateItemGoupCommand::redo()
{
if(!m_pGroup->scene()) //因为添加图元后同步创建一条该指令平且在push进入stack的时候redo会被触发一次因此这里加判断防止重复操作
{
foreach (QGraphicsItem *item, m_listItem)
{
item->setSelected(false);
m_pGroup->addToGroup(item);
}
m_pGraphicsScene->addItem(m_pGroup);
}
m_pGroup->setSelected(true);
m_pGraphicsScene->update();
}
DestroyItemGoupCommand::DestroyItemGoupCommand(GraphicsItemGroup* group, QGraphicsScene* scene, QUndoCommand* parent)
: QUndoCommand(parent)
{
m_pGroup = group;
m_pGraphicsScene = scene;
m_listItem = group->getItems();
}
DestroyItemGoupCommand::~DestroyItemGoupCommand()
{
}
void DestroyItemGoupCommand::undo()
{
foreach (QGraphicsItem *item, m_listItem)
{
item->setSelected(false);
m_pGroup->addToGroup(item);
}
m_pGroup->setSelected(true);
m_pGraphicsScene->addItem(m_pGroup);
m_pGraphicsScene->update();
}
void DestroyItemGoupCommand::redo()
{
m_pGroup->setSelected(false);
foreach (QGraphicsItem *item, m_listItem)
{
//item->setSelected(true);
m_pGroup->removeFromGroup(item);
AbstractShape *ab = qgraphicsitem_cast<AbstractShape*>(item);
ab->updateCoordinate();
ab->setSelected(true);
}
m_pGraphicsScene->removeItem(m_pGroup);
m_pGraphicsScene->update();
}