QGraphicsItem的contains接口

1.概要

2.内容

QGraphicsItem 的 contains 接口是一个虚函数,用于判断一个指定的点是否位于该图形项的边界矩形(bounding rectangle)内。这个函数通常用于实现鼠标事件处理、碰撞检测等功能。

函数原型如下:

virtual bool contains(const QPointF &point) const = 0;
  • point 是一个 QPointF 类型的参数,表示要检查的点。
  • 返回值是一个布尔值,如果点位于图形项的边界矩形内,则返回 true;否则返回 false

由于 contains 是一个纯虚函数,因此在 QGraphicsItem 类中没有提供默认实现。这意味着当你创建自定义的图形项时,需要重写这个函数来提供你自己的实现。

在实现 contains 函数时,你通常会使用 boundingRect 函数返回的边界矩形来进行判断。例如,如果你的图形项是一个简单的矩形,你可能会这样实现 contains

bool MyGraphicsItem::contains(const QPointF &point) const {
    return boundingRect().contains(point);
}

然而,如果你的图形项有更复杂的形状,你可能需要使用 QPainterPath 来定义图形项的形状,并在 contains 函数中使用这个形状来进行判断。例如:

bool MyComplexGraphicsItem::contains(const QPointF &point) const {
    return shape().contains(point);
}

在这个例子中,shape 函数返回一个 QPainterPath 对象,它描述了图形项的形状。然后,contains 函数使用这个形状来检查点是否位于图形项内。

总的来说,contains 函数是 QGraphicsItem 类中一个非常重要的接口,它允许你判断一个点是否位于图形项内,这对于实现交互功能(如鼠标点击、拖拽等)和碰撞检测非常有用。

3.关联链接

4.关联知识 

1.QGraphicsItem详解

QGraphicsItem 是 Qt 图形视图框架(Graphics View Framework)中的核心类,用于表示场景(QGraphicsScene)中的图形项。它提供了灵活的 2D 图形项管理、绘制、事件处理和坐标变换等功能。以下是详细解析:


1. 核心功能

  • 绘制:通过重写 paint() 函数定义项的外观。
  • 边界定义:通过 boundingRect() 返回项的包围矩形(用于碰撞检测和渲染优化)。
  • 形状定义:通过 shape() 返回精确的碰撞检测形状(默认使用 boundingRect())。
  • 事件处理:支持鼠标、键盘、悬停等事件(需重写 sceneEvent() 或特定事件函数)。
  • 变换:支持平移、旋转、缩放(通过 setTransform() 或 setPos() 等)。
  • 父子关系:支持父子项层级结构,子项坐标相对于父项。
  • 标志位:通过 setFlags() 控制项的行为(如是否可选、可移动)。

2. 关键函数

绘制与边界
  • paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    重写此函数自定义绘制逻辑。需使用 painter 在项的局部坐标系中绘制。

  • QRectF boundingRect() const
    定义项的包围矩形,用于碰撞检测和渲染优化。必须实现,且返回值需覆盖所有绘制内容。

形状与碰撞检测
  • QPainterPath shape() const
    返回精确的碰撞检测形状(默认使用 boundingRect())。复杂的形状需手动定义以提高碰撞精度。
事件处理
  • void sceneEvent(QEvent *event)
    集中处理所有事件(如鼠标、键盘事件)。需根据事件类型调用对应处理函数(如 mousePressEvent)。

  • 示例:处理鼠标事件

    void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
        qDebug() << "Mouse pressed at" << event->pos();
        QGraphicsItem::mousePressEvent(event);
    }
变换与位置
  • void setPos(const QPointF &pos):设置项的位置(局部坐标系原点)。
  • void setRotation(qreal angle):设置旋转角度(单位:度)。
  • void setScale(qreal scale):设置缩放比例。
  • QTransform transform() const:获取项的变换矩阵。
标志位控制
  • setFlags(GraphicsItemFlags flags)
    常用标志:
    • ItemIsMovable:允许用户拖动项。
    • ItemIsSelectable:允许选中项。
    • ItemIgnoresTransformations:忽略视图变换(提升性能)。

3. 自定义 QGraphicsItem 步骤

  1. 继承 QGraphicsItem

    class MyItem : public QGraphicsItem {
    public:
        QRectF boundingRect() const override { return QRectF(-50, -50, 100, 100); }
        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override {
            painter->setBrush(Qt::blue);
            painter->drawEllipse(-50, -50, 100, 100);
        }
    };
  2. 添加到场景

    MyItem *item = new MyItem;
    scene->addItem(item);
  3. 处理事件(可选)
    重写 mousePressEventhoverEnterEvent 等函数。


4. 高级用法

性能优化
  • 减少 boundingRect() 和 paint() 的计算量
  • 使用 ItemIgnoresTransformations 标志:若项不受视图变换影响,可提升性能。
  • 批量更新:通过 setPos() 等函数移动项时,尽量合并多次操作为一次。
动画支持
  • 使用 QVariantAnimation 改变项的属性:

    QVariantAnimation *anim = new QVariantAnimation;
    anim->setTargetObject(item);
    anim->setPropertyName("pos");
    anim->setStartValue(QPointF(0, 0));
    anim->setEndValue(QPointF(100, 100));
    anim->start();
组合项
  • 使用 QGraphicsItemGroup 将多个项组合为一个逻辑单元:

    QVariantAnimation *anim = new QVariantAnimation;
    anim->setTargetObject(item);
    anim->setPropertyName("pos");
    anim->setStartValue(QPointF(0, 0));
    anim->setEndValue(QPointF(100, 100));
    anim->start();

5. 坐标系转换

  • 场景坐标系:所有项的全局坐标系。

  • 视图坐标系:视图的逻辑坐标系(受缩放、滚动影响)。

  • 项坐标系:以项的原点 (0,0) 为基准的局部坐标系。

  • 常用转换函数

    • mapToScene():将项坐标转换为场景坐标。
    • mapFromScene():将场景坐标转换为项坐标。
    • mapToItem():将当前项坐标转换为另一项的坐标。

6. 示例:交互式矩形项

class DraggableRect : public QGraphicsRectItem {
public:
    DraggableRect(qreal x, qreal y, qreal w, qreal h) 
        : QGraphicsRectItem(x, y, w, h) {
        setFlag(QGraphicsItem::ItemIsMovable);
        setBrush(Qt::yellow);
    }

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
        qDebug() << "Pressed at" << event->pos();
        QGraphicsRectItem::mousePressEvent(event);
    }
};

// 使用
DraggableRect *rect = new DraggableRect(0, 0, 100, 50);
scene->addItem(rect);

总结

QGraphicsItem 通过灵活的设计支持复杂的 2D 图形交互需求。关键点包括:

  • 正确实现 boundingRect() 和 paint()
  • 利用标志位控制行为。
  • 合理处理事件和坐标系转换。
  • 结合 QGraphicsView 和 QGraphicsScene 实现完整图形界面。

2.QGraphicsItem的contains应用举例 

QGraphicsItem 的 contains() 方法用于判断一个点是否位于项的边界或形状内,常用于精确的事件处理(如鼠标点击检测)或碰撞检测。以下是应用举例和详细说明:


1. 默认行为

  • 默认实现contains() 默认使用 shape() 的返回值(若未重写则使用 boundingRect())判断点是否在项内。
  • 局限性:若项的 boundingRect() 比实际绘制区域大(如圆形),点击矩形边缘可能误判。

2. 应用场景

  • 精确点击检测:确保只有点击图形内部时触发事件。
  • 碰撞检测:判断两个复杂形状的项是否相交。
  • 悬停效果:仅在鼠标进入图形内部时高亮。

3. 示例:圆形项的精确点击检测

步骤 1:继承 QGraphicsItem

#include <QGraphicsItem>
#include <QPainter>

class CircleItem : public QGraphicsItem {
public:
    CircleItem(qreal radius, QGraphicsItem *parent = nullptr)
        : m_radius(radius) {
        setFlag(QGraphicsItem::ItemIsMovable); // 允许拖动
    }

    QRectF boundingRect() const override {
        return QRectF(-m_radius, -m_radius, 2*m_radius, 2*m_radius);
    }

    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override {
        painter->setBrush(Qt::blue);
        painter->drawEllipse(-m_radius, -m_radius, 2*m_radius, 2*m_radius);
    }

protected:
    // 重写 contains() 实现精确检测
    bool contains(const QPointF &point) const override {
        // 计算点到圆心的距离
        qreal dx = point.x();
        qreal dy = point.y();
        return (dx*dx + dy*dy) <= (m_radius * m_radius);
    }

private:
    qreal m_radius;
};
步骤 2:在事件处理中使用 contains()

void CircleItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
    if (contains(event->pos())) {
        qDebug() << "Clicked inside the circle!";
    } else {
        qDebug() << "Clicked outside the circle (but inside bounding rect).";
    }
    QGraphicsItem::mousePressEvent(event);
}
步骤 3:添加到场景

CircleItem *circle = new CircleItem(50);
scene->addItem(circle);

4. 关键说明

  • 坐标系统event->pos() 返回的是相对于项原点的局部坐标,可直接用于 contains()
  • 性能优化:对于复杂形状,可预计算 QPainterPath 并使用其 contains() 方法:

    QPainterPath m_path; // 在构造函数中初始化
    m_path.addEllipse(-m_radius, -m_radius, 2*m_radius, 2*m_radius);
    
    bool contains(const QPointF &point) const override {
        return m_path.contains(point);
    }
  • 与碰撞检测结合:使用 collidesWithItem() 时,若需精确检测,需同时重写 contains() 和 shape()

5. 效果对比

  • 默认行为:点击圆形包围矩形(如边缘)会触发事件。
  • 自定义 contains():仅点击圆形内部时触发事件,提升交互精度。

通过重写 contains(),可以实现更精确的交互逻辑,适用于需要严格区分图形边界的场景。

 

<think>我们面临的问题是如何在Qt中高效地绘制和操作大量(上千个)矩形图形项。每个矩形都需要支持选中、移动、旋转和缩放等交互操作。直接使用QGraphicsRectItem会导致性能急剧下降,因为每个图形项都是独立的对象,会占用大量内存和CPU资源。因此,我们需要采用更高效的方案。 解决方案: 1. 使用自定义图形项(Custom Graphics Item)合并绘制:创建一个自定义的QGraphicsItem,在该项中统一绘制所有矩形,并管理它们的状态(位置、旋转、缩放、选中等)。这样可以将上千个矩形的绘制合并为一次OpenGL绘制调用,大大提高性能。 2. 使用顶点缓冲对象(VBO)和OpenGL进行绘制:在自定义图形项中,我们使用OpenGL的VBO来存储所有矩形的顶点数据,并一次性提交给GPU进行绘制。这样可以极大减少CPU到GPU的数据传输开销。 3. 实现空间索引(如四叉树)进行快速命中测试:为了支持鼠标交互(如选中某个矩形),我们需要快速判断鼠标点击位置在哪个矩形上。如果遍历所有矩形,当数量很大时效率很低。使用空间索引(如四叉树)可以快速定位可能被命中的矩形,然后进行精确判断。 4. 使用变换矩阵实现旋转和缩放:每个矩形的旋转和缩放可以通过模型变换矩阵实现。在绘制每个矩形时,应用对应的变换矩阵。 5. 批量处理状态变化:当需要同时改变多个矩形的状态(如同时移动多个选中的矩形)时,批量更新它们的位置,然后一次性重绘。 具体实现步骤: 步骤1:创建自定义图形项类(继承自QGraphicsItem) 在这个类中,我们将管理所有矩形的数据(位置、大小、旋转角度、缩放因子、选中状态等)以及一个用于空间索引的结构(如四叉树)。 步骤2:重写paint()函数 在paint()函数中,使用OpenGL命令进行绘制。我们使用VBO来存储顶点数据,并且为每个矩形设置模型变换矩阵(包括平移、旋转和缩放)。同时,根据选中状态设置不同的颜色。 步骤3:重写boundingRect()函数 返回整个场景的边界,或者当前所有矩形的合并边界。 步骤4:实现鼠标事件处理 在自定义图形项中处理鼠标事件(mousePressEvent, mouseMoveEvent, mouseReleaseEvent),通过空间索引快速找到被点击的矩形,然后进行交互操作(如选中、移动、旋转、缩放)。 步骤5:使用四叉树(Quadtree)进行空间索引 四叉树用于快速查询某个区域内的矩形。当图形项很多时,四叉树可以大大减少需要检查的矩形数量。 步骤6:优化重绘区域 当只改变一个矩形时,只重绘该矩形所在的区域,而不是整个视图。可以使用QGraphicsItem::update(const QRectF &rect)来指定重绘区域。 下面是一个简化的代码框架: ```cpp // customrectitem.h #include <QGraphicsItem> #include <QVector> #include <QOpenGLBuffer> #include <QOpenGLShaderProgram> #include <QOpenGLFunctions> #include <QMatrix4x4> // 定义一个矩形的数据结构 struct RectData { QRectF rect; qreal rotation; // 旋转角度(度) qreal scale; // 缩放因子 bool selected; QMatrix4x4 transform; // 变换矩阵(由位置、旋转、缩放计算得到) // 其他属性,如颜色等 }; class CustomRectItem : public QGraphicsItem, protected QOpenGLFunctions { public: CustomRectItem(); ~CustomRectItem(); QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; // 添加矩形 void addRect(const QRectF &rect); // 鼠标事件 void mousePressEvent(QGraphicsSceneMouseEvent *event) override; void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; private: void updateTransformations(); // 更新所有矩形的变换矩阵 void updateBuffer(); // 更新顶点缓冲 QVector<RectData> m_rects; QOpenGLBuffer m_vbo; // 顶点缓冲对象 QOpenGLShaderProgram m_program; // 着色器程序 bool m_initialized; // 空间索引结构(四叉树)这里省略,实际需要实现 }; // customrectitem.cpp #include "customrectitem.h" CustomRectItem::CustomRectItem() : m_initialized(false) { setFlag(ItemSendsGeometryChanges); m_vbo.create(); } CustomRectItem::~CustomRectItem() { m_vbo.destroy(); } QRectF CustomRectItem::boundingRect() const { // 计算所有矩形的合并边界 QRectF bounds; for (const RectData &rd : m_rects) { bounds = bounds.united(rd.rect); } return bounds; } void CustomRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(painter); // 我们不使用QPainter,而是直接使用OpenGL Q_UNUSED(option); Q_UNUSED(widget); if (m_rects.isEmpty()) return; // 初始化OpenGL函数和着色器 if (!m_initialized) { initializeOpenGLFunctions(); // 初始化着色器程序 m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec2 vertexPosition;" "uniform mat4 modelMatrix;" "uniform mat4 viewMatrix;" "uniform mat4 projectionMatrix;" "void main() {" " gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(vertexPosition, 0.0, 1.0);" "}"); m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, "uniform vec4 color;" "void main() {" " gl_FragColor = color;" "}"); m_program.link(); m_initialized = true; } // 使用OpenGL绘制 glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_program.bind(); // 设置视图和投影矩阵(这里使用单位矩阵,实际可能需要根据视图调整) QMatrix4x4 viewMatrix; QMatrix4x4 projectionMatrix; m_program.setUniformValue("viewMatrix", viewMatrix); m_program.setUniformValue("projectionMatrix", projectionMatrix); // 绑定顶点缓冲 m_vbo.bind(); m_vbo.allocate(/*顶点数据指针*/, /*数据大小*/); int vertexLocation = m_program.attributeLocation("vertexPosition"); m_program.enableAttributeArray(vertexLocation); m_program.setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 2, 0); // 绘制每个矩形 for (int i = 0; i < m_rects.size(); ++i) { const RectData &rd = m_rects[i]; // 设置模型矩阵(包括平移、旋转、缩放) m_program.setUniformValue("modelMatrix", rd.transform); // 设置颜色(选中状态用不同颜色) if (rd.selected) { m_program.setUniformValue("color", QVector4D(1.0f, 0.0f, 0.0f, 0.5f)); } else { m_program.setUniformValue("color", QVector4D(0.0f, 0.0f, 1.0f, 0.5f)); } // 绘制矩形(每个矩形由两个三角形组成,共6个顶点) glDrawArrays(GL_TRIANGLES, i * 6, 6); } m_program.disableAttributeArray(vertexLocation); m_vbo.release(); m_program.release(); } void CustomRectItem::addRect(const QRectF &rect) { RectData rd; rd.rect = rect; rd.rotation = 0; rd.scale = 1.0; rd.selected = false; m_rects.append(rd); updateTransformations(); updateBuffer(); // 更新顶点缓冲 update(); // 请求重绘 } void CustomRectItem::updateTransformations() { for (RectData &rd : m_rects) { QMatrix4x4 matrix; // 先平移到中心,然后旋转和缩放,再平移回原位 QPointF center = rd.rect.center(); matrix.translate(center.x(), center.y()); matrix.rotate(rd.rotation, 0, 0, 1); matrix.scale(rd.scale, rd.scale); matrix.translate(-center.x(), -center.y()); // 设置到rectData中 rd.transform = matrix; } } void CustomRectItem::updateBuffer() { // 生成所有矩形的顶点数据(每个矩形由两个三角形组成,共6个顶点) QVector<float> vertices; for (const RectData &rd : m_rects) { // 矩形的四个顶点(未变换的) QVector<QPointF> points; points << rd.rect.topLeft() << rd.rect.topRight() << rd.rect.bottomRight() << rd.rect.bottomLeft(); // 第一个三角形:左上、右上、左下 vertices << points[0].x() << points[0].y(); vertices << points[1].x() << points[1].y(); vertices << points[3].x() << points[3].y(); // 第二个三角形:右上、右下、左下 vertices << points[1].x() << points[1].y(); vertices << points[2].x() << points[2].y(); vertices << points[3].x() << points[3].y(); } m_vbo.bind(); m_vbo.allocate(vertices.constData(), vertices.size() * sizeof(float)); m_vbo.release(); } // 鼠标事件处理(简化版,实际需要实现空间索引查询) void CustomRectItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { QPointF pos = event->pos(); // 遍历所有矩形(实际应该用四叉树查询)找到点击的矩形 for (int i = 0; i < m_rects.size(); ++i) { if (m_rects[i].rect.contains(pos)) { m_rects[i].selected = !m_rects[i].selected; update(); // 重绘该项 break; } } event->accept(); } void CustomRectItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { // 移动选中的矩形 QPointF offset = event->pos() - event->lastPos(); for (int i = 0; i < m_rects.size(); ++i) { if (m_rects[i].selected) { m_rects[i].rect.translate(offset); } } updateTransformations(); updateBuffer(); update(); event->accept(); } void CustomRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { event->accept(); } ``` 注意:上述代码是一个简化版本,实际应用中需要: 1. 实现四叉树空间索引,以加速鼠标点击测试。 2. 考虑旋转和缩放后的点击测试(需要将点逆变换到矩形局部坐标系)。 3. 优化顶点缓冲的更新(只更新变化的部分)。 4. 实现旋转和缩放的交互(例如通过控制点)。 性能优化关键点: - 使用VBO一次提交所有顶点数据,减少绘制调用次数。 - 使用着色器进行变换,避免在CPU上进行顶点变换。 - 使用空间索引加速命中测试。 - 只更新变化部分的顶点数据(如果可能)。 对于旋转和缩放的交互,我们可以在选中矩形时绘制控制点(例如8个控制点),然后根据鼠标拖动不同的控制点实现不同方向的缩放或旋转。这需要额外的代码来处理控制点的绘制和交互。 总结:通过自定义图形项合并绘制,并利用OpenGL的VBO和着色器,可以高效地绘制和操作大量矩形。同时,空间索引和局部更新策略可以进一步提升交互性能。 引用说明: [^1]: 使用自定义图形项合并绘制大量图形元素是Qt中处理大量图形项的常用优化方法。 [^2]: 空间索引(如四叉树)可以加速图形项的查找,在交互操作中非常重要。 [^3]: 使用OpenGL进行绘制可以充分利用硬件加速,尤其当图形数量很大时。 §§相关问题§§ 1. 在Qt中如何实现四叉树空间索引? 2. 如何优化OpenGL绘制以支持动态更新部分图形? 3. 如何处理图形项的旋转和缩放交互?</think>### 高性能绘制大量可交互矩形的实现方案 在Qt中处理上千个可交互矩形(支持选中、移动、旋转、缩放)时,需要综合运用多种优化技术。以下是高性能实现方案: --- #### 核心优化策略 1. **批量绘制技术**(关键优化) 2. **空间分区索引**(快速命中检测) 3. **细节层次渲染**(LOD) 4. **GPU加速**(OpenGL/Vulkan) 5. **增量更新机制** --- ### 完整实现代码(关键部分) #### 1. 批量绘制矩形类(使用OpenGL) ```cpp // batchrectitem.h #include <QGraphicsItem> #include <QVector> #include <QOpenGLBuffer> #include <QOpenGLShaderProgram> class BatchRectItem : public QGraphicsItem, protected QOpenGLFunctions { public: explicit BatchRectItem(int capacity = 1000); ~BatchRectItem(); void addRect(const QRectF& rect); void updateRect(int index, const QRectF& rect); void setSelected(int index, bool selected); QRectF boundingRect() const override; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; private: void updateBuffer(); struct RectData { QRectF rect; bool selected = false; }; QVector<RectData> m_rects; QOpenGLBuffer m_vbo; QOpenGLShaderProgram m_program; bool m_bufferDirty = true; }; ``` ```cpp // batchrectitem.cpp BatchRectItem::BatchRectItem(int capacity) { setFlag(ItemHasNoContents, false); // 启用自定义绘制 // 初始化OpenGL资源 initializeOpenGLFunctions(); m_vbo.create(); m_vbo.setUsagePattern(QOpenGLBuffer::DynamicDraw); // 设置着色器程序 m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec2 vertex;" "uniform mat4 matrix;" "void main() {" " gl_Position = matrix * vec4(vertex, 0.0, 1.0);" "}"); m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, "uniform vec4 color;" "void main() {" " gl_FragColor = color;" "}"); m_program.link(); m_rects.reserve(capacity); } void BatchRectItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(painter); // 更新顶点缓冲 if (m_bufferDirty) { updateBuffer(); m_bufferDirty = false; } // OpenGL绘制 glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_program.bind(); m_vbo.bind(); // 设置视图矩阵 QMatrix4x4 matrix; matrix.ortho(0, option->exposedRect.width(), option->exposedRect.height(), 0, -1, 1); m_program.setUniformValue("matrix", matrix); // 绘制所有矩形 for (int i = 0; i < m_rects.size(); ++i) { const auto& rect = m_rects[i]; QColor color = rect.selected ? Qt::red : Qt::blue; m_program.setUniformValue("color", color); // 绘制矩形(每个矩形6个顶点,两个三角形) glDrawArrays(GL_TRIANGLES, i * 6, 6); } m_vbo.release(); m_program.release(); } void BatchRectItem::updateBuffer() { QVector<float> vertices; vertices.reserve(m_rects.size() * 12); // 每个矩形12个浮点数 for (const auto& rect : m_rects) { // 三角形1: 左上->右上->左下 vertices << rect.rect.left() << rect.rect.top(); vertices << rect.rect.right() << rect.rect.top(); vertices << rect.rect.left() << rect.rect.bottom(); // 三角形2: 右上->右下->左下 vertices << rect.rect.right() << rect.rect.top(); vertices << rect.rect.right() << rect.rect.bottom(); vertices << rect.rect
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值