1 /****************************************************************************
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
6 ** This file is part of the examples of the Qt Toolkit.
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.
18 ** Alternatively, you may use this file under the terms of the BSD license
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
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
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.
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."
49 ****************************************************************************/
51 #include "mandelbrotwidget.h"
52 #include "mandelbrotwidget.moc"
54 #include <QtGui/QPainter>
55 #include <QtGui/QKeyEvent>
59 const double DefaultCenterX
= -0.637011;
60 const double DefaultCenterY
= -0.0395159;
61 const double DefaultScale
= 0.00403897;
63 const double ZoomInFactor
= 0.8;
64 const double ZoomOutFactor
= 1 / ZoomInFactor
;
65 const int ScrollStep
= 20;
67 MandelbrotWidget::MandelbrotWidget(QWidget
* parent
)
69 , centerX(DefaultCenterX
)
70 , centerY(DefaultCenterY
)
71 , pixmapScale(DefaultScale
)
72 , curScale(DefaultScale
)
74 connect(&thread
, &RenderThread::renderedImage
, this, &MandelbrotWidget::updatePixmap
);
76 setWindowTitle(tr("Mandelbrot"));
78 setCursor(Qt::CrossCursor
);
83 void MandelbrotWidget::paintEvent(QPaintEvent
* /* event */)
85 QPainter
painter(this);
86 painter
.fillRect(rect(), Qt::black
);
90 painter
.setPen(Qt::white
);
91 painter
.drawText(rect(), Qt::AlignCenter
, tr("Rendering initial image, please wait..."));
95 if (qFuzzyCompare(curScale
, pixmapScale
))
97 painter
.drawPixmap(pixmapOffset
, pixmap
);
101 auto previewPixmap
= qFuzzyCompare(pixmap
.devicePixelRatioF(), qreal(1))
103 : pixmap
.scaled(pixmap
.size() / pixmap
.devicePixelRatioF(),
104 Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
105 double scaleFactor
= pixmapScale
/ curScale
;
106 int newWidth
= int(previewPixmap
.width() * scaleFactor
);
107 int newHeight
= int(previewPixmap
.height() * scaleFactor
);
108 int newX
= pixmapOffset
.x() + (previewPixmap
.width() - newWidth
) / 2;
109 int newY
= pixmapOffset
.y() + (previewPixmap
.height() - newHeight
) / 2;
112 painter
.translate(newX
, newY
);
113 painter
.scale(scaleFactor
, scaleFactor
);
115 QRectF exposed
= painter
.transform().inverted().mapRect(rect()).adjusted(-1, -1, 1, 1);
116 painter
.drawPixmap(exposed
, previewPixmap
, exposed
);
120 QString text
= tr("Use mouse wheel or the '+' and '-' keys to zoom. "
121 "Press and hold left mouse button to scroll.");
122 QFontMetrics metrics
= painter
.fontMetrics();
123 int textWidth
= metrics
.horizontalAdvance(text
);
125 painter
.setPen(Qt::NoPen
);
126 painter
.setBrush(QColor(0, 0, 0, 127));
127 painter
.drawRect((width() - textWidth
) / 2 - 5, 0, textWidth
+ 10, metrics
.lineSpacing() + 5);
128 painter
.setPen(Qt::white
);
129 painter
.drawText((width() - textWidth
) / 2, metrics
.leading() + metrics
.ascent(), text
);
132 void MandelbrotWidget::resizeEvent(QResizeEvent
* /* event */)
134 thread
.render(centerX
, centerY
, curScale
, size(), devicePixelRatioF());
137 void MandelbrotWidget::keyPressEvent(QKeyEvent
* event
)
139 switch (event
->key())
148 scroll(-ScrollStep
, 0);
151 scroll(+ScrollStep
, 0);
154 scroll(0, -ScrollStep
);
157 scroll(0, +ScrollStep
);
160 QWidget::keyPressEvent(event
);
164 #if QT_CONFIG(wheelevent)
165 void MandelbrotWidget::wheelEvent(QWheelEvent
* event
)
167 const int numDegrees
= event
->angleDelta().y() / 8;
168 const double numSteps
= numDegrees
/ double(15);
169 zoom(pow(ZoomInFactor
, numSteps
));
173 void MandelbrotWidget::mousePressEvent(QMouseEvent
* event
)
175 if (event
->button() == Qt::LeftButton
)
176 lastDragPos
= event
->pos();
179 void MandelbrotWidget::mouseMoveEvent(QMouseEvent
* event
)
181 if (event
->buttons() & Qt::LeftButton
)
183 pixmapOffset
+= event
->pos() - lastDragPos
;
184 lastDragPos
= event
->pos();
189 void MandelbrotWidget::mouseReleaseEvent(QMouseEvent
* event
)
191 if (event
->button() == Qt::LeftButton
)
193 pixmapOffset
+= event
->pos() - lastDragPos
;
194 lastDragPos
= QPoint();
196 const auto pixmapSize
= pixmap
.size() / pixmap
.devicePixelRatioF();
197 int deltaX
= (width() - pixmapSize
.width()) / 2 - pixmapOffset
.x();
198 int deltaY
= (height() - pixmapSize
.height()) / 2 - pixmapOffset
.y();
199 scroll(deltaX
, deltaY
);
203 void MandelbrotWidget::updatePixmap(const QImage
& image
, double scaleFactor
)
205 if (!lastDragPos
.isNull())
208 pixmap
= QPixmap::fromImage(image
);
209 pixmapOffset
= QPoint();
210 lastDragPos
= QPoint();
211 pixmapScale
= scaleFactor
;
215 void MandelbrotWidget::zoom(double zoomFactor
)
217 curScale
*= zoomFactor
;
219 thread
.render(centerX
, centerY
, curScale
, size(), devicePixelRatioF());
222 void MandelbrotWidget::scroll(int deltaX
, int deltaY
)
224 centerX
+= deltaX
* curScale
;
225 centerY
+= deltaY
* curScale
;
227 thread
.render(centerX
, centerY
, curScale
, size(), devicePixelRatioF());