00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "pathdeform.h"
00025
00026 #include <QApplication>
00027 #include <QtDebug>
00028 #include <QMouseEvent>
00029 #include <QTimerEvent>
00030 #include <QLayout>
00031 #include <QLineEdit>
00032 #include <QPainter>
00033 #include <QSlider>
00034
00035 #include <math.h>
00036
00037
00038 PathDeformWidget::PathDeformWidget(QWidget *parent)
00039 : QWidget(parent)
00040 {
00041 setWindowTitle("Vector Deformation");
00042
00043 m_renderer = new PathDeformRenderer(this);
00044 m_renderer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
00045
00046 QGroupBox *mainGroup = new QGroupBox(this);
00047 mainGroup->setTitle("Vector Deformation");
00048
00049 QGroupBox *radiusGroup = new QGroupBox(mainGroup);
00050 radiusGroup->setAttribute(Qt::WA_ContentsPropagated);
00051 radiusGroup->setTitle("Lens Radius");
00052 QSlider *radiusSlider = new QSlider(Qt::Horizontal, radiusGroup);
00053 radiusSlider->setRange(50, 150);
00054 radiusSlider->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
00055
00056 QGroupBox *deformGroup = new QGroupBox(mainGroup);
00057 deformGroup->setAttribute(Qt::WA_ContentsPropagated);
00058 deformGroup->setTitle("Deformation");
00059 QSlider *deformSlider = new QSlider(Qt::Horizontal, deformGroup);
00060 deformSlider->setRange(-100, 100);
00061 deformSlider->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
00062
00063 QGroupBox *fontSizeGroup = new QGroupBox(mainGroup);
00064 fontSizeGroup->setAttribute(Qt::WA_ContentsPropagated);
00065 fontSizeGroup->setTitle("Font Size");
00066 QSlider *fontSizeSlider = new QSlider(Qt::Horizontal, fontSizeGroup);
00067 fontSizeSlider->setRange(16, 200);
00068 fontSizeSlider->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
00069
00070 QGroupBox *textGroup = new QGroupBox(mainGroup);
00071 textGroup->setAttribute(Qt::WA_ContentsPropagated);
00072 textGroup->setTitle("Text");
00073 QLineEdit *textInput = new QLineEdit(textGroup);
00074
00075 QPushButton *animateButton = new QPushButton(mainGroup);
00076 animateButton->setText("Animated");
00077 animateButton->setCheckable(true);
00078
00079 QPushButton *showSourceButton = new QPushButton(mainGroup);
00080 showSourceButton->setText("Show Source");
00081
00082 #ifdef QT_OPENGL_SUPPORT
00083 QPushButton *enableOpenGLButton = new QPushButton(mainGroup);
00084 enableOpenGLButton->setText("Use OpenGL");
00085 enableOpenGLButton->setCheckable(true);
00086 enableOpenGLButton->setChecked(m_renderer->usesOpenGL());
00087 if (!QGLFormat::hasOpenGL())
00088 enableOpenGLButton->hide();
00089 #endif
00090 QPushButton *whatsThisButton = new QPushButton(mainGroup);
00091 whatsThisButton->setText("What's This?");
00092 whatsThisButton->setCheckable(true);
00093
00094
00095 QHBoxLayout *mainLayout = new QHBoxLayout(this);
00096 mainLayout->addWidget(m_renderer);
00097 mainLayout->addWidget(mainGroup);
00098 mainGroup->setFixedWidth(180);
00099
00100 QVBoxLayout *mainGroupLayout = new QVBoxLayout(mainGroup);
00101 mainGroupLayout->addWidget(radiusGroup);
00102 mainGroupLayout->addWidget(deformGroup);
00103 mainGroupLayout->addWidget(fontSizeGroup);
00104 mainGroupLayout->addWidget(textGroup);
00105 mainGroupLayout->addWidget(animateButton);
00106 mainGroupLayout->addStretch(1);
00107 mainGroupLayout->addWidget(showSourceButton);
00108 #ifdef QT_OPENGL_SUPPORT
00109 mainGroupLayout->addWidget(enableOpenGLButton);
00110 #endif
00111 mainGroupLayout->addWidget(whatsThisButton);
00112
00113 QVBoxLayout *radiusGroupLayout = new QVBoxLayout(radiusGroup);
00114 radiusGroupLayout->addWidget(radiusSlider);
00115
00116 QVBoxLayout *deformGroupLayout = new QVBoxLayout(deformGroup);
00117 deformGroupLayout->addWidget(deformSlider);
00118
00119 QVBoxLayout *fontSizeGroupLayout = new QVBoxLayout(fontSizeGroup);
00120 fontSizeGroupLayout->addWidget(fontSizeSlider);
00121
00122 QVBoxLayout *textGroupLayout = new QVBoxLayout(textGroup);
00123 textGroupLayout->addWidget(textInput);
00124
00125 connect(textInput, SIGNAL(textChanged(QString)), m_renderer, SLOT(setText(QString)));
00126 connect(radiusSlider, SIGNAL(valueChanged(int)), m_renderer, SLOT(setRadius(int)));
00127 connect(deformSlider, SIGNAL(valueChanged(int)), m_renderer, SLOT(setIntensity(int)));
00128 connect(fontSizeSlider, SIGNAL(valueChanged(int)), m_renderer, SLOT(setFontSize(int)));
00129 connect(animateButton, SIGNAL(clicked(bool)), m_renderer, SLOT(setAnimated(bool)));
00130 connect(whatsThisButton, SIGNAL(clicked(bool)), m_renderer, SLOT(setDescriptionEnabled(bool)));
00131 connect(showSourceButton, SIGNAL(clicked()), m_renderer, SLOT(showSource()));
00132 #ifdef QT_OPENGL_SUPPORT
00133 connect(enableOpenGLButton, SIGNAL(clicked(bool)), m_renderer, SLOT(enableOpenGL(bool)));
00134 #endif
00135 connect(m_renderer, SIGNAL(descriptionEnabledChanged(bool)),
00136 whatsThisButton, SLOT(setChecked(bool)));
00137
00138 animateButton->animateClick();
00139 deformSlider->setValue(80);
00140 radiusSlider->setValue(100);
00141 fontSizeSlider->setValue(120);
00142 textInput->setText("Qt");
00143
00144 m_renderer->loadSourceFile(":res/pathdeform.cpp");
00145 m_renderer->loadDescription(":res/pathdeform.html");
00146 m_renderer->setDescriptionEnabled(false);
00147 }
00148
00149
00150 static inline QRect circle_bounds(const QPointF ¢er, double radius, double compensation)
00151 {
00152 return QRect(qRound(center.x() - radius - compensation),
00153 qRound(center.y() - radius - compensation),
00154 qRound((radius + compensation) * 2),
00155 qRound((radius + compensation) * 2));
00156
00157 }
00158
00159 const int LENS_EXTENT = 10;
00160
00161 PathDeformRenderer::PathDeformRenderer(QWidget *widget)
00162 : ArthurFrame(widget)
00163 {
00164 m_radius = 100;
00165 m_pos = QPointF(m_radius, m_radius);
00166 m_direction = QPointF(1, 1);
00167 m_fontSize = 24;
00168 m_animated = true;
00169 m_repaintTimer.start(25, this);
00170 m_repaintTracker.start();
00171 m_intensity = 100;
00172
00173
00174
00175
00176 generateLensPixmap();
00177 }
00178
00179 void PathDeformRenderer::setText(const QString &text)
00180 {
00181 m_text = text;
00182
00183 QFont f("times new roman,utopia");
00184 f.setStyleStrategy(QFont::ForceOutline);
00185 f.setPointSize(m_fontSize);
00186 f.setStyleHint(QFont::Times);
00187
00188 QFontMetrics fm(f);
00189
00190 m_paths.clear();
00191 m_pathBounds = QRect();
00192
00193 QPointF advance(0, 0);
00194
00195 bool do_quick = true;
00196 for (int i=0; i<text.size(); ++i) {
00197 if (text.at(i).unicode() >= 0x4ff && text.at(i).unicode() <= 0x1e00) {
00198 do_quick = false;
00199 break;
00200 }
00201 }
00202
00203 if (do_quick) {
00204 for (int i=0; i<text.size(); ++i) {
00205 QPainterPath path;
00206 path.addText(advance, f, text.mid(i, 1));
00207 m_pathBounds |= path.boundingRect();
00208 m_paths << path;
00209 advance += QPointF(fm.width(text.mid(i, 1)), 0);
00210 }
00211 } else {
00212 QPainterPath path;
00213 path.addText(advance, f, text);
00214 m_pathBounds |= path.boundingRect();
00215 m_paths << path;
00216 }
00217
00218 for (int i=0; i<m_paths.size(); ++i)
00219 m_paths[i] = m_paths[i] * QMatrix(1, 0, 0, 1, -m_pathBounds.x(), -m_pathBounds.y());
00220
00221 update();
00222 }
00223
00224
00225 void PathDeformRenderer::generateLensPixmap()
00226 {
00227 double rad = m_radius + LENS_EXTENT;
00228
00229 QRect bounds = circle_bounds(QPointF(), rad, 0);
00230
00231 QPainter painter;
00232
00233 if (preferImage()) {
00234 m_lens_image = QImage(bounds.size(), QImage::Format_ARGB32_Premultiplied);
00235 m_lens_image.fill(0);
00236 painter.begin(&m_lens_image);
00237 } else {
00238 m_lens_pixmap = QPixmap(bounds.size());
00239 m_lens_pixmap.fill(QColor(0, 0, 0, 0));
00240 painter.begin(&m_lens_pixmap);
00241 }
00242
00243 QRadialGradient gr(rad, rad, rad, 3 * rad / 5, 3 * rad / 5);
00244 gr.setColorAt(0.0, QColor(255, 255, 255, 191));
00245 gr.setColorAt(0.2, QColor(255, 255, 127, 191));
00246 gr.setColorAt(0.9, QColor(150, 150, 200, 63));
00247 gr.setColorAt(0.95, QColor(0, 0, 0, 127));
00248 gr.setColorAt(1, QColor(0, 0, 0, 0));
00249 painter.setRenderHint(QPainter::Antialiasing);
00250 painter.setBrush(gr);
00251 painter.setPen(Qt::NoPen);
00252 painter.drawEllipse(0, 0, bounds.width(), bounds.height());
00253 }
00254
00255
00256 void PathDeformRenderer::setAnimated(bool animated)
00257 {
00258 m_animated = animated;
00259
00260 if (m_animated) {
00261
00262
00263 m_repaintTimer.start(25, this);
00264 m_repaintTracker.start();
00265 } else {
00266
00267 m_repaintTimer.stop();
00268 }
00269 }
00270
00271 void PathDeformRenderer::timerEvent(QTimerEvent *e)
00272 {
00273
00274 if (e->timerId() == m_repaintTimer.timerId()) {
00275
00276 if (QLineF(QPointF(0,0), m_direction).length() > 1)
00277 m_direction *= 0.995;
00278 double time = m_repaintTracker.restart();
00279
00280 QRect rectBefore = circle_bounds(m_pos, m_radius, m_fontSize);
00281
00282 double dx = m_direction.x();
00283 double dy = m_direction.y();
00284 if (time > 0) {
00285 dx = dx * time * .1;
00286 dy = dy * time * .1;
00287 }
00288
00289 m_pos += QPointF(dx, dy);
00290
00291
00292
00293 if (m_pos.x() - m_radius < 0) {
00294 m_direction.setX(-m_direction.x());
00295 m_pos.setX(m_radius);
00296 } else if (m_pos.x() + m_radius > width()) {
00297 m_direction.setX(-m_direction.x());
00298 m_pos.setX(width() - m_radius);
00299 }
00300
00301 if (m_pos.y() - m_radius < 0) {
00302 m_direction.setY(-m_direction.y());
00303 m_pos.setY(m_radius);
00304 } else if (m_pos.y() + m_radius > height()) {
00305 m_direction.setY(-m_direction.y());
00306 m_pos.setY(height() - m_radius);
00307 }
00308
00309 #ifdef QT_OPENGL_SUPPORT
00310 if (usesOpenGL()) {
00311 update();
00312 } else
00313 #endif
00314 {
00315 QRect rectAfter = circle_bounds(m_pos, m_radius, m_fontSize);
00316 update(rectAfter | rectBefore);
00317 QApplication::syncX();
00318 }
00319 }
00320
00321
00322
00323
00324
00325
00326 }
00327
00328 void PathDeformRenderer::mousePressEvent(QMouseEvent *e)
00329 {
00330 setDescriptionEnabled(false);
00331
00332 m_repaintTimer.stop();
00333 m_offset = QPointF();
00334 if (QLineF(m_pos, e->pos()).length() <= m_radius)
00335 m_offset = m_pos - e->pos();
00336
00337 mouseMoveEvent(e);
00338 }
00339
00340 void PathDeformRenderer::mouseReleaseEvent(QMouseEvent *e)
00341 {
00342 if (e->buttons() == Qt::NoButton && m_animated) {
00343 m_repaintTimer.start(10, this);
00344 m_repaintTracker.start();
00345 }
00346 }
00347
00348 void PathDeformRenderer::mouseMoveEvent(QMouseEvent *e)
00349 {
00350 QRect rectBefore = circle_bounds(m_pos, m_radius, m_fontSize);
00351 if (e->type() == QEvent::MouseMove) {
00352 QLineF line(m_pos, e->pos() + m_offset);
00353 line.setLength(line.length() * .1);
00354 QPointF dir(line.dx(), line.dy());
00355 m_direction = (m_direction + dir) / 2;
00356 }
00357 m_pos = e->pos() + m_offset;
00358 #ifdef QT_OPENGL_SUPPORT
00359 if (usesOpenGL()) {
00360 update();
00361 } else
00362 #endif
00363 {
00364 QRect rectAfter = circle_bounds(m_pos, m_radius, m_fontSize);
00365 update(rectBefore | rectAfter);
00366 }
00367 }
00368
00369 QPainterPath PathDeformRenderer::lensDeform(const QPainterPath &source, const QPointF &offset)
00370 {
00371 QPainterPath path;
00372 path.addPath(source);
00373
00374 double flip = m_intensity / 100.0;
00375
00376 for (int i=0; i<path.elementCount(); ++i) {
00377 const QPainterPath::Element &e = path.elementAt(i);
00378
00379 double x = e.x + offset.x();
00380 double y = e.y + offset.y();
00381
00382 double dx = x - m_pos.x();
00383 double dy = y - m_pos.y();
00384 double len = m_radius - sqrt(dx * dx + dy * dy);
00385
00386 if (len > 0) {
00387 path.setElementPositionAt(i,
00388 x + flip * dx * len / m_radius,
00389 y + flip * dy * len / m_radius);
00390 } else {
00391 path.setElementPositionAt(i, x, y);
00392 }
00393
00394 }
00395
00396 return path;
00397 }
00398
00399
00400 void PathDeformRenderer::paint(QPainter *painter)
00401 {
00402 int pad_x = 5;
00403 int pad_y = 5;
00404
00405 int skip_x = qRound(m_pathBounds.width() + pad_x + m_fontSize/2);
00406 int skip_y = qRound(m_pathBounds.height() + pad_y);
00407
00408 painter->setPen(Qt::NoPen);
00409 painter->setBrush(Qt::black);
00410
00411 QRectF clip(painter->clipPath().boundingRect());
00412
00413 int overlap = pad_x / 2;
00414
00415 for (int start_y=0; start_y < height(); start_y += skip_y) {
00416
00417 if (start_y > clip.bottom())
00418 break;
00419
00420 int start_x = -overlap;
00421 for (; start_x < width(); start_x += skip_x) {
00422
00423 if (start_y + skip_y >= clip.top() &&
00424 start_x + skip_x >= clip.left() &&
00425 start_x <= clip.right()) {
00426 for (int i=0; i<m_paths.size(); ++i) {
00427 QPainterPath path = lensDeform(m_paths[i], QPointF(start_x, start_y));
00428 painter->drawPath(path);
00429 }
00430 }
00431 }
00432 overlap = skip_x - (start_x - width());
00433
00434 }
00435
00436 if (preferImage()) {
00437 painter->drawImage(m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT),
00438 m_lens_image);
00439 } else {
00440 painter->drawPixmap(m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT),
00441 m_lens_pixmap);
00442 }
00443 }
00444
00445
00446
00447 void PathDeformRenderer::setRadius(int radius)
00448 {
00449 double max = qMax(m_radius, (double)radius);
00450 m_radius = radius;
00451 generateLensPixmap();
00452 if (!m_animated || m_radius < max) {
00453 #ifdef QT_OPENGL_SUPPORT
00454 if (usesOpenGL()) {
00455 update();
00456 } else
00457 #endif
00458 {
00459 update(circle_bounds(m_pos, max, m_fontSize));
00460 }
00461 }
00462 }
00463
00464 void PathDeformRenderer::setIntensity(int intensity)
00465 {
00466 m_intensity = intensity;
00467 if (!m_animated) {
00468 #ifdef QT_OPENGL_SUPPORT
00469 if (usesOpenGL()) {
00470 update();
00471 } else
00472 #endif
00473 {
00474 update(circle_bounds(m_pos, m_radius, m_fontSize));
00475 }
00476 }
00477 }