#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_OBJECTpublic: 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();}
void Widget::paintEvent(QPaintEvent *event){ QPainter painter(this); painter.fillRect(0, 0, width(), height(), Qt::darkGray); painter.setClipRect(0, 0, width(), height()); // <<<<<< added 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);}
// This limitations comes from qgrayraster.c. Any higher and// rasterization of shapes will produce incorrect results.const int QT_RASTER_COORD_LIMIT = 32767;