diff options
Diffstat (limited to 'examples/animation/stickman')
-rw-r--r-- | examples/animation/stickman/animation.cpp | 152 | ||||
-rw-r--r-- | examples/animation/stickman/animation.h | 40 | ||||
-rw-r--r-- | examples/animation/stickman/animations/chilling | bin | 0 -> 6508 bytes | |||
-rw-r--r-- | examples/animation/stickman/animations/dancing | bin | 0 -> 2348 bytes | |||
-rw-r--r-- | examples/animation/stickman/animations/dead | bin | 0 -> 268 bytes | |||
-rw-r--r-- | examples/animation/stickman/animations/jumping | bin | 0 -> 1308 bytes | |||
-rw-r--r-- | examples/animation/stickman/graphicsview.cpp | 15 | ||||
-rw-r--r-- | examples/animation/stickman/graphicsview.h | 19 | ||||
-rw-r--r-- | examples/animation/stickman/lifecycle.cpp | 176 | ||||
-rw-r--r-- | examples/animation/stickman/lifecycle.h | 40 | ||||
-rw-r--r-- | examples/animation/stickman/main.cpp | 58 | ||||
-rw-r--r-- | examples/animation/stickman/node.cpp | 42 | ||||
-rw-r--r-- | examples/animation/stickman/node.h | 26 | ||||
-rw-r--r-- | examples/animation/stickman/stickman.cpp | 299 | ||||
-rw-r--r-- | examples/animation/stickman/stickman.h | 59 | ||||
-rw-r--r-- | examples/animation/stickman/stickman.pro | 12 |
16 files changed, 938 insertions, 0 deletions
diff --git a/examples/animation/stickman/animation.cpp b/examples/animation/stickman/animation.cpp new file mode 100644 index 0000000..998632a --- /dev/null +++ b/examples/animation/stickman/animation.cpp @@ -0,0 +1,152 @@ +#include "animation.h" + +#include <QPointF> +#include <QIODevice> +#include <QDataStream> + +class Frame +{ +public: + Frame() { + } + + int nodeCount() const + { + return m_nodePositions.size(); + } + + void setNodeCount(int nodeCount) + { + while (nodeCount > m_nodePositions.size()) + m_nodePositions.append(QPointF()); + + while (nodeCount < m_nodePositions.size()) + m_nodePositions.removeLast(); + } + + QPointF nodePos(int idx) const + { + return m_nodePositions.at(idx); + } + + void setNodePos(int idx, const QPointF &pos) + { + m_nodePositions[idx] = pos; + } + +private: + QList<QPointF> m_nodePositions; +}; + +Animation::Animation() +{ + m_currentFrame = 0; + m_frames.append(new Frame); +} + +Animation::~Animation() +{ + qDeleteAll(m_frames); +} + +void Animation::setTotalFrames(int totalFrames) +{ + while (m_frames.size() < totalFrames) + m_frames.append(new Frame); + + while (totalFrames < m_frames.size()) + delete m_frames.takeLast(); +} + +int Animation::totalFrames() const +{ + return m_frames.size(); +} + +void Animation::setCurrentFrame(int currentFrame) +{ + m_currentFrame = qMax(qMin(currentFrame, totalFrames()-1), 0); +} + +int Animation::currentFrame() const +{ + return m_currentFrame; +} + +void Animation::setNodeCount(int nodeCount) +{ + Frame *frame = m_frames.at(m_currentFrame); + frame->setNodeCount(nodeCount); +} + +int Animation::nodeCount() const +{ + Frame *frame = m_frames.at(m_currentFrame); + return frame->nodeCount(); +} + +void Animation::setNodePos(int idx, const QPointF &pos) +{ + Frame *frame = m_frames.at(m_currentFrame); + frame->setNodePos(idx, pos); +} + +QPointF Animation::nodePos(int idx) const +{ + Frame *frame = m_frames.at(m_currentFrame); + return frame->nodePos(idx); +} + +QString Animation::name() const +{ + return m_name; +} + +void Animation::setName(const QString &name) +{ + m_name = name; +} + +void Animation::save(QIODevice *device) const +{ + QDataStream stream(device); + stream << m_name; + stream << m_frames.size(); + foreach (Frame *frame, m_frames) { + stream << frame->nodeCount(); + for (int i=0; i<frame->nodeCount(); ++i) + stream << frame->nodePos(i); + } +} + +void Animation::load(QIODevice *device) +{ + if (!m_frames.isEmpty()) + qDeleteAll(m_frames); + + m_frames.clear(); + + QDataStream stream(device); + stream >> m_name; + + int frameCount; + stream >> frameCount; + + for (int i=0; i<frameCount; ++i) { + + int nodeCount; + stream >> nodeCount; + + Frame *frame = new Frame; + frame->setNodeCount(nodeCount); + + for (int j=0; j<nodeCount; ++j) { + QPointF pos; + stream >> pos; + + frame->setNodePos(j, pos); + } + + m_frames.append(frame); + } +}
\ No newline at end of file diff --git a/examples/animation/stickman/animation.h b/examples/animation/stickman/animation.h new file mode 100644 index 0000000..22afab6 --- /dev/null +++ b/examples/animation/stickman/animation.h @@ -0,0 +1,40 @@ +#ifndef ANIMATION_H +#define ANIMATION_H + +#include <QPointF> +#include <QList> +#include <QString> + +class Frame; +class QIODevice; +class Animation +{ +public: + Animation(); + ~Animation(); + + void setTotalFrames(int totalFrames); + int totalFrames() const; + + void setCurrentFrame(int currentFrame); + int currentFrame() const; + + void setNodeCount(int nodeCount); + int nodeCount() const; + + void setNodePos(int idx, const QPointF &pos); + QPointF nodePos(int idx) const; + + QString name() const; + void setName(const QString &name); + + void save(QIODevice *device) const; + void load(QIODevice *device); + +private: + QString m_name; + QList<Frame *> m_frames; + int m_currentFrame; +}; + +#endif diff --git a/examples/animation/stickman/animations/chilling b/examples/animation/stickman/animations/chilling Binary files differnew file mode 100644 index 0000000..a81fc7a --- /dev/null +++ b/examples/animation/stickman/animations/chilling diff --git a/examples/animation/stickman/animations/dancing b/examples/animation/stickman/animations/dancing Binary files differnew file mode 100644 index 0000000..462f66f --- /dev/null +++ b/examples/animation/stickman/animations/dancing diff --git a/examples/animation/stickman/animations/dead b/examples/animation/stickman/animations/dead Binary files differnew file mode 100644 index 0000000..9859b4b --- /dev/null +++ b/examples/animation/stickman/animations/dead diff --git a/examples/animation/stickman/animations/jumping b/examples/animation/stickman/animations/jumping Binary files differnew file mode 100644 index 0000000..12661a1 --- /dev/null +++ b/examples/animation/stickman/animations/jumping diff --git a/examples/animation/stickman/graphicsview.cpp b/examples/animation/stickman/graphicsview.cpp new file mode 100644 index 0000000..1b6afa9 --- /dev/null +++ b/examples/animation/stickman/graphicsview.cpp @@ -0,0 +1,15 @@ +#include "graphicsview.h" + +#include <QtGui/QKeyEvent> + +GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent) {} + +void GraphicsView::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Escape) + close(); + + emit keyPressed(Qt::Key(e->key())); +} + + diff --git a/examples/animation/stickman/graphicsview.h b/examples/animation/stickman/graphicsview.h new file mode 100644 index 0000000..9ea2cfb --- /dev/null +++ b/examples/animation/stickman/graphicsview.h @@ -0,0 +1,19 @@ +#ifndef GRAPHICSVIEW_H +#define GRAPHICSVIEW + +#include <QtGui/QGraphicsView> + +class GraphicsView: public QGraphicsView +{ + Q_OBJECT +public: + GraphicsView(QWidget *parent = 0); + +protected: + void keyPressEvent(QKeyEvent *); + +signals: + void keyPressed(int key); +}; + +#endif diff --git a/examples/animation/stickman/lifecycle.cpp b/examples/animation/stickman/lifecycle.cpp new file mode 100644 index 0000000..3e92aec --- /dev/null +++ b/examples/animation/stickman/lifecycle.cpp @@ -0,0 +1,176 @@ +#include "lifecycle.h" +#include "stickman.h" +#include "node.h" +#include "animation.h" +#include "graphicsview.h" + +#include <QtCore> +#include <QtGui> + +class KeyPressTransition: public QSignalTransition +{ +public: + KeyPressTransition(GraphicsView *receiver, Qt::Key key) + : QSignalTransition(receiver, SIGNAL(keyPressed(int))), m_key(key) + { + } + KeyPressTransition(GraphicsView *receiver, Qt::Key key, QAbstractState *target) + : QSignalTransition(receiver, SIGNAL(keyPressed(int)), QList<QAbstractState*>() << target), m_key(key) + { + } + + virtual bool eventTest(QEvent *e) const + { + if (QSignalTransition::eventTest(e)) { + QVariant key = static_cast<QSignalEvent*>(e)->arguments().at(0); + return (key.toInt() == int(m_key)); + } + + return false; + } +private: + Qt::Key m_key; +}; + +class LightningStrikesTransition: public QEventTransition +{ +public: + LightningStrikesTransition(QAbstractState *target) + : QEventTransition(this, QEvent::Timer, QList<QAbstractState*>() << target) + { + qsrand((uint)QDateTime::currentDateTime().toTime_t()); + startTimer(1000); + } + + virtual bool eventTest(QEvent *e) const + { + return QEventTransition::eventTest(e) && ((qrand() % 50) == 0); + } +}; + +LifeCycle::LifeCycle(StickMan *stickMan, GraphicsView *keyReceiver) + : m_stickMan(stickMan), m_keyReceiver(keyReceiver) +{ + // Create animation group to be used for all transitions + m_animationGroup = new QParallelAnimationGroup(); + const int stickManNodeCount = m_stickMan->nodeCount(); + for (int i=0; i<stickManNodeCount; ++i) { + QPropertyAnimation *pa = new QPropertyAnimation(m_stickMan->node(i), "position"); + m_animationGroup->addAnimation(pa); + } + + // Set up intial state graph + m_machine = new QStateMachine(); + m_machine->setGlobalRestorePolicy(QState::RestoreProperties); + + m_alive = new QState(m_machine->rootState()); + m_alive->setObjectName("alive"); + + // Make it blink when lightning strikes before entering dead animation + QState *lightningBlink = new QState(m_machine->rootState()); + lightningBlink->setRestorePolicy(QState::DoNotRestoreProperties); + lightningBlink->setPropertyOnEntry(m_stickMan->scene(), "backgroundBrush", Qt::white); + lightningBlink->setPropertyOnEntry(m_stickMan, "penColor", Qt::black); + lightningBlink->setPropertyOnEntry(m_stickMan, "fillColor", Qt::white); + lightningBlink->setPropertyOnEntry(m_stickMan, "isDead", true); + + m_dead = new QState(m_machine->rootState()); + m_dead->setRestorePolicy(QState::DoNotRestoreProperties); + m_dead->setPropertyOnEntry(m_stickMan->scene(), "backgroundBrush", Qt::black); + m_dead->setPropertyOnEntry(m_stickMan, "penColor", Qt::white); + m_dead->setPropertyOnEntry(m_stickMan, "fillColor", Qt::black); + m_dead->setObjectName("dead"); + + // Idle state (sets no properties) + m_idle = new QState(m_alive); + m_idle->setObjectName("idle"); + m_alive->setInitialState(m_idle); + + // Lightning strikes at random + m_alive->addTransition(new LightningStrikesTransition(lightningBlink)); + m_alive->addTransition(new KeyPressTransition(m_keyReceiver, Qt::Key_L, lightningBlink)); + connectByAnimation(m_machine->rootState(), lightningBlink, m_dead); + + m_machine->setInitialState(m_alive); +} + +void LifeCycle::setResetKey(Qt::Key resetKey) +{ + // When resetKey is pressed, enter the idle state and do a restoration animation + // (requires no animation pointer, since no property is being set in the idle state) + m_alive->addAnimatedTransition(new KeyPressTransition(m_keyReceiver, resetKey, m_idle)); +} + +void LifeCycle::setDeathAnimation(const QString &fileName) +{ + QState *deathAnimation = makeState(m_dead, fileName); + m_dead->setInitialState(deathAnimation); +} + +void LifeCycle::start() +{ + m_machine->start(); +} + +void LifeCycle::connectByAnimation(QState *parentState, + QState *s1, QAbstractState *s2, + QAbstractTransition *transition) +{ + QAnimationState *animationState = new QAnimationState(m_animationGroup, parentState); + + if (transition == 0) + s1->addTransition(animationState); + else { + transition->setTargetStates(QList<QAbstractState*>() << animationState); + s1->addTransition(transition); + } + + animationState->addFinishedTransition(s2); +} + +void LifeCycle::addActivity(const QString &fileName, Qt::Key key) +{ + QState *state = makeState(m_alive, fileName); + connectByAnimation(m_alive, m_alive, state, new KeyPressTransition(m_keyReceiver, key)); +} + +QState *LifeCycle::makeState(QState *parentState, const QString &animationFileName) +{ + QState *topLevel = new QState(parentState); + + Animation animation; + { + QFile file(animationFileName); + if (file.open(QIODevice::ReadOnly)) + animation.load(&file); + } + + const int frameCount = animation.totalFrames(); + QState *previousState = 0; + for (int i=0; i<frameCount; ++i) { + QState *frameState = new QState(topLevel); + + animation.setCurrentFrame(i); + const int nodeCount = animation.nodeCount(); + for (int j=0; j<nodeCount; ++j) + frameState->setPropertyOnEntry(m_stickMan->node(j), "position", animation.nodePos(j)); + + if (previousState == 0) + topLevel->setInitialState(frameState); + else + connectByAnimation(topLevel, previousState, frameState); + previousState = frameState; + } + + // Loop + connectByAnimation(topLevel, previousState, topLevel->initialState()); + + return topLevel; + +} + +LifeCycle::~LifeCycle() +{ + delete m_machine; + delete m_animationGroup; +} diff --git a/examples/animation/stickman/lifecycle.h b/examples/animation/stickman/lifecycle.h new file mode 100644 index 0000000..8094a76 --- /dev/null +++ b/examples/animation/stickman/lifecycle.h @@ -0,0 +1,40 @@ +#ifndef LIFECYCLE_H +#define LIFECYCLE_H + +#include <Qt> + +class StickMan; +class QStateMachine; +class QAnimationGroup; +class QState; +class QAbstractState; +class QAbstractTransition; +class GraphicsView; +class LifeCycle +{ +public: + LifeCycle(StickMan *stickMan, GraphicsView *keyEventReceiver); + ~LifeCycle(); + + void setDeathAnimation(const QString &fileName); + void setResetKey(Qt::Key key); + void addActivity(const QString &fileName, Qt::Key key); + + void start(); + +private: + void connectByAnimation(QState *parentState, QState *s1, QAbstractState *s2, + QAbstractTransition *transition = 0); + QState *makeState(QState *parentState, const QString &animationFileName); + + StickMan *m_stickMan; + QStateMachine *m_machine; + QAnimationGroup *m_animationGroup; + GraphicsView *m_keyReceiver; + + QState *m_alive; + QState *m_dead; + QState *m_idle; +}; + +#endif diff --git a/examples/animation/stickman/main.cpp b/examples/animation/stickman/main.cpp new file mode 100644 index 0000000..a094e28 --- /dev/null +++ b/examples/animation/stickman/main.cpp @@ -0,0 +1,58 @@ +#include "animation.h" +#include "node.h" +#include "lifecycle.h" +#include "stickman.h" +#include "graphicsview.h" + +#include <QtCore> +#include <QtGui> + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + StickMan *stickMan = new StickMan; + stickMan->setDrawSticks(false); + + QGraphicsTextItem *textItem = new QGraphicsTextItem(); + textItem->setHtml("<font color=\"white\"><b>Stickman</b>" + "<p>" + "Tell the stickman what to do!" + "</p>" + "<p><i>" + "<li>Press <font color=\"purple\">J</font> to make the stickman jump.</li>" + "<li>Press <font color=\"purple\">D</font> to make the stickman dance.</li>" + "<li>Press <font color=\"purple\">C</font> to make him chill out.</li>" + "<li>Press <font color=\"purple\">Return</font> to make him return to his original position.</li>" + "<li>When you are done, press <font color=\"purple\">Escape</font>.</li>" + "</i></p>" + "<p>If you are unlucky, the stickman will get struck by lightning, and never jump, dance or chill out again." + "</p></font>"); + qreal w = textItem->boundingRect().width(); + QRectF stickManBoundingRect = stickMan->mapToScene(stickMan->boundingRect()).boundingRect(); + textItem->setPos(-w / 2.0, stickManBoundingRect.bottom() + 25.0); + + QGraphicsScene *scene = new QGraphicsScene(); + scene->addItem(stickMan); + scene->addItem(textItem); + scene->setBackgroundBrush(Qt::black); + + GraphicsView *view = new GraphicsView(); + view->setRenderHints(QPainter::Antialiasing); + view->setTransformationAnchor(QGraphicsView::NoAnchor); + view->setScene(scene); + view->showFullScreen(); + view->setFocus(); + view->setSceneRect(scene->sceneRect()); + + LifeCycle *cycle = new LifeCycle(stickMan, view); + cycle->setResetKey(Qt::Key_Return); + cycle->setDeathAnimation("animations/dead"); + + cycle->addActivity("animations/jumping", Qt::Key_J); + cycle->addActivity("animations/dancing", Qt::Key_D); + cycle->addActivity("animations/chilling", Qt::Key_C); + cycle->start(); + + return app.exec(); +} diff --git a/examples/animation/stickman/node.cpp b/examples/animation/stickman/node.cpp new file mode 100644 index 0000000..f3468d0 --- /dev/null +++ b/examples/animation/stickman/node.cpp @@ -0,0 +1,42 @@ +#include "node.h" + +#include <QRectF> +#include <QPainter> +#include <QGraphicsSceneMouseEvent> + +Node::Node(const QPointF &pos, QGraphicsItem *parent) + : QGraphicsItem(parent), m_dragging(false) +{ + setPos(pos); +} + +Node::~Node() +{ +} + +QRectF Node::boundingRect() const +{ + return QRectF(-6.0, -6.0, 12.0, 12.0); +} + +void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + painter->setPen(Qt::white); + painter->drawEllipse(QPointF(0.0, 0.0), 5.0, 5.0); +} + +void Node::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + m_dragging = true; +} + +void Node::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + if (m_dragging) + setPos(mapToParent(event->pos())); +} + +void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + m_dragging = false; +}
\ No newline at end of file diff --git a/examples/animation/stickman/node.h b/examples/animation/stickman/node.h new file mode 100644 index 0000000..b796774 --- /dev/null +++ b/examples/animation/stickman/node.h @@ -0,0 +1,26 @@ +#ifndef NODE_H +#define NODE_H + +#include <QGraphicsItem> + +class Node: public QObject, public QGraphicsItem +{ + Q_OBJECT + Q_PROPERTY(QPointF position READ pos WRITE setPos); +public: + Node(const QPointF &pos, QGraphicsItem *parent = 0); + ~Node(); + + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *); + void mouseMoveEvent(QGraphicsSceneMouseEvent *); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *); + +private: + bool m_dragging; +}; + +#endif diff --git a/examples/animation/stickman/stickman.cpp b/examples/animation/stickman/stickman.cpp new file mode 100644 index 0000000..22d48d3 --- /dev/null +++ b/examples/animation/stickman/stickman.cpp @@ -0,0 +1,299 @@ +#include "stickman.h" +#include "node.h" + +#include <QPainter> +#include <QTimer> + +#define _USE_MATH_DEFINES +#include <math.h> + +static const int NodeCount = 16; +static const qreal Coords[NodeCount * 2] = { + 0.0, -150.0, // head, #0 + + 0.0, -100.0, // body pentagon, top->bottom, left->right, #1 - 5 + -50.0, -50.0, + 50.0, -50.0, + -25.0, 50.0, + 25.0, 50.0, + + -100.0, 0.0, // right arm, #6 - 7 + -125.0, 50.0, + + 100.0, 0.0, // left arm, #8 - 9 + 125.0, 50.0, + + -35.0, 75.0, // lower body, #10 - 11 + 35.0, 75.0, + + -25.0, 200.0, // right leg, #12 - 13 + -30.0, 300.0, + + 25.0, 200.0, // left leg, #14 - 15 + 30.0, 300.0 + +}; + +static const int BoneCount = 24; +static const int Bones[BoneCount * 2] = { + 0, 1, // neck + + 1, 2, // body + 1, 3, + 1, 4, + 1, 5, + 2, 3, + 2, 4, + 2, 5, + 3, 4, + 3, 5, + 4, 5, + + 2, 6, // right arm + 6, 7, + + 3, 8, // left arm + 8, 9, + + 4, 10, // lower body + 4, 11, + 5, 10, + 5, 11, + 10, 11, + + 10, 12, // right leg + 12, 13, + + 11, 14, // left leg + 14, 15 + +}; + +StickMan::StickMan() +{ + m_nodes = new Node*[NodeCount]; + m_sticks = true; + m_isDead = false; + m_pixmap = QPixmap("images/head.png"); + m_penColor = Qt::white; + m_fillColor = Qt::black; + + // Set up start position of limbs + for (int i=0; i<NodeCount; ++i) { + m_nodes[i] = new Node(QPointF(Coords[i * 2], Coords[i * 2 + 1]), this); + } + + m_perfectBoneLengths = new qreal[BoneCount]; + for (int i=0; i<BoneCount; ++i) { + int n1 = Bones[i * 2]; + int n2 = Bones[i * 2 + 1]; + + Node *node1 = m_nodes[n1]; + Node *node2 = m_nodes[n2]; + + QPointF dist = node1->pos() - node2->pos(); + m_perfectBoneLengths[i] = sqrt(pow(dist.x(),2) + pow(dist.y(),2)); + } + + startTimer(10); +} + +StickMan::~StickMan() +{ + delete m_nodes; +} + +void StickMan::setDrawSticks(bool on) +{ + m_sticks = on; + for (int i=0;i<nodeCount();++i) { + Node *node = m_nodes[i]; + node->setVisible(on); + } +} + +QRectF StickMan::boundingRect() const +{ + // account for head radius=50.0 plus pen which is 5.0, plus jump height :-) + return QRectF(-125, -200, 250, 450 + 50).adjusted(-55.0, -55.0, 55.0, 55.0); +} + +int StickMan::nodeCount() const +{ + return NodeCount; +} + +Node *StickMan::node(int idx) const +{ + const_cast<StickMan *>(this)->prepareGeometryChange(); + if (idx >= 0 && idx < NodeCount) + return m_nodes[idx]; + else + return 0; +} + +void StickMan::timerEvent(QTimerEvent *e) +{ + prepareGeometryChange(); +} + +void StickMan::stabilize() +{ + for (int i=0; i<BoneCount; ++i) { + int n1 = Bones[i * 2]; + int n2 = Bones[i * 2 + 1]; + + Node *node1 = m_nodes[n1]; + Node *node2 = m_nodes[n2]; + + QPointF pos1 = node1->pos(); + QPointF pos2 = node2->pos(); + + QPointF dist = pos1 - pos2; + qreal length = sqrt(pow(dist.x(),2) + pow(dist.y(),2)); + qreal diff = (length - m_perfectBoneLengths[i]) / length; + + pos1 -= dist * (0.5 * diff); + pos2 += dist * (0.5 * diff); + + node1->setPos(pos1); + node2->setPos(pos2); + + } +} + +QPointF StickMan::posFor(int idx) const +{ + return m_nodes[idx]->pos(); +} + +//#include <QTime> +void StickMan::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + /* static int frames = 0; + static QTime time; + if (frames++ % 100 == 0) { + frames = 1; + time.restart(); + } + + if (time.elapsed() > 0) { + painter->setPen(Qt::white); + painter->drawText(0, 0, QString::number(frames / (time.elapsed() / 1000.0))); + }*/ + + stabilize(); + if (m_sticks) { + painter->setPen(Qt::white); + for (int i=0; i<BoneCount; ++i) { + int n1 = Bones[i * 2]; + int n2 = Bones[i * 2 + 1]; + + Node *node1 = m_nodes[n1]; + Node *node2 = m_nodes[n2]; + + painter->drawLine(node1->pos(), node2->pos()); + } + } else { + // first bone is neck and will be used for head + + QPainterPath path; + path.moveTo(posFor(0)); + path.lineTo(posFor(1)); + + // right arm + path.lineTo(posFor(2)); + path.lineTo(posFor(6)); + path.lineTo(posFor(7)); + + // left arm + path.moveTo(posFor(3)); + path.lineTo(posFor(8)); + path.lineTo(posFor(9)); + + // body + path.moveTo(posFor(2)); + path.lineTo(posFor(4)); + path.lineTo(posFor(10)); + path.lineTo(posFor(11)); + path.lineTo(posFor(5)); + path.lineTo(posFor(3)); + path.lineTo(posFor(1)); + + // right leg + path.moveTo(posFor(10)); + path.lineTo(posFor(12)); + path.lineTo(posFor(13)); + + // left leg + path.moveTo(posFor(11)); + path.lineTo(posFor(14)); + path.lineTo(posFor(15)); + + painter->setPen(QPen(m_penColor, 5.0, Qt::SolidLine, Qt::RoundCap)); + painter->drawPath(path); + + { + int n1 = Bones[0]; + int n2 = Bones[1]; + Node *node1 = m_nodes[n1]; + Node *node2 = m_nodes[n2]; + + QPointF dist = node2->pos() - node1->pos(); + + qreal sinAngle = dist.x() / sqrt(pow(dist.x(), 2) + pow(dist.y(), 2)); + qreal angle = asin(sinAngle) * 180.0 / M_PI; + + QPointF headPos = node1->pos(); + painter->save(); + painter->translate(headPos); + painter->rotate(-angle); + + painter->setBrush(m_fillColor); + painter->drawEllipse(QPointF(0,0), 50.0, 50.0); + + /*painter->drawArc(QRectF(-20.0, 0.0, 40.0, 20.0), 30.0 * 16, 120.0 * 16); + + painter->setBrush(m_penColor); + painter->drawEllipse(QPointF(-30.0, -30.0), 2.5, 2.5); + painter->drawEllipse(QPointF(30.0, -30.0), 2.5, 2.5);*/ + + painter->setBrush(m_penColor); + painter->setPen(QPen(m_penColor, 2.5, Qt::SolidLine, Qt::RoundCap)); + + // eyes + if (m_isDead) { + painter->drawLine(-30.0, -30.0, -20.0, -20.0); + painter->drawLine(-20.0, -30.0, -30.0, -20.0); + + painter->drawLine(20.0, -30.0, 30.0, -20.0); + painter->drawLine(30.0, -30.0, 20.0, -20.0); + } else { + painter->drawChord(QRectF(-30.0, -30.0, 25.0, 70.0), 30.0*16, 120.0*16); + painter->drawChord(QRectF(5.0, -30.0, 25.0, 70.0), 30.0*16, 120.0*16); + } + + // mouth + if (m_isDead) { + painter->drawLine(-28.0, 2.0, 29.0, 2.0); + } else { + painter->setBrush(QColor(128, 0, 64 )); + painter->drawChord(QRectF(-28.0, 2.0-55.0/2.0, 57.0, 55.0), 0.0, -180.0*16); + } + + // pupils + if (!m_isDead) { + painter->setPen(QPen(m_fillColor, 1.0, Qt::SolidLine, Qt::RoundCap)); + painter->setBrush(m_fillColor); + painter->drawEllipse(QPointF(-12.0, -25.0), 5.0, 5.0); + painter->drawEllipse(QPointF(22.0, -25.0), 5.0, 5.0); + } + + + painter->restore(); + } + } +} + + + diff --git a/examples/animation/stickman/stickman.h b/examples/animation/stickman/stickman.h new file mode 100644 index 0000000..ae406ca --- /dev/null +++ b/examples/animation/stickman/stickman.h @@ -0,0 +1,59 @@ +#ifndef STICKMAN_H +#define STICKMAN_H + +#include <QGraphicsItem> + +const int LimbCount = 16; + +class Node; +class QTimer; +class StickMan: public QObject, public QGraphicsItem +{ + Q_OBJECT + Q_PROPERTY(QColor penColor WRITE setPenColor READ penColor) + Q_PROPERTY(QColor fillColor WRITE setFillColor READ fillColor) + Q_PROPERTY(bool isDead WRITE setIsDead READ isDead) +public: + StickMan(); + ~StickMan(); + + virtual QRectF boundingRect() const; + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + + int nodeCount() const; + Node *node(int idx) const; + + void setDrawSticks(bool on); + bool drawSticks() const { return m_sticks; } + + QColor penColor() const { return m_penColor; } + void setPenColor(const QColor &color) { m_penColor = color; } + + QColor fillColor() const { return m_fillColor; } + void setFillColor(const QColor &color) { m_fillColor = color; } + + bool isDead() const { return m_isDead; } + void setIsDead(bool isDead) { m_isDead = isDead; } + +public slots: + void stabilize(); + +protected: + void timerEvent(QTimerEvent *e); + +private: + QPointF posFor(int idx) const; + + Node **m_nodes; + qreal *m_perfectBoneLengths; + + uint m_sticks : 1; + uint m_isDead : 1; + uint m_reserved : 30; + + QPixmap m_pixmap; + QColor m_penColor; + QColor m_fillColor; +}; + +#endif // STICKMAN_H diff --git a/examples/animation/stickman/stickman.pro b/examples/animation/stickman/stickman.pro new file mode 100644 index 0000000..136cb44 --- /dev/null +++ b/examples/animation/stickman/stickman.pro @@ -0,0 +1,12 @@ +###################################################################### +# Automatically generated by qmake (2.01a) ti 3. feb 19:50:14 2009 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += stickman.h animation.h node.h lifecycle.h graphicsview.h +SOURCES += main.cpp stickman.cpp animation.cpp node.cpp lifecycle.cpp graphicsview.cpp |