1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the demonstration applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "qtbox.h" |
52 | |
53 | constexpr qreal ROTATE_SPEED_X = 30.0 / 1000.0; |
54 | constexpr qreal ROTATE_SPEED_Y = 20.0 / 1000.0; |
55 | constexpr qreal ROTATE_SPEED_Z = 40.0 / 1000.0; |
56 | constexpr int MAX_ITEM_SIZE = 512; |
57 | constexpr int MIN_ITEM_SIZE = 16; |
58 | |
59 | //============================================================================// |
60 | // ItemBase // |
61 | //============================================================================// |
62 | |
63 | ItemBase::ItemBase(int size, int x, int y) : m_size(size), m_startTime(QTime::currentTime()) |
64 | { |
65 | setFlag(flag: QGraphicsItem::ItemIsMovable, enabled: true); |
66 | setFlag(flag: QGraphicsItem::ItemIsSelectable, enabled: true); |
67 | setFlag(flag: QGraphicsItem::ItemIsFocusable, enabled: true); |
68 | setAcceptHoverEvents(true); |
69 | setPos(ax: x, ay: y); |
70 | } |
71 | |
72 | QRectF ItemBase::boundingRect() const |
73 | { |
74 | return QRectF(-m_size / 2, -m_size / 2, m_size, m_size); |
75 | } |
76 | |
77 | void ItemBase::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) |
78 | { |
79 | if (option->state & QStyle::State_Selected) { |
80 | painter->setRenderHint(hint: QPainter::Antialiasing, on: true); |
81 | if (option->state & QStyle::State_HasFocus) |
82 | painter->setPen(Qt::yellow); |
83 | else |
84 | painter->setPen(Qt::white); |
85 | painter->drawRect(rect: boundingRect()); |
86 | |
87 | painter->drawLine(x1: m_size / 2 - 9, y1: m_size / 2, x2: m_size / 2, y2: m_size / 2 - 9); |
88 | painter->drawLine(x1: m_size / 2 - 6, y1: m_size / 2, x2: m_size / 2, y2: m_size / 2 - 6); |
89 | painter->drawLine(x1: m_size / 2 - 3, y1: m_size / 2, x2: m_size / 2, y2: m_size / 2 - 3); |
90 | |
91 | painter->setRenderHint(hint: QPainter::Antialiasing, on: false); |
92 | } |
93 | } |
94 | |
95 | void ItemBase::(QGraphicsSceneContextMenuEvent *event) |
96 | { |
97 | if (!isSelected() && scene()) { |
98 | scene()->clearSelection(); |
99 | setSelected(true); |
100 | } |
101 | |
102 | QMenu ; |
103 | QAction *delAction = menu.addAction(text: "Delete" ); |
104 | QAction *newAction = menu.addAction(text: "New" ); |
105 | QAction *growAction = menu.addAction(text: "Grow" ); |
106 | QAction *shrinkAction = menu.addAction(text: "Shrink" ); |
107 | |
108 | QAction *selectedAction = menu.exec(pos: event->screenPos()); |
109 | |
110 | if (selectedAction == delAction) |
111 | deleteSelectedItems(scene: scene()); |
112 | else if (selectedAction == newAction) |
113 | duplicateSelectedItems(scene: scene()); |
114 | else if (selectedAction == growAction) |
115 | growSelectedItems(scene: scene()); |
116 | else if (selectedAction == shrinkAction) |
117 | shrinkSelectedItems(scene: scene()); |
118 | } |
119 | |
120 | void ItemBase::duplicateSelectedItems(QGraphicsScene *scene) |
121 | { |
122 | if (!scene) |
123 | return; |
124 | |
125 | const QList<QGraphicsItem *> selected = scene->selectedItems(); |
126 | for (QGraphicsItem *item : selected) { |
127 | ItemBase *itemBase = qgraphicsitem_cast<ItemBase *>(item); |
128 | if (itemBase) |
129 | scene->addItem(item: itemBase->createNew(size: itemBase->m_size, x: itemBase->pos().x() + itemBase->m_size, y: itemBase->pos().y())); |
130 | } |
131 | } |
132 | |
133 | void ItemBase::deleteSelectedItems(QGraphicsScene *scene) |
134 | { |
135 | if (!scene) |
136 | return; |
137 | |
138 | const QList<QGraphicsItem *> selected = scene->selectedItems(); |
139 | for (QGraphicsItem *item : selected) { |
140 | ItemBase *itemBase = qgraphicsitem_cast<ItemBase *>(item); |
141 | if (itemBase) |
142 | delete itemBase; |
143 | } |
144 | } |
145 | |
146 | void ItemBase::growSelectedItems(QGraphicsScene *scene) |
147 | { |
148 | if (!scene) |
149 | return; |
150 | |
151 | const QList<QGraphicsItem *> selected = scene->selectedItems(); |
152 | for (QGraphicsItem *item : selected) { |
153 | ItemBase *itemBase = qgraphicsitem_cast<ItemBase *>(item); |
154 | if (itemBase) { |
155 | itemBase->prepareGeometryChange(); |
156 | itemBase->m_size *= 2; |
157 | if (itemBase->m_size > MAX_ITEM_SIZE) |
158 | itemBase->m_size = MAX_ITEM_SIZE; |
159 | } |
160 | } |
161 | } |
162 | |
163 | void ItemBase::shrinkSelectedItems(QGraphicsScene *scene) |
164 | { |
165 | if (!scene) |
166 | return; |
167 | |
168 | const QList<QGraphicsItem *> selected = scene->selectedItems(); |
169 | for (QGraphicsItem *item : selected) { |
170 | ItemBase *itemBase = qgraphicsitem_cast<ItemBase *>(item); |
171 | if (itemBase) { |
172 | itemBase->prepareGeometryChange(); |
173 | itemBase->m_size /= 2; |
174 | if (itemBase->m_size < MIN_ITEM_SIZE) |
175 | itemBase->m_size = MIN_ITEM_SIZE; |
176 | } |
177 | } |
178 | } |
179 | |
180 | void ItemBase::mouseMoveEvent(QGraphicsSceneMouseEvent *event) |
181 | { |
182 | if (m_isResizing) { |
183 | int dx = int(2.0 * event->pos().x()); |
184 | int dy = int(2.0 * event->pos().y()); |
185 | prepareGeometryChange(); |
186 | m_size = (dx > dy ? dx : dy); |
187 | if (m_size < MIN_ITEM_SIZE) |
188 | m_size = MIN_ITEM_SIZE; |
189 | else if (m_size > MAX_ITEM_SIZE) |
190 | m_size = MAX_ITEM_SIZE; |
191 | } else { |
192 | QGraphicsItem::mouseMoveEvent(event); |
193 | } |
194 | } |
195 | |
196 | void ItemBase::hoverMoveEvent(QGraphicsSceneHoverEvent *event) |
197 | { |
198 | if (m_isResizing || (isInResizeArea(pos: event->pos()) && isSelected())) |
199 | setCursor(Qt::SizeFDiagCursor); |
200 | else |
201 | setCursor(Qt::ArrowCursor); |
202 | QGraphicsItem::hoverMoveEvent(event); |
203 | } |
204 | |
205 | void ItemBase::mousePressEvent(QGraphicsSceneMouseEvent *event) |
206 | { |
207 | static qreal z = 0.0; |
208 | setZValue(z += 1.0); |
209 | if (event->button() == Qt::LeftButton && isInResizeArea(pos: event->pos())) { |
210 | m_isResizing = true; |
211 | } else { |
212 | QGraphicsItem::mousePressEvent(event); |
213 | } |
214 | } |
215 | |
216 | void ItemBase::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) |
217 | { |
218 | if (event->button() == Qt::LeftButton && m_isResizing) { |
219 | m_isResizing = false; |
220 | } else { |
221 | QGraphicsItem::mouseReleaseEvent(event); |
222 | } |
223 | } |
224 | |
225 | void ItemBase::keyPressEvent(QKeyEvent *event) |
226 | { |
227 | switch (event->key()) { |
228 | case Qt::Key_Delete: |
229 | deleteSelectedItems(scene: scene()); |
230 | break; |
231 | case Qt::Key_Insert: |
232 | duplicateSelectedItems(scene: scene()); |
233 | break; |
234 | case Qt::Key_Plus: |
235 | growSelectedItems(scene: scene()); |
236 | break; |
237 | case Qt::Key_Minus: |
238 | shrinkSelectedItems(scene: scene()); |
239 | break; |
240 | default: |
241 | QGraphicsItem::keyPressEvent(event); |
242 | break; |
243 | } |
244 | } |
245 | |
246 | void ItemBase::wheelEvent(QGraphicsSceneWheelEvent *event) |
247 | { |
248 | prepareGeometryChange(); |
249 | m_size = int(m_size * qExp(v: -event->delta() / 600.0)); |
250 | m_size = qBound(min: MIN_ITEM_SIZE, val: m_size, max: MAX_ITEM_SIZE); |
251 | } |
252 | |
253 | int ItemBase::type() const |
254 | { |
255 | return Type; |
256 | } |
257 | |
258 | |
259 | bool ItemBase::isInResizeArea(const QPointF &pos) |
260 | { |
261 | return (-pos.y() < pos.x() - m_size + 9); |
262 | } |
263 | |
264 | //============================================================================// |
265 | // QtBox // |
266 | //============================================================================// |
267 | |
268 | QtBox::QtBox(int size, int x, int y) : ItemBase(size, x, y) |
269 | { |
270 | for (int i = 0; i < 8; ++i) { |
271 | m_vertices[i].setX(i & 1 ? 0.5f : -0.5f); |
272 | m_vertices[i].setY(i & 2 ? 0.5f : -0.5f); |
273 | m_vertices[i].setZ(i & 4 ? 0.5f : -0.5f); |
274 | } |
275 | for (int i = 0; i < 4; ++i) { |
276 | m_texCoords[i].setX(i & 1 ? 1.0f : 0.0f); |
277 | m_texCoords[i].setY(i & 2 ? 1.0f : 0.0f); |
278 | } |
279 | m_normals[0] = QVector3D(-1.0f, 0.0f, 0.0f); |
280 | m_normals[1] = QVector3D(1.0f, 0.0f, 0.0f); |
281 | m_normals[2] = QVector3D(0.0f, -1.0f, 0.0f); |
282 | m_normals[3] = QVector3D(0.0f, 1.0f, 0.0f); |
283 | m_normals[4] = QVector3D(0.0f, 0.0f, -1.0f); |
284 | m_normals[5] = QVector3D(0.0f, 0.0f, 1.0f); |
285 | } |
286 | |
287 | QtBox::~QtBox() |
288 | { |
289 | delete m_texture; |
290 | } |
291 | |
292 | ItemBase *QtBox::createNew(int size, int x, int y) |
293 | { |
294 | return new QtBox(size, x, y); |
295 | } |
296 | |
297 | void QtBox::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
298 | { |
299 | QRectF rect = boundingRect().translated(p: pos()); |
300 | float width = float(painter->device()->width()); |
301 | float height = float(painter->device()->height()); |
302 | |
303 | float left = 2.0f * float(rect.left()) / width - 1.0f; |
304 | float right = 2.0f * float(rect.right()) / width - 1.0f; |
305 | float top = 1.0f - 2.0f * float(rect.top()) / height; |
306 | float bottom = 1.0f - 2.0f * float(rect.bottom()) / height; |
307 | float moveToRectMatrix[] = { |
308 | 0.5f * (right - left), 0.0f, 0.0f, 0.0f, |
309 | 0.0f, 0.5f * (bottom - top), 0.0f, 0.0f, |
310 | 0.0f, 0.0f, 1.0f, 0.0f, |
311 | 0.5f * (right + left), 0.5f * (bottom + top), 0.0f, 1.0f |
312 | }; |
313 | |
314 | painter->beginNativePainting(); |
315 | |
316 | glMatrixMode(GL_PROJECTION); |
317 | glPushMatrix(); |
318 | glLoadMatrixf(m: moveToRectMatrix); |
319 | qgluPerspective(fovy: 60.0, aspect: 1.0, zNear: 0.01, zFar: 10.0); |
320 | |
321 | glMatrixMode(GL_MODELVIEW); |
322 | glPushMatrix(); |
323 | glLoadIdentity(); |
324 | |
325 | //glEnable(GL_DEPTH_TEST); |
326 | glEnable(GL_CULL_FACE); |
327 | glEnable(GL_LIGHTING); |
328 | glEnable(GL_COLOR_MATERIAL); |
329 | glEnable(GL_NORMALIZE); |
330 | |
331 | if (m_texture == nullptr) |
332 | m_texture = new GLTexture2D(":/res/boxes/qt-logo.jpg" , 64, 64); |
333 | m_texture->bind(); |
334 | glEnable(GL_TEXTURE_2D); |
335 | |
336 | glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); |
337 | float lightColour[] = {1.0f, 1.0f, 1.0f, 1.0f}; |
338 | float lightDir[] = {0.0f, 0.0f, 1.0f, 0.0f}; |
339 | glLightfv(GL_LIGHT0, GL_DIFFUSE, params: lightColour); |
340 | glLightfv(GL_LIGHT0, GL_POSITION, params: lightDir); |
341 | glEnable(GL_LIGHT0); |
342 | |
343 | glTranslatef(x: 0.0f, y: 0.0f, z: -1.5f); |
344 | glRotatef(angle: ROTATE_SPEED_X * m_startTime.msecsTo(QTime::currentTime()), x: 1.0f, y: 0.0f, z: 0.0f); |
345 | glRotatef(angle: ROTATE_SPEED_Y * m_startTime.msecsTo(QTime::currentTime()), x: 0.0f, y: 1.0f, z: 0.0f); |
346 | glRotatef(angle: ROTATE_SPEED_Z * m_startTime.msecsTo(QTime::currentTime()), x: 0.0f, y: 0.0f, z: 1.0f); |
347 | int dt = m_startTime.msecsTo(QTime::currentTime()); |
348 | if (dt < 500) |
349 | glScalef(x: dt / 500.0f, y: dt / 500.0f, z: dt / 500.0f); |
350 | |
351 | for (int dir = 0; dir < 3; ++dir) { |
352 | glColor4f(red: 1.0f, green: 1.0f, blue: 1.0f, alpha: 1.0); |
353 | |
354 | glBegin(GL_TRIANGLE_STRIP); |
355 | glNormal3fv(v: reinterpret_cast<float *>(&m_normals[2 * dir + 0])); |
356 | for (int i = 0; i < 2; ++i) { |
357 | for (int j = 0; j < 2; ++j) { |
358 | glTexCoord2fv(v: reinterpret_cast<float *>(&m_texCoords[(j << 1) | i])); |
359 | glVertex3fv(v: reinterpret_cast<float *>(&m_vertices[(i << ((dir + 2) % 3)) | (j << ((dir + 1) % 3))])); |
360 | } |
361 | } |
362 | glEnd(); |
363 | |
364 | glBegin(GL_TRIANGLE_STRIP); |
365 | glNormal3fv(v: reinterpret_cast<float *>(&m_normals[2 * dir + 1])); |
366 | for (int i = 0; i < 2; ++i) { |
367 | for (int j = 0; j < 2; ++j) { |
368 | glTexCoord2fv(v: reinterpret_cast<float *>(&m_texCoords[(j << 1) | i])); |
369 | glVertex3fv(v: reinterpret_cast<float *>(&m_vertices[(1 << dir) | (i << ((dir + 1) % 3)) | (j << ((dir + 2) % 3))])); |
370 | } |
371 | } |
372 | glEnd(); |
373 | } |
374 | m_texture->unbind(); |
375 | |
376 | //glDisable(GL_DEPTH_TEST); |
377 | glDisable(GL_CULL_FACE); |
378 | glDisable(GL_LIGHTING); |
379 | glDisable(GL_COLOR_MATERIAL); |
380 | glDisable(GL_TEXTURE_2D); |
381 | glDisable(GL_LIGHT0); |
382 | glDisable(GL_NORMALIZE); |
383 | |
384 | glPopMatrix(); |
385 | |
386 | glMatrixMode(GL_PROJECTION); |
387 | glPopMatrix(); |
388 | |
389 | painter->endNativePainting(); |
390 | |
391 | ItemBase::paint(painter, option, widget); |
392 | } |
393 | |
394 | //============================================================================// |
395 | // CircleItem // |
396 | //============================================================================// |
397 | |
398 | CircleItem::CircleItem(int size, int x, int y) : ItemBase(size, x, y) |
399 | , m_color(QColor::fromHsv(h: QRandomGenerator::global()->bounded(highest: 360), s: 255, v: 255)) |
400 | {} |
401 | |
402 | void CircleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
403 | { |
404 | int dt = m_startTime.msecsTo(QTime::currentTime()); |
405 | |
406 | qreal r0 = 0.5 * m_size * (1.0 - qExp(v: -0.001 * ((dt + 3800) % 4000))); |
407 | qreal r1 = 0.5 * m_size * (1.0 - qExp(v: -0.001 * ((dt + 0) % 4000))); |
408 | qreal r2 = 0.5 * m_size * (1.0 - qExp(v: -0.001 * ((dt + 1800) % 4000))); |
409 | qreal r3 = 0.5 * m_size * (1.0 - qExp(v: -0.001 * ((dt + 2000) % 4000))); |
410 | |
411 | if (r0 > r1) |
412 | r0 = 0.0; |
413 | if (r2 > r3) |
414 | r2 = 0.0; |
415 | |
416 | QPainterPath path; |
417 | path.moveTo(x: r1, y: 0.0); |
418 | path.arcTo(x: -r1, y: -r1, w: 2 * r1, h: 2 * r1, startAngle: 0.0, arcLength: 360.0); |
419 | path.lineTo(x: r0, y: 0.0); |
420 | path.arcTo(x: -r0, y: -r0, w: 2 * r0, h: 2 * r0, startAngle: 0.0, arcLength: -360.0); |
421 | path.closeSubpath(); |
422 | path.moveTo(x: r3, y: 0.0); |
423 | path.arcTo(x: -r3, y: -r3, w: 2 * r3, h: 2 * r3, startAngle: 0.0, arcLength: 360.0); |
424 | path.lineTo(x: r0, y: 0.0); |
425 | path.arcTo(x: -r2, y: -r2, w: 2 * r2, h: 2 * r2, startAngle: 0.0, arcLength: -360.0); |
426 | path.closeSubpath(); |
427 | painter->setRenderHint(hint: QPainter::Antialiasing, on: true); |
428 | painter->setBrush(QBrush(m_color)); |
429 | painter->setPen(Qt::NoPen); |
430 | painter->drawPath(path); |
431 | painter->setBrush(Qt::NoBrush); |
432 | painter->setPen(Qt::SolidLine); |
433 | painter->setRenderHint(hint: QPainter::Antialiasing, on: false); |
434 | |
435 | ItemBase::paint(painter, option, widget); |
436 | } |
437 | |
438 | ItemBase *CircleItem::createNew(int size, int x, int y) |
439 | { |
440 | return new CircleItem(size, x, y); |
441 | } |
442 | |
443 | //============================================================================// |
444 | // SquareItem // |
445 | //============================================================================// |
446 | |
447 | SquareItem::SquareItem(int size, int x, int y) : ItemBase(size, x, y) |
448 | , m_image(QPixmap(":/res/boxes/square.jpg" )) |
449 | {} |
450 | |
451 | void SquareItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) |
452 | { |
453 | int dt = m_startTime.msecsTo(QTime::currentTime()); |
454 | QTransform oldTransform = painter->worldTransform(); |
455 | int dtMod = dt % 2000; |
456 | qreal amp = 0.002 * (dtMod < 1000 ? dtMod : 2000 - dtMod) - 1.0; |
457 | |
458 | qreal scale = 0.6 + 0.2 * amp * amp; |
459 | painter->setWorldTransform(matrix: QTransform().rotate(a: 15.0 * amp).scale(sx: scale, sy: scale), combine: true); |
460 | |
461 | painter->drawPixmap(x: -m_size / 2, y: -m_size / 2, w: m_size, h: m_size, pm: m_image); |
462 | |
463 | painter->setWorldTransform(matrix: oldTransform, combine: false); |
464 | ItemBase::paint(painter, option, widget); |
465 | } |
466 | |
467 | ItemBase *SquareItem::createNew(int size, int x, int y) |
468 | { |
469 | return new SquareItem(size, x, y); |
470 | } |
471 | |