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 "pathstroke.h"
00025 #include "arthurstyle.h"
00026 #include "arthurwidgets.h"
00027
00028 extern void draw_round_rect(QPainter *p, const QRect &bounds, int radius);
00029
00030 PathStrokeWidget::PathStrokeWidget()
00031 {
00032 setWindowTitle(tr("Path Stroking"));
00033
00034
00035 QPalette pal = palette();
00036
00037
00038
00039 setPalette(pal);
00040
00041
00042 m_renderer = new PathStrokeRenderer(this);
00043
00044 QGroupBox *mainGroup = new QGroupBox(this);
00045
00046 mainGroup->setFixedWidth(180);
00047 mainGroup->setTitle("Path Stroking");
00048
00049 QGroupBox *capGroup = new QGroupBox(mainGroup);
00050 capGroup->setAttribute(Qt::WA_ContentsPropagated);
00051 QRadioButton *flatCap = new QRadioButton(capGroup);
00052 QRadioButton *squareCap = new QRadioButton(capGroup);
00053 QRadioButton *roundCap = new QRadioButton(capGroup);
00054 capGroup->setTitle("Cap Style");
00055 flatCap->setText("Flat Cap");
00056 squareCap->setText("Square Cap");
00057 roundCap->setText("Round Cap");
00058
00059 QGroupBox *joinGroup = new QGroupBox(mainGroup);
00060 joinGroup->setAttribute(Qt::WA_ContentsPropagated);
00061 QRadioButton *bevelJoin = new QRadioButton(joinGroup);
00062 QRadioButton *miterJoin = new QRadioButton(joinGroup);
00063 QRadioButton *roundJoin = new QRadioButton(joinGroup);
00064 joinGroup->setTitle("Join Style");
00065 bevelJoin->setText("Bevel Join");
00066 miterJoin->setText("Miter Join");
00067 roundJoin->setText("Round Join");
00068
00069 QGroupBox *styleGroup = new QGroupBox(mainGroup);
00070 styleGroup->setAttribute(Qt::WA_ContentsPropagated);
00071 QRadioButton *solidLine = new QRadioButton(styleGroup);
00072 QRadioButton *dashLine = new QRadioButton(styleGroup);
00073 QRadioButton *dotLine = new QRadioButton(styleGroup);
00074 QRadioButton *dashDotLine = new QRadioButton(styleGroup);
00075 QRadioButton *dashDotDotLine = new QRadioButton(styleGroup);
00076 QRadioButton *customDashLine = new QRadioButton(styleGroup);
00077 styleGroup->setTitle("Pen Style");
00078 #if 0
00079 solidLine->setText("Solid Line");
00080 dashLine->setText("Dash Line");
00081 dotLine->setText("Dot Line");
00082 dashDotLine->setText("Dash Dot Line");
00083 dashDotDotLine->setText("Dash Dot Dot Line");
00084 #else
00085 QPixmap line_solid(":res/images/line_solid.png");
00086 solidLine->setIcon(line_solid);
00087 solidLine->setIconSize(line_solid.size());
00088 QPixmap line_dashed(":res/images/line_dashed.png");
00089 dashLine->setIcon(line_dashed);
00090 dashLine->setIconSize(line_dashed.size());
00091 QPixmap line_dotted(":res/images/line_dotted.png");
00092 dotLine->setIcon(line_dotted);
00093 dotLine->setIconSize(line_dotted.size());
00094 QPixmap line_dash_dot(":res/images/line_dash_dot.png");
00095 dashDotLine->setIcon(line_dash_dot);
00096 dashDotLine->setIconSize(line_dash_dot.size());
00097 QPixmap line_dash_dot_dot(":res/images/line_dash_dot_dot.png");
00098 dashDotDotLine->setIcon(line_dash_dot_dot);
00099 dashDotDotLine->setIconSize(line_dash_dot_dot.size());
00100 customDashLine->setText("Custom Style");
00101
00102 int fixedHeight = bevelJoin->sizeHint().height();
00103 solidLine->setFixedHeight(fixedHeight);
00104 dashLine->setFixedHeight(fixedHeight);
00105 dotLine->setFixedHeight(fixedHeight);
00106 dashDotLine->setFixedHeight(fixedHeight);
00107 dashDotDotLine->setFixedHeight(fixedHeight);
00108 #endif
00109
00110 QGroupBox *pathModeGroup = new QGroupBox(mainGroup);
00111 pathModeGroup->setAttribute(Qt::WA_ContentsPropagated);
00112 QRadioButton *curveMode = new QRadioButton(pathModeGroup);
00113 QRadioButton *lineMode = new QRadioButton(pathModeGroup);
00114 pathModeGroup->setTitle("Path composed of");
00115 curveMode->setText("Curves");
00116 lineMode->setText("Lines");
00117
00118 QGroupBox *penWidthGroup = new QGroupBox(mainGroup);
00119 penWidthGroup->setAttribute(Qt::WA_ContentsPropagated);
00120 QSlider *penWidth = new QSlider(Qt::Horizontal, penWidthGroup);
00121 penWidth->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
00122 penWidthGroup->setTitle("Pen Width");
00123 penWidth->setRange(0, 500);
00124
00125 #if 0
00126 QCheckBox *animated = new QCheckBox(mainGroup);
00127 animated->setText("Animated");
00128 #else
00129 QPushButton *animated = new QPushButton(mainGroup);
00130 animated->setText("Animate");
00131 animated->setCheckable(true);
00132 #endif
00133
00134 QPushButton *showSourceButton = new QPushButton(mainGroup);
00135 showSourceButton->setText("Show Source");
00136 #ifdef QT_OPENGL_SUPPORT
00137 QPushButton *enableOpenGLButton = new QPushButton(mainGroup);
00138 enableOpenGLButton->setText("Use OpenGL");
00139 enableOpenGLButton->setCheckable(true);
00140 enableOpenGLButton->setChecked(m_renderer->usesOpenGL());
00141 if (!QGLFormat::hasOpenGL())
00142 enableOpenGLButton->hide();
00143 #endif
00144 QPushButton *whatsThisButton = new QPushButton(mainGroup);
00145 whatsThisButton->setText("What's This?");
00146 whatsThisButton->setCheckable(true);
00147
00148
00149 QHBoxLayout *viewLayout = new QHBoxLayout(this);
00150 viewLayout->addWidget(m_renderer);
00151 viewLayout->addWidget(mainGroup);
00152
00153 QVBoxLayout *mainGroupLayout = new QVBoxLayout(mainGroup);
00154 mainGroupLayout->setMargin(3);
00155 mainGroupLayout->addWidget(capGroup);
00156 mainGroupLayout->addWidget(joinGroup);
00157 mainGroupLayout->addWidget(styleGroup);
00158 mainGroupLayout->addWidget(penWidthGroup);
00159 mainGroupLayout->addWidget(pathModeGroup);
00160 mainGroupLayout->addWidget(animated);
00161 mainGroupLayout->addStretch(1);
00162 mainGroupLayout->addWidget(showSourceButton);
00163 #ifdef QT_OPENGL_SUPPORT
00164 mainGroupLayout->addWidget(enableOpenGLButton);
00165 #endif
00166 mainGroupLayout->addWidget(whatsThisButton);
00167
00168 QVBoxLayout *capGroupLayout = new QVBoxLayout(capGroup);
00169 capGroupLayout->addWidget(flatCap);
00170 capGroupLayout->addWidget(squareCap);
00171 capGroupLayout->addWidget(roundCap);
00172
00173 QVBoxLayout *joinGroupLayout = new QVBoxLayout(joinGroup);
00174 joinGroupLayout->addWidget(bevelJoin);
00175 joinGroupLayout->addWidget(miterJoin);
00176 joinGroupLayout->addWidget(roundJoin);
00177
00178 QVBoxLayout *styleGroupLayout = new QVBoxLayout(styleGroup);
00179 styleGroupLayout->addWidget(solidLine);
00180 styleGroupLayout->addWidget(dashLine);
00181 styleGroupLayout->addWidget(dotLine);
00182 styleGroupLayout->addWidget(dashDotLine);
00183 styleGroupLayout->addWidget(dashDotDotLine);
00184 styleGroupLayout->addWidget(customDashLine);
00185
00186 QVBoxLayout *pathModeGroupLayout = new QVBoxLayout(pathModeGroup);
00187 pathModeGroupLayout->addWidget(curveMode);
00188 pathModeGroupLayout->addWidget(lineMode);
00189
00190 QVBoxLayout *penWidthLayout = new QVBoxLayout(penWidthGroup);
00191 penWidthLayout->addWidget(penWidth);
00192
00193
00194 connect(penWidth, SIGNAL(valueChanged(int)),
00195 m_renderer, SLOT(setPenWidth(int)));
00196 connect(animated, SIGNAL(toggled(bool)),
00197 m_renderer, SLOT(setAnimation(bool)));
00198
00199 connect(flatCap, SIGNAL(clicked()), m_renderer, SLOT(setFlatCap()));
00200 connect(squareCap, SIGNAL(clicked()), m_renderer, SLOT(setSquareCap()));
00201 connect(roundCap, SIGNAL(clicked()), m_renderer, SLOT(setRoundCap()));
00202
00203 connect(bevelJoin, SIGNAL(clicked()), m_renderer, SLOT(setBevelJoin()));
00204 connect(miterJoin, SIGNAL(clicked()), m_renderer, SLOT(setMiterJoin()));
00205 connect(roundJoin, SIGNAL(clicked()), m_renderer, SLOT(setRoundJoin()));
00206
00207 connect(curveMode, SIGNAL(clicked()), m_renderer, SLOT(setCurveMode()));
00208 connect(lineMode, SIGNAL(clicked()), m_renderer, SLOT(setLineMode()));
00209
00210 connect(solidLine, SIGNAL(clicked()), m_renderer, SLOT(setSolidLine()));
00211 connect(dashLine, SIGNAL(clicked()), m_renderer, SLOT(setDashLine()));
00212 connect(dotLine, SIGNAL(clicked()), m_renderer, SLOT(setDotLine()));
00213 connect(dashDotLine, SIGNAL(clicked()), m_renderer, SLOT(setDashDotLine()));
00214 connect(dashDotDotLine, SIGNAL(clicked()), m_renderer, SLOT(setDashDotDotLine()));
00215 connect(customDashLine, SIGNAL(clicked()), m_renderer, SLOT(setCustomDashLine()));
00216
00217 connect(showSourceButton, SIGNAL(clicked()), m_renderer, SLOT(showSource()));
00218 #ifdef QT_OPENGL_SUPPORT
00219 connect(enableOpenGLButton, SIGNAL(clicked(bool)), m_renderer, SLOT(enableOpenGL(bool)));
00220 #endif
00221 connect(whatsThisButton, SIGNAL(clicked(bool)), m_renderer, SLOT(setDescriptionEnabled(bool)));
00222 connect(m_renderer, SIGNAL(descriptionEnabledChanged(bool)),
00223 whatsThisButton, SLOT(setChecked(bool)));
00224
00225
00226 animated->setChecked(true);
00227 flatCap->setChecked(true);
00228 bevelJoin->setChecked(true);
00229 penWidth->setValue(50);
00230 curveMode->setChecked(true);
00231 solidLine->setChecked(true);
00232
00233 m_renderer->loadSourceFile(":res/pathstroke.cpp");
00234 m_renderer->loadDescription(":res/pathstroke.html");
00235 }
00236
00237
00238 PathStrokeRenderer::PathStrokeRenderer(QWidget *parent)
00239 : ArthurFrame(parent)
00240 {
00241 m_pointSize = 10;
00242 m_activePoint = -1;
00243 m_capStyle = Qt::FlatCap;
00244 m_joinStyle = Qt::BevelJoin;
00245 m_pathMode = CurveMode;
00246 m_penWidth = 1;
00247 m_penStyle = Qt::SolidLine;
00248 m_wasAnimated = true;
00249 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
00250 }
00251
00252 void PathStrokeRenderer::paint(QPainter *painter)
00253 {
00254 if (m_points.isEmpty())
00255 initializePoints();
00256
00257 painter->setRenderHint(QPainter::Antialiasing);
00258
00259 QPalette pal = palette();
00260 painter->setPen(Qt::NoPen);
00261
00262
00263 QPainterPath path;
00264 path.moveTo(m_points.at(0));
00265
00266 if (m_pathMode == LineMode) {
00267 for (int i=1; i<m_points.size(); ++i) {
00268 path.lineTo(m_points.at(i));
00269 }
00270 } else {
00271 int i=1;
00272 while (i + 2 < m_points.size()) {
00273 path.cubicTo(m_points.at(i), m_points.at(i+1), m_points.at(i+2));
00274 i += 3;
00275 }
00276 while (i < m_points.size()) {
00277 path.lineTo(m_points.at(i));
00278 ++i;
00279 }
00280 }
00281
00282
00283 {
00284 QColor lg = Qt::red;
00285
00286
00287 if (m_penStyle == Qt::NoPen) {
00288 QPainterPathStroker stroker;
00289 stroker.setWidth(m_penWidth);
00290 stroker.setJoinStyle(m_joinStyle);
00291 stroker.setCapStyle(m_capStyle);
00292
00293 QVector<qreal> dashes;
00294 qreal space = 4;
00295 dashes << 1 << space
00296 << 3 << space
00297 << 9 << space
00298 << 27 << space
00299 << 9 << space
00300 << 3 << space;
00301 stroker.setDashPattern(dashes);
00302 QPainterPath stroke = stroker.createStroke(path);
00303 painter->fillPath(stroke, lg);
00304
00305 } else {
00306 QPen pen(lg, m_penWidth, m_penStyle, m_capStyle, m_joinStyle);
00307 painter->strokePath(path, pen);
00308 }
00309 }
00310
00311 if (1) {
00312
00313 painter->setPen(QColor(50, 100, 120, 200));
00314 painter->setBrush(QColor(200, 200, 210, 120));
00315 for (int i=0; i<m_points.size(); ++i) {
00316 QPointF pos = m_points.at(i);
00317 painter->drawEllipse(QRectF(pos.x() - m_pointSize,
00318 pos.y() - m_pointSize,
00319 m_pointSize*2, m_pointSize*2));
00320 }
00321 painter->setPen(QPen(Qt::lightGray, 0, Qt::SolidLine));
00322 painter->setBrush(Qt::NoBrush);
00323 painter->drawPolyline(m_points);
00324 }
00325
00326 }
00327
00328 void PathStrokeRenderer::initializePoints()
00329 {
00330 const int count = 7;
00331 m_points.clear();
00332 m_vectors.clear();
00333
00334 QMatrix m;
00335 double rot = 360 / count;
00336 QPointF center(width() / 2, height() / 2);
00337 QMatrix vm;
00338 vm.shear(2, -1);
00339 vm.scale(3, 3);
00340
00341 for (int i=0; i<count; ++i) {
00342 m_vectors << QPointF(.1f, .25f) * (m * vm);
00343 m_points << QPointF(0, 100) * m + center;
00344 m.rotate(rot);
00345 }
00346 }
00347
00348 void PathStrokeRenderer::updatePoints()
00349 {
00350 double pad = 10;
00351 double left = pad;
00352 double right = width() - pad;
00353 double top = pad;
00354 double bottom = height() - pad;
00355
00356 Q_ASSERT(m_points.size() == m_vectors.size());
00357 for (int i=0; i<m_points.size(); ++i) {
00358
00359 if (i == m_activePoint)
00360 continue;
00361
00362 QPointF pos = m_points.at(i);
00363 QPointF vec = m_vectors.at(i);
00364 pos += vec;
00365 if (pos.x() < left || pos.x() > right) {
00366 vec.setX(-vec.x());
00367 pos.setX(pos.x() < left ? left : right);
00368 } if (pos.y() < top || pos.y() > bottom) {
00369 vec.setY(-vec.y());
00370 pos.setY(pos.y() < top ? top : bottom);
00371 }
00372 m_points[i] = pos;
00373 m_vectors[i] = vec;
00374 }
00375 update();
00376 }
00377
00378 void PathStrokeRenderer::mousePressEvent(QMouseEvent *e)
00379 {
00380 setDescriptionEnabled(false);
00381 m_activePoint = -1;
00382 qreal distance = -1;
00383 for (int i=0; i<m_points.size(); ++i) {
00384 qreal d = QLineF(e->pos(), m_points.at(i)).length();
00385 if ((distance < 0 && d < 8 * m_pointSize) || d < distance) {
00386 distance = d;
00387 m_activePoint = i;
00388 }
00389 }
00390
00391 if (m_activePoint != -1) {
00392 m_wasAnimated = m_timer.isActive();
00393 setAnimation(false);
00394 mouseMoveEvent(e);
00395 }
00396 }
00397
00398 void PathStrokeRenderer::mouseMoveEvent(QMouseEvent *e)
00399 {
00400 if (m_activePoint >= 0 && m_activePoint < m_points.size()) {
00401 m_points[m_activePoint] = e->pos();
00402 update();
00403 }
00404 }
00405
00406 void PathStrokeRenderer::mouseReleaseEvent(QMouseEvent *)
00407 {
00408 m_activePoint = -1;
00409 setAnimation(m_wasAnimated);
00410 }
00411
00412 void PathStrokeRenderer::timerEvent(QTimerEvent *e)
00413 {
00414 if (e->timerId() == m_timer.timerId()) {
00415 updatePoints();
00416 QApplication::syncX();
00417 }
00418
00419
00420
00421 }
00422
00423 void PathStrokeRenderer::setAnimation(bool animation)
00424 {
00425 m_timer.stop();
00426
00427
00428 if (animation) {
00429 m_timer.start(25, this);
00430
00431
00432 }
00433 }