kamre
10th September 2009, 19:23
It is possible to drag with pressed right mouse button and to zoom with mouse wheel. And initially everything works rather smooth. But when I zoom to the smallest circle the performance of drawing is significantly degraded. Why? And how can I fix it?
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QList>
struct circle {
QPointF c;
qreal r;
circle(QPointF center, qreal radius): c(center), r(radius) {}
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
private:
QList<circle> circles;
QPointF shift;
qreal scale;
QPoint previous;
bool isDragging;
};
#endif // WIDGET_H
#include "widget.h"
#include <math.h>
#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>
circle tangent2(const circle &c1, const circle &c2, qreal r, const QPointF &p)
{
qreal r1 = c1.r + r;
qreal r2 = c2.r + r;
qreal d = c1.r + c2.r;
qreal dx = (d*d - r2*r2 + r1*r1)/(2*d);
qreal dy = sqrt(r1*r1 - dx*dx);
QPointF v = (c2.c - c1.c)/d;
QPointF n(-v.y(), v.x());
QPointF pv = p - c1.c;
if (pv.x()*n.x() + pv.y()*n.y() < 0)
n = -n;
QPointF c = c1.c + dx*v + dy*n;
return circle(c, r);
}
circle tangent3(const circle &c1, const circle &c2, const circle &c3)
{
qreal k1 = 1/c1.r;
qreal k2 = 1/c2.r;
qreal k3 = 1/c3.r;
qreal a = k1 + k2 + k3;
qreal b = 2*sqrt(k1*k2 + k2*k3 + k3*k1);
qreal k = qMax(qAbs(a - b), a + b);
return tangent2(c1, c2, 1/k, c3.c);
}
Widget::Widget(QWidget *parent)
: QWidget(parent), shift(300, 400), scale(30), isDragging(false)
{
setAutoFillBackground(false);
circle c1(QPointF(0, 0), 5);
circle c2(QPointF(8, 0), 3);
circle c3 = tangent2(c1, c2, 4, QPointF(0, -1));
circles << c1 << c2 << c3;
for (int i = 0; i < 6; ++i) {
circle c = tangent3(c1, c2, c3);
circles << c;
c1 = c2;
c2 = c3;
c3 = c;
}
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.fillRect(0, 0, width(), height(), Qt::darkGray);
painter.setBrush(Qt::NoBrush);
painter.setPen(QPen(QBrush(Qt::white), 1.5));
painter.setRenderHint(QPainter::Antialiasing, true);
foreach(const circle &c, circles)
painter.drawEllipse(c.c * scale + shift, c.r * scale, c.r * scale);
}
void Widget::mousePressEvent(QMouseEvent *event)
{
if (!isDragging && event->button() == Qt::RightButton) {
isDragging = true;
previous = event->pos();
}
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
if (isDragging && event->button() == Qt::RightButton) {
isDragging = false;
}
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if (isDragging) {
shift += event->pos() - previous;
previous = event->pos();
repaint();
}
}
void Widget::wheelEvent(QWheelEvent *event)
{
int numDegrees = event->delta() / 8;
int numTicks = numDegrees / 15;
qreal factor = pow(1.2, -numTicks);
shift += (1 - factor) * (event->pos() - shift);
scale *= factor;
repaint();
}
#include <QtGui/QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.resize(800, 600);
w.show();
return a.exec();
}
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QList>
struct circle {
QPointF c;
qreal r;
circle(QPointF center, qreal radius): c(center), r(radius) {}
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
private:
QList<circle> circles;
QPointF shift;
qreal scale;
QPoint previous;
bool isDragging;
};
#endif // WIDGET_H
#include "widget.h"
#include <math.h>
#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>
circle tangent2(const circle &c1, const circle &c2, qreal r, const QPointF &p)
{
qreal r1 = c1.r + r;
qreal r2 = c2.r + r;
qreal d = c1.r + c2.r;
qreal dx = (d*d - r2*r2 + r1*r1)/(2*d);
qreal dy = sqrt(r1*r1 - dx*dx);
QPointF v = (c2.c - c1.c)/d;
QPointF n(-v.y(), v.x());
QPointF pv = p - c1.c;
if (pv.x()*n.x() + pv.y()*n.y() < 0)
n = -n;
QPointF c = c1.c + dx*v + dy*n;
return circle(c, r);
}
circle tangent3(const circle &c1, const circle &c2, const circle &c3)
{
qreal k1 = 1/c1.r;
qreal k2 = 1/c2.r;
qreal k3 = 1/c3.r;
qreal a = k1 + k2 + k3;
qreal b = 2*sqrt(k1*k2 + k2*k3 + k3*k1);
qreal k = qMax(qAbs(a - b), a + b);
return tangent2(c1, c2, 1/k, c3.c);
}
Widget::Widget(QWidget *parent)
: QWidget(parent), shift(300, 400), scale(30), isDragging(false)
{
setAutoFillBackground(false);
circle c1(QPointF(0, 0), 5);
circle c2(QPointF(8, 0), 3);
circle c3 = tangent2(c1, c2, 4, QPointF(0, -1));
circles << c1 << c2 << c3;
for (int i = 0; i < 6; ++i) {
circle c = tangent3(c1, c2, c3);
circles << c;
c1 = c2;
c2 = c3;
c3 = c;
}
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.fillRect(0, 0, width(), height(), Qt::darkGray);
painter.setBrush(Qt::NoBrush);
painter.setPen(QPen(QBrush(Qt::white), 1.5));
painter.setRenderHint(QPainter::Antialiasing, true);
foreach(const circle &c, circles)
painter.drawEllipse(c.c * scale + shift, c.r * scale, c.r * scale);
}
void Widget::mousePressEvent(QMouseEvent *event)
{
if (!isDragging && event->button() == Qt::RightButton) {
isDragging = true;
previous = event->pos();
}
}
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
if (isDragging && event->button() == Qt::RightButton) {
isDragging = false;
}
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
if (isDragging) {
shift += event->pos() - previous;
previous = event->pos();
repaint();
}
}
void Widget::wheelEvent(QWheelEvent *event)
{
int numDegrees = event->delta() / 8;
int numTicks = numDegrees / 15;
qreal factor = pow(1.2, -numTicks);
shift += (1 - factor) * (event->pos() - shift);
scale *= factor;
repaint();
}
#include <QtGui/QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.resize(800, 600);
w.show();
return a.exec();
}