/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the demonstration applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtbox.h" constexpr qreal ROTATE_SPEED_X = 30.0 / 1000.0; constexpr qreal ROTATE_SPEED_Y = 20.0 / 1000.0; constexpr qreal ROTATE_SPEED_Z = 40.0 / 1000.0; constexpr int MAX_ITEM_SIZE = 512; constexpr int MIN_ITEM_SIZE = 16; //============================================================================// // ItemBase // //============================================================================// ItemBase::ItemBase(int size, int x, int y) : m_size(size), m_startTime(QTime::currentTime()) { setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setAcceptHoverEvents(true); setPos(x, y); } QRectF ItemBase::boundingRect() const { return QRectF(-m_size / 2, -m_size / 2, m_size, m_size); } void ItemBase::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { if (option->state & QStyle::State_Selected) { painter->setRenderHint(QPainter::Antialiasing, true); if (option->state & QStyle::State_HasFocus) painter->setPen(Qt::yellow); else painter->setPen(Qt::white); painter->drawRect(boundingRect()); painter->drawLine(m_size / 2 - 9, m_size / 2, m_size / 2, m_size / 2 - 9); painter->drawLine(m_size / 2 - 6, m_size / 2, m_size / 2, m_size / 2 - 6); painter->drawLine(m_size / 2 - 3, m_size / 2, m_size / 2, m_size / 2 - 3); painter->setRenderHint(QPainter::Antialiasing, false); } } void ItemBase::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { if (!isSelected() && scene()) { scene()->clearSelection(); setSelected(true); } QMenu menu; QAction *delAction = menu.addAction("Delete"); QAction *newAction = menu.addAction("New"); QAction *growAction = menu.addAction("Grow"); QAction *shrinkAction = menu.addAction("Shrink"); QAction *selectedAction = menu.exec(event->screenPos()); if (selectedAction == delAction) deleteSelectedItems(scene()); else if (selectedAction == newAction) duplicateSelectedItems(scene()); else if (selectedAction == growAction) growSelectedItems(scene()); else if (selectedAction == shrinkAction) shrinkSelectedItems(scene()); } void ItemBase::duplicateSelectedItems(QGraphicsScene *scene) { if (!scene) return; const QList selected = scene->selectedItems(); for (QGraphicsItem *item : selected) { ItemBase *itemBase = qgraphicsitem_cast(item); if (itemBase) scene->addItem(itemBase->createNew(itemBase->m_size, itemBase->pos().x() + itemBase->m_size, itemBase->pos().y())); } } void ItemBase::deleteSelectedItems(QGraphicsScene *scene) { if (!scene) return; const QList selected = scene->selectedItems(); for (QGraphicsItem *item : selected) { ItemBase *itemBase = qgraphicsitem_cast(item); if (itemBase) delete itemBase; } } void ItemBase::growSelectedItems(QGraphicsScene *scene) { if (!scene) return; const QList selected = scene->selectedItems(); for (QGraphicsItem *item : selected) { ItemBase *itemBase = qgraphicsitem_cast(item); if (itemBase) { itemBase->prepareGeometryChange(); itemBase->m_size *= 2; if (itemBase->m_size > MAX_ITEM_SIZE) itemBase->m_size = MAX_ITEM_SIZE; } } } void ItemBase::shrinkSelectedItems(QGraphicsScene *scene) { if (!scene) return; const QList selected = scene->selectedItems(); for (QGraphicsItem *item : selected) { ItemBase *itemBase = qgraphicsitem_cast(item); if (itemBase) { itemBase->prepareGeometryChange(); itemBase->m_size /= 2; if (itemBase->m_size < MIN_ITEM_SIZE) itemBase->m_size = MIN_ITEM_SIZE; } } } void ItemBase::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (m_isResizing) { int dx = int(2.0 * event->pos().x()); int dy = int(2.0 * event->pos().y()); prepareGeometryChange(); m_size = (dx > dy ? dx : dy); if (m_size < MIN_ITEM_SIZE) m_size = MIN_ITEM_SIZE; else if (m_size > MAX_ITEM_SIZE) m_size = MAX_ITEM_SIZE; } else { QGraphicsItem::mouseMoveEvent(event); } } void ItemBase::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { if (m_isResizing || (isInResizeArea(event->pos()) && isSelected())) setCursor(Qt::SizeFDiagCursor); else setCursor(Qt::ArrowCursor); QGraphicsItem::hoverMoveEvent(event); } void ItemBase::mousePressEvent(QGraphicsSceneMouseEvent *event) { static qreal z = 0.0; setZValue(z += 1.0); if (event->button() == Qt::LeftButton && isInResizeArea(event->pos())) { m_isResizing = true; } else { QGraphicsItem::mousePressEvent(event); } } void ItemBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton && m_isResizing) { m_isResizing = false; } else { QGraphicsItem::mouseReleaseEvent(event); } } void ItemBase::keyPressEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_Delete: deleteSelectedItems(scene()); break; case Qt::Key_Insert: duplicateSelectedItems(scene()); break; case Qt::Key_Plus: growSelectedItems(scene()); break; case Qt::Key_Minus: shrinkSelectedItems(scene()); break; default: QGraphicsItem::keyPressEvent(event); break; } } void ItemBase::wheelEvent(QGraphicsSceneWheelEvent *event) { prepareGeometryChange(); m_size = int(m_size * qExp(-event->delta() / 600.0)); m_size = qBound(MIN_ITEM_SIZE, m_size, MAX_ITEM_SIZE); } int ItemBase::type() const { return Type; } bool ItemBase::isInResizeArea(const QPointF &pos) { return (-pos.y() < pos.x() - m_size + 9); } //============================================================================// // QtBox // //============================================================================// QtBox::QtBox(int size, int x, int y) : ItemBase(size, x, y) { for (int i = 0; i < 8; ++i) { m_vertices[i].setX(i & 1 ? 0.5f : -0.5f); m_vertices[i].setY(i & 2 ? 0.5f : -0.5f); m_vertices[i].setZ(i & 4 ? 0.5f : -0.5f); } for (int i = 0; i < 4; ++i) { m_texCoords[i].setX(i & 1 ? 1.0f : 0.0f); m_texCoords[i].setY(i & 2 ? 1.0f : 0.0f); } m_normals[0] = QVector3D(-1.0f, 0.0f, 0.0f); m_normals[1] = QVector3D(1.0f, 0.0f, 0.0f); m_normals[2] = QVector3D(0.0f, -1.0f, 0.0f); m_normals[3] = QVector3D(0.0f, 1.0f, 0.0f); m_normals[4] = QVector3D(0.0f, 0.0f, -1.0f); m_normals[5] = QVector3D(0.0f, 0.0f, 1.0f); } QtBox::~QtBox() { delete m_texture; } ItemBase *QtBox::createNew(int size, int x, int y) { return new QtBox(size, x, y); } void QtBox::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QRectF rect = boundingRect().translated(pos()); float width = float(painter->device()->width()); float height = float(painter->device()->height()); float left = 2.0f * float(rect.left()) / width - 1.0f; float right = 2.0f * float(rect.right()) / width - 1.0f; float top = 1.0f - 2.0f * float(rect.top()) / height; float bottom = 1.0f - 2.0f * float(rect.bottom()) / height; float moveToRectMatrix[] = { 0.5f * (right - left), 0.0f, 0.0f, 0.0f, 0.0f, 0.5f * (bottom - top), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f * (right + left), 0.5f * (bottom + top), 0.0f, 1.0f }; painter->beginNativePainting(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadMatrixf(moveToRectMatrix); qgluPerspective(60.0, 1.0, 0.01, 10.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); //glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_COLOR_MATERIAL); glEnable(GL_NORMALIZE); if (m_texture == nullptr) m_texture = new GLTexture2D(":/res/boxes/qt-logo.jpg", 64, 64); m_texture->bind(); glEnable(GL_TEXTURE_2D); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); float lightColour[] = {1.0f, 1.0f, 1.0f, 1.0f}; float lightDir[] = {0.0f, 0.0f, 1.0f, 0.0f}; glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColour); glLightfv(GL_LIGHT0, GL_POSITION, lightDir); glEnable(GL_LIGHT0); glTranslatef(0.0f, 0.0f, -1.5f); glRotatef(ROTATE_SPEED_X * m_startTime.msecsTo(QTime::currentTime()), 1.0f, 0.0f, 0.0f); glRotatef(ROTATE_SPEED_Y * m_startTime.msecsTo(QTime::currentTime()), 0.0f, 1.0f, 0.0f); glRotatef(ROTATE_SPEED_Z * m_startTime.msecsTo(QTime::currentTime()), 0.0f, 0.0f, 1.0f); int dt = m_startTime.msecsTo(QTime::currentTime()); if (dt < 500) glScalef(dt / 500.0f, dt / 500.0f, dt / 500.0f); for (int dir = 0; dir < 3; ++dir) { glColor4f(1.0f, 1.0f, 1.0f, 1.0); glBegin(GL_TRIANGLE_STRIP); glNormal3fv(reinterpret_cast(&m_normals[2 * dir + 0])); for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { glTexCoord2fv(reinterpret_cast(&m_texCoords[(j << 1) | i])); glVertex3fv(reinterpret_cast(&m_vertices[(i << ((dir + 2) % 3)) | (j << ((dir + 1) % 3))])); } } glEnd(); glBegin(GL_TRIANGLE_STRIP); glNormal3fv(reinterpret_cast(&m_normals[2 * dir + 1])); for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { glTexCoord2fv(reinterpret_cast(&m_texCoords[(j << 1) | i])); glVertex3fv(reinterpret_cast(&m_vertices[(1 << dir) | (i << ((dir + 1) % 3)) | (j << ((dir + 2) % 3))])); } } glEnd(); } m_texture->unbind(); //glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_LIGHTING); glDisable(GL_COLOR_MATERIAL); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHT0); glDisable(GL_NORMALIZE); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); painter->endNativePainting(); ItemBase::paint(painter, option, widget); } //============================================================================// // CircleItem // //============================================================================// CircleItem::CircleItem(int size, int x, int y) : ItemBase(size, x, y) , m_color(QColor::fromHsv(QRandomGenerator::global()->bounded(360), 255, 255)) {} void CircleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { int dt = m_startTime.msecsTo(QTime::currentTime()); qreal r0 = 0.5 * m_size * (1.0 - qExp(-0.001 * ((dt + 3800) % 4000))); qreal r1 = 0.5 * m_size * (1.0 - qExp(-0.001 * ((dt + 0) % 4000))); qreal r2 = 0.5 * m_size * (1.0 - qExp(-0.001 * ((dt + 1800) % 4000))); qreal r3 = 0.5 * m_size * (1.0 - qExp(-0.001 * ((dt + 2000) % 4000))); if (r0 > r1) r0 = 0.0; if (r2 > r3) r2 = 0.0; QPainterPath path; path.moveTo(r1, 0.0); path.arcTo(-r1, -r1, 2 * r1, 2 * r1, 0.0, 360.0); path.lineTo(r0, 0.0); path.arcTo(-r0, -r0, 2 * r0, 2 * r0, 0.0, -360.0); path.closeSubpath(); path.moveTo(r3, 0.0); path.arcTo(-r3, -r3, 2 * r3, 2 * r3, 0.0, 360.0); path.lineTo(r0, 0.0); path.arcTo(-r2, -r2, 2 * r2, 2 * r2, 0.0, -360.0); path.closeSubpath(); painter->setRenderHint(QPainter::Antialiasing, true); painter->setBrush(QBrush(m_color)); painter->setPen(Qt::NoPen); painter->drawPath(path); painter->setBrush(Qt::NoBrush); painter->setPen(Qt::SolidLine); painter->setRenderHint(QPainter::Antialiasing, false); ItemBase::paint(painter, option, widget); } ItemBase *CircleItem::createNew(int size, int x, int y) { return new CircleItem(size, x, y); } //============================================================================// // SquareItem // //============================================================================// SquareItem::SquareItem(int size, int x, int y) : ItemBase(size, x, y) , m_image(QPixmap(":/res/boxes/square.jpg")) {} void SquareItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { int dt = m_startTime.msecsTo(QTime::currentTime()); QTransform oldTransform = painter->worldTransform(); int dtMod = dt % 2000; qreal amp = 0.002 * (dtMod < 1000 ? dtMod : 2000 - dtMod) - 1.0; qreal scale = 0.6 + 0.2 * amp * amp; painter->setWorldTransform(QTransform().rotate(15.0 * amp).scale(scale, scale), true); painter->drawPixmap(-m_size / 2, -m_size / 2, m_size, m_size, m_image); painter->setWorldTransform(oldTransform, false); ItemBase::paint(painter, option, widget); } ItemBase *SquareItem::createNew(int size, int x, int y) { return new SquareItem(size, x, y); }