1
#include "pathdeform.h"
3
#include <qapplication.h>
12
PathDeformWidget::PathDeformWidget(QWidget *parent)
15
setWindowTitle("Vector Deformation");
17
m_renderer = new PathDeformRenderer(this);
18
m_renderer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
20
ArthurGroupBox *mainGroup = new ArthurGroupBox(this);
21
mainGroup->setTitle("Vector Deformation");
23
ArthurGroupBox *radiusGroup = new ArthurGroupBox(mainGroup);
24
radiusGroup->setAttribute(Qt::WA_ContentsPropagated);
25
radiusGroup->setTitle("Lens radius");
26
QSlider *radiusSlider = new QSlider(Qt::Horizontal, radiusGroup);
27
radiusSlider->setRange(50, 150);
28
radiusSlider->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
30
ArthurGroupBox *deformGroup = new ArthurGroupBox(mainGroup);
31
deformGroup->setAttribute(Qt::WA_ContentsPropagated);
32
deformGroup->setTitle("Deformation");
33
QSlider *deformSlider = new QSlider(Qt::Horizontal, deformGroup);
34
deformSlider->setRange(-100, 100);
35
deformSlider->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
37
ArthurGroupBox *fontSizeGroup = new ArthurGroupBox(mainGroup);
38
fontSizeGroup->setAttribute(Qt::WA_ContentsPropagated);
39
fontSizeGroup->setTitle("Font Size");
40
QSlider *fontSizeSlider = new QSlider(Qt::Horizontal, fontSizeGroup);
41
fontSizeSlider->setRange(16, 200);
42
fontSizeSlider->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
44
ArthurGroupBox *textGroup = new ArthurGroupBox(mainGroup);
45
textGroup->setAttribute(Qt::WA_ContentsPropagated);
46
textGroup->setTitle("Text");
47
QLineEdit *textInput = new QLineEdit(textGroup);
49
QPushButton *animateButton = new QPushButton(mainGroup);
50
animateButton->setText("Animated");
51
animateButton->setCheckable(true);
53
QPushButton *showSourceButton = new QPushButton(mainGroup);
54
showSourceButton->setText("Show Source");
55
// showSourceButton->setCheckable(true);
57
QPushButton *whatsThisButton = new QPushButton(mainGroup);
58
whatsThisButton->setText("What's This?");
59
whatsThisButton->setCheckable(true);
62
QHBoxLayout *mainLayout = new QHBoxLayout(this);
63
mainLayout->addWidget(m_renderer);
64
mainLayout->addWidget(mainGroup);
65
mainGroup->setFixedWidth(180);
67
QVBoxLayout *mainGroupLayout = new QVBoxLayout(mainGroup);
68
mainGroupLayout->addWidget(radiusGroup);
69
mainGroupLayout->addWidget(deformGroup);
70
mainGroupLayout->addWidget(fontSizeGroup);
71
mainGroupLayout->addWidget(textGroup);
72
mainGroupLayout->addWidget(animateButton);
73
mainGroupLayout->addStretch(1);
74
mainGroupLayout->addWidget(showSourceButton);
75
mainGroupLayout->addWidget(whatsThisButton);
77
QVBoxLayout *radiusGroupLayout = new QVBoxLayout(radiusGroup);
78
radiusGroupLayout->addWidget(radiusSlider);
80
QVBoxLayout *deformGroupLayout = new QVBoxLayout(deformGroup);
81
deformGroupLayout->addWidget(deformSlider);
83
QVBoxLayout *fontSizeGroupLayout = new QVBoxLayout(fontSizeGroup);
84
fontSizeGroupLayout->addWidget(fontSizeSlider);
86
QVBoxLayout *textGroupLayout = new QVBoxLayout(textGroup);
87
textGroupLayout->addWidget(textInput);
89
connect(textInput, SIGNAL(textChanged(QString)), m_renderer, SLOT(setText(QString)));
90
connect(radiusSlider, SIGNAL(valueChanged(int)), m_renderer, SLOT(setRadius(int)));
91
connect(deformSlider, SIGNAL(valueChanged(int)), m_renderer, SLOT(setIntensity(int)));
92
connect(fontSizeSlider, SIGNAL(valueChanged(int)), m_renderer, SLOT(setFontSize(int)));
93
connect(animateButton, SIGNAL(clicked(bool)), m_renderer, SLOT(setAnimated(bool)));
94
connect(whatsThisButton, SIGNAL(clicked(bool)), m_renderer, SLOT(setDescriptionEnabled(bool)));
95
connect(showSourceButton, SIGNAL(clicked()), m_renderer, SLOT(showSource()));
96
connect(m_renderer, SIGNAL(descriptionEnabledChanged(bool)),
97
whatsThisButton, SLOT(setChecked(bool)));
99
animateButton->animateClick();
100
deformSlider->setValue(80);
101
radiusSlider->setValue(100);
102
fontSizeSlider->setValue(120);
103
textInput->setText("Qt");
105
m_renderer->loadSourceFile(":res/pathdeform.cpp");
106
m_renderer->loadDescription(":res/pathdeform.html");
107
m_renderer->setDescriptionEnabled(false);
111
static inline QRect circle_bounds(const QPointF ¢er, double radius, double compensation)
113
return QRect(qRound(center.x() - radius - compensation),
114
qRound(center.y() - radius - compensation),
115
qRound((radius + compensation) * 2),
116
qRound((radius + compensation) * 2));
120
const int LENS_EXTENT = 10;
122
PathDeformRenderer::PathDeformRenderer(QWidget *widget)
123
: ArthurFrame(widget)
126
m_pos = QPointF(m_radius, m_radius);
127
m_direction = QPointF(1, 1);
130
m_repaintTimer.start(25, this);
131
m_repaintTracker.start();
134
// m_fpsTimer.start(1000, this);
137
generateLensPixmap();
140
void PathDeformRenderer::setText(const QString &text)
144
QFont f("times new roman,utopia");
145
f.setStyleStrategy(QFont::ForceOutline);
146
f.setPointSize(m_fontSize);
147
f.setStyleHint(QFont::Times);
152
m_pathBounds = QRect();
154
QPointF advance(0, 0);
156
bool do_quick = true;
157
for (int i=0; i<text.size(); ++i) {
158
if (text.at(i).unicode() >= 0x4ff && text.at(i).unicode() <= 0x1e00) {
165
for (int i=0; i<text.size(); ++i) {
167
path.addText(advance, f, text.mid(i, 1));
168
m_pathBounds |= path.boundingRect();
170
advance += QPointF(fm.width(text.mid(i, 1)), 0);
174
path.addText(advance, f, text);
175
m_pathBounds |= path.boundingRect();
179
for (int i=0; i<m_paths.size(); ++i)
180
m_paths[i] = m_paths[i] * QMatrix(1, 0, 0, 1, -m_pathBounds.x(), -m_pathBounds.y());
186
void PathDeformRenderer::generateLensPixmap()
188
double rad = m_radius + LENS_EXTENT;
190
QRect bounds = circle_bounds(QPointF(), rad, 0);
195
m_lens_image = QImage(bounds.size(), QImage::Format_ARGB32_Premultiplied);
196
m_lens_image.fill(0);
197
painter.begin(&m_lens_image);
199
m_lens_pixmap = QPixmap(bounds.size());
200
m_lens_pixmap.fill(QColor(0, 0, 0, 0));
201
painter.begin(&m_lens_pixmap);
204
QRadialGradient gr(rad, rad, rad, 3 * rad / 5, 3 * rad / 5);
205
gr.setColorAt(0.0, QColor(255, 255, 255, 191));
206
gr.setColorAt(0.2, QColor(255, 255, 127, 191));
207
gr.setColorAt(0.9, QColor(150, 150, 200, 63));
208
gr.setColorAt(0.95, QColor(0, 0, 0, 127));
209
gr.setColorAt(1, QColor(0, 0, 0, 0));
210
painter.setRenderHint(QPainter::Antialiasing);
211
painter.setBrush(gr);
212
painter.setPen(Qt::NoPen);
213
painter.drawEllipse(0, 0, bounds.width(), bounds.height());
217
void PathDeformRenderer::setAnimated(bool animated)
219
m_animated = animated;
222
// m_fpsTimer.start(1000, this);
224
m_repaintTimer.start(25, this);
225
m_repaintTracker.start();
227
// m_fpsTimer.stop();
228
m_repaintTimer.stop();
232
void PathDeformRenderer::timerEvent(QTimerEvent *e)
235
if (e->timerId() == m_repaintTimer.timerId()) {
237
if (QLineF(QPointF(0,0), m_direction).length() > 1)
238
m_direction *= 0.995;
239
double time = m_repaintTracker.restart();
241
QRect rectBefore = circle_bounds(m_pos, m_radius, m_fontSize);
243
double dx = m_direction.x();
244
double dy = m_direction.y();
250
m_pos += QPointF(dx, dy);
254
if (m_pos.x() - m_radius < 0) {
255
m_direction.setX(-m_direction.x());
256
m_pos.setX(m_radius);
257
} else if (m_pos.x() + m_radius > width()) {
258
m_direction.setX(-m_direction.x());
259
m_pos.setX(width() - m_radius);
262
if (m_pos.y() - m_radius < 0) {
263
m_direction.setY(-m_direction.y());
264
m_pos.setY(m_radius);
265
} else if (m_pos.y() + m_radius > height()) {
266
m_direction.setY(-m_direction.y());
267
m_pos.setY(height() - m_radius);
270
QRect rectAfter = circle_bounds(m_pos, m_radius, m_fontSize);
271
update(QRect(rectBefore | rectAfter));
272
QApplication::syncX();
274
// else if (e->timerId() == m_fpsTimer.timerId()) {
275
// printf("fps: %d\n", m_fpsCounter);
276
// emit frameRate(m_fpsCounter);
282
void PathDeformRenderer::mousePressEvent(QMouseEvent *e)
284
setDescriptionEnabled(false);
286
m_repaintTimer.stop();
287
m_offset = QPointF();
288
if (QLineF(m_pos, e->pos()).length() <= m_radius)
289
m_offset = m_pos - e->pos();
294
void PathDeformRenderer::mouseReleaseEvent(QMouseEvent *e)
296
if (e->buttons() == Qt::NoButton && m_animated) {
297
m_repaintTimer.start(10, this);
298
m_repaintTracker.start();
302
void PathDeformRenderer::mouseMoveEvent(QMouseEvent *e)
304
QRect rectBefore = circle_bounds(m_pos, m_radius, m_fontSize);
305
if (e->type() == QEvent::MouseMove) {
306
QLineF line(m_pos, e->pos() + m_offset);
307
line.setLength(line.length() * .1);
308
QPointF dir(line.dx(), line.dy());
309
m_direction = (m_direction + dir) / 2;
311
m_pos = e->pos() + m_offset;
312
QRect rectAfter = circle_bounds(m_pos, m_radius, m_fontSize);
313
update(rectBefore | rectAfter);
316
QPainterPath PathDeformRenderer::lensDeform(const QPainterPath &source, const QPointF &offset)
319
path.addPath(source);
321
double flip = m_intensity;
323
for (int i=0; i<path.elementCount(); ++i) {
324
QPainterPath::Element &e = const_cast<QPainterPath::Element &>(path.elementAt(i));
326
double x = e.x + offset.x();
327
double y = e.y + offset.y();
329
double dx = x - m_pos.x();
330
double dy = y - m_pos.y();
331
double len = m_radius - sqrt(dx * dx + dy * dy);
334
e.x = x + flip * dx * len / m_radius;
335
e.y = y + flip * dy * len / m_radius;
347
void PathDeformRenderer::paint(QPainter *painter)
352
int skip_x = qRound(m_pathBounds.width() + pad_x + m_fontSize/2);
353
int skip_y = qRound(m_pathBounds.height() + pad_y);
355
painter->setPen(Qt::NoPen);
356
painter->setBrush(Qt::black);
358
QRectF clip(painter->clipPath().boundingRect());
360
int overlap = pad_x / 2;
362
for (int start_y=0; start_y < height(); start_y += skip_y) {
364
if (start_y > clip.bottom())
367
int start_x = -overlap;
368
for (; start_x < width(); start_x += skip_x) {
370
if (start_y + skip_y >= clip.top() &&
371
start_x + skip_x >= clip.left() &&
372
start_x <= clip.right()) {
373
for (int i=0; i<m_paths.size(); ++i) {
374
QPainterPath path = lensDeform(m_paths[i], QPointF(start_x, start_y));
375
painter->drawPath(path);
379
overlap = skip_x - (start_x - width());
384
painter->drawImage(m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT),
387
painter->drawPixmap(m_pos - QPointF(m_radius + LENS_EXTENT, m_radius + LENS_EXTENT),
394
void PathDeformRenderer::setRadius(int radius)
396
double max = qMax(m_radius, (double)radius);
398
generateLensPixmap();
399
if (!m_animated || m_radius < max)
400
update(circle_bounds(m_pos, max, m_fontSize));
403
void PathDeformRenderer::setIntensity(int intensity)
405
m_intensity = intensity / 100.0;
407
update(circle_bounds(m_pos, m_radius, m_fontSize));