From 4d312a6e635ce0cde0ae5c439541f315d7fddff3 Mon Sep 17 00:00:00 2001
From: Andreas Aardal Hanssen <andreas.aardal.hanssen@nokia.com>
Date: Thu, 29 Apr 2010 12:39:13 +0200
Subject: Documentation for the Elastic Nodes example.

Checked for qdoc errors.
---
 doc/src/examples/elasticnodes.qdoc                 | 391 ++++++++++++++++++++-
 doc/src/getting-started/examples.qdoc              |   2 +-
 examples/graphicsview/elasticnodes/edge.cpp        |  30 +-
 examples/graphicsview/elasticnodes/edge.h          |   6 +-
 examples/graphicsview/elasticnodes/graphwidget.cpp |  34 +-
 examples/graphicsview/elasticnodes/graphwidget.h   |   4 +-
 examples/graphicsview/elasticnodes/node.cpp        |  46 ++-
 examples/graphicsview/elasticnodes/node.h          |   2 +
 8 files changed, 471 insertions(+), 44 deletions(-)

diff --git a/doc/src/examples/elasticnodes.qdoc b/doc/src/examples/elasticnodes.qdoc
index f7b1c37..edc62d8 100644
--- a/doc/src/examples/elasticnodes.qdoc
+++ b/doc/src/examples/elasticnodes.qdoc
@@ -43,7 +43,396 @@
     \example graphicsview/elasticnodes
     \title Elastic Nodes Example
 
-    This GraphicsView example shows how to implement edges between nodes in a graph.
+    This GraphicsView example shows how to implement edges between nodes in a
+    graph, with basic interaction. You can click to drag a node around, and
+    zoom in and out using the mouse wheel or the keyboard. Hitting the space
+    bar will randomize the nodes. The example is also resolution independent;
+    as you zoom in, the graphics remain crisp.
 
     \image elasticnodes-example.png
+
+    Graphics View provides the QGraphicsScene class for managing and
+    interacting with a large number of custom-made 2D graphical items derived
+    from the QGraphicsItem class, and a QGraphicsView widget for visualizing
+    the items, with support for zooming and rotation.
+
+    This example consists of a \c Node class, an \c Edge class, a \c
+    GraphWidget test, and a \c main function: the \c Node class represents
+    draggable yellow nodes in a grid, the \c Edge class represents the lines
+    between the nodes, the \c GraphWidget class represents the application
+    window, and the \c main() function creates and shows this window, and runs
+    the event loop.
+
+    \section1 Node Class Definition
+
+    The \c Node class serves three purposes:
+
+    \list
+    \o Painting a yellow gradient "ball" in two states: sunken and raised.
+    \o Managing connections to other nodes.
+    \o Calculating forces pulling and pushing the nodes in the grid.
+    \endlist
+
+    Let's start by looking at the \c Node class declaration.
+
+    \snippet examples/graphicsview/elasticnodes/node.h 0
+
+    The \c Node class inherits QGraphicsItem, and reimplements the two
+    mandatory functions \l{QGraphicsItem::boundingRect()}{boundingRect()} and
+    \l{QGraphicsItem::paint()}{paint()} to provide its visual appearance. It
+    also reimplements \l{QGraphicsItem::shape()}{shape()} to ensure its hit
+    area has an elliptic shape (as opposed to the default bounding rectangle).
+
+    For edge management purposes the node provides a simple API for adding
+    edges to a node, and for listing all connected edges.
+
+    The \l{QGraphicsItem::advance()}{advance()} reimplementation is called
+    whenever the scene's state advances by one step. The calculateForces()
+    function is called to calculate the forces that push and pull on this node
+    and its neighbors.
+
+   The \c Node class also reimplements
+   \l{QGraphicsItem::itemChange()}{itemChange()} to react to state changes (in
+   this case, position changes), and
+   \l{QGraphicsItem::mousePressEvent()}{mousePressEvent()} and
+   \l{QGraphicsItem::mouseReleaseEvent()}{mouseReleaseEvent()} to update the
+   item's visual appearance.
+
+    We will start reviewing the \c Node implementation by looking at its
+    constructor:
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 0
+
+    In the constructor, we set the
+    \l{QGraphicsItem::ItemIsMovable}{ItemIsMovable} flag to allow the item to
+    move in response to mouse dragging, and
+    \l{QGraphicsItem::ItemSendsGeometryChanges}{ItemSendsGeometryChanges} to
+    enable \l{QGraphicsItem::itemChange()}{itemChange()} notifications for
+    position and transformation changes. We also enable
+    \l{QGraphicsItem::DeviceCoordinateCache}{DeviceCoordinateCache} to speed up
+    rendering performance. To ensure that the nodes are always stacked on top
+    of edges, we finally set the item's Z value to -1.
+
+    \c Node's constructor takes a \c GraphWidget pointer and stores this as a
+    member variable. We will revisit this pointer later on.
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 1
+
+    The addEdge() function adds the input edge to a list of attached edges. The
+    edge is then adjusted so that the end points for the edge match the
+    positions of the source and destination nodes.
+
+    The edges() function simply returns the list of attached edges.
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 2
+
+    The \e calculateForces() function implements the elastic forces effect that
+    pulls and pushes on nodes in the grid. In addition to this algorithm, the
+    user can move one node around with the mouse. Because we do not want the
+    two to interfere, we start by checking if this \c Node is the current mouse
+    grabber item (i.e., QGraphicsScene::mouseGrabberItem()). Because we need to
+    find all neighboring (but not necessarily connected) nodes, we also make
+    sure the item is part of a scene in the first place.
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 3
+
+    The algorithm has two steps: the first is to calculate the forces that push
+    the nodes apart, and the second is to subtract the forces that pull the
+    nodes together. First we need to find all the nodes in the graph. We call
+    QGraphicsScene::items() to find all items in the scene, and then use
+    qgraphicsitem_cast() to look for \c Node instances.
+
+    We make use of \l{QGraphicsItem::mapFromItem()}{mapFromItem()} to create a
+    vector pointing from this node to each other node, in \l{The Graphics View
+    Coordinate System}{local coordinates}. We use the decomposed components of
+    this vector to determine the direction and strength of force that apply to
+    the node. The forces are added up for each node, and weighted so that the
+    closest nodes are given the strongest force. The sum of all forces are
+    stored in \e xvel (X-velocity) and \e yvel (Y-velocity).
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 4
+
+    The edges between the nodes represent the forces that pull the nodes
+    together. By visiting each edge that is connected to this node, we can use
+    a similar approach as above to find the direction and strength of all
+    forces. These forces are subtracted from \e xvel and \e yvel.
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 5
+
+    In theory, the sum of pushing and pulling forces should stabilize to
+    precisely 0. In practise, however, they never do. To circumvent errors in
+    numerical precision, we simply force the sum of forces to be 0 when they
+    are less than 0.1.
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 6
+
+    The final step of \e calculateForces() determines the node's new position.
+    We add the force to the node's current position. We also make sure the new
+    position stays inside of our defined boundaries. We don't actually move the
+    item in this function; that's done in a separate step, from \e advance().
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 7
+
+    The \e advance() function updates the item's current position. It is called
+    from \e GraphWidget::timerEvent(). If the node's position changed, the
+    function returns true; otherwise false is returned.
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 8
+
+    The \e Node's bounding rectangle is a 20x20 sized rectangle centered around
+    its origin (0, 0), adjusted by 2 units in all directions to compensate for
+    the node's outline stroke, and by 3 units down and to the right to make
+    room for a simple drop shadow.
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 9
+
+    The shape is a simple ellipse. This ensures that you must click inside the
+    node's elliptic shape in order to drag it around. You can test this effect
+    by running the example, and zooming far enough in so that the nodes become
+    very large. Without reimplementing \l{QGraphicsItem::shape()}{shape()}, the
+    item's hit area would be identical to its bounding rectangle (i.e.,
+    rectangular).
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 10
+
+    This function implements the node's painting. We start by drawing a simple
+    dark gray elliptic drop shadow at (-7, -7), that is, (3, 3) units down and
+    to the right.
+
+    We then draw an ellipse with a radial gradient fill. This fill is either
+    Qt::yellow to Qt::darkYellow when raised, or the opposite when sunken. In
+    sunken state we also shift the center and focal point by (3, 3) to
+    emphasize the impression that something has been pushed down.
+
+    Drawing filled ellipses with gradients can be quite slow, especially when
+    using complex gradients such as QRadialGradient. This is why this example
+    uses \l{QGraphicsItem::DeviceCoordinateCache}{DeviceCoordinateCache}, a
+    simple yet effective measure that prevents unnecessary redrawing.
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 11
+
+    We reimplement \l{QGraphicsItem::itemChange()}{itemChange()} to adjust the
+    position of all connected edges, and to notify the scene that an item has
+    moved (i.e., "something has happened"). This will trigger new force
+    calculations.
+
+    This notification is the only reason why the nodes need to keep a pointer
+    back to the \e GraphWidget. Another approach could be to provide such
+    notification using a signal; in such case, \e Node would need to inherit
+    from QGraphicsObject.
+
+    \snippet examples/graphicsview/elasticnodes/node.cpp 12
+
+    Because we have set the \l{QGraphicsItem::ItemIsMovable}{ItemIsMovable}
+    flag, we don't need to implement the logic that moves the node according to
+    mouse input; this is already provided for us. We still need to reimplement
+    the mouse press and release handlers though, to update the nodes' visual
+    appearance (i.e., sunken or raised).
+
+    \section1 Edge Class Definition
+
+    The \e Edge class represents the arrow-lines between the nodes in this
+    example. The class is very simple: it maintains a source- and destination
+    node pointer, and provides an \e adjust() function that makes sure the line
+    starts at the position of the source, and ends at the position of the
+    destination. The edges are the only items that change continuously as
+    forces pull and push on the nodes.
+
+    Let's take a look at the class declaration:
+
+    \snippet examples/graphicsview/elasticnodes/edge.h 0
+
+    \e Edge inherits from QGraphicsItem, as it's a simple class that has no use
+    for signals, slots, and properties (compare to QGraphicsObject).
+
+    The constructor takes two node pointers as input. Both pointers are
+    mandatory in this example. We also provide get-functions for each node.
+
+    The \e adjust() function repositions the edge, and the item also implements
+    \l{QGraphicsItem::boundingRect()}{boundingRect()} and
+    \{QGraphicsItem::paint()}{paint()}.
+
+    We will now review its implementation.
+
+    \snippet examples/graphicsview/elasticnodes/edge.cpp 0
+
+    The \e Edge constructor initializes its arrowSize data member to 10 units;
+    this determines the size of the arrow which is drawn in
+    \l{QGraphicsItem::paint()}{paint()}.
+
+    In the constructor body, we call
+    \l{QGraphicsItem::setAcceptedMouseButtons()}{setAcceptedMouseButtons(0)}.
+    This ensures that the edge items are not considered for mouse input at all
+    (i.e., you cannot click the edges). Then, the source and destination
+    pointers are updated, this edge is registered with each node, and we call
+    \e adjust() to update this edge's start end end position.
+
+    \snippet examples/graphicsview/elasticnodes/edge.cpp 1
+
+    The source and destination get-functions simply return the respective
+    pointers.
+
+    \snippet examples/graphicsview/elasticnodes/edge.cpp 2
+
+    In \e adjust(), we define two points: \e sourcePoint, and \e destPoint,
+    pointing at the source and destination nodes' origins respectively. Each
+    point is calculated using \l{The Graphics View Coordinate System}{local
+    coordinates}.
+
+    We want the tip of the edge's arrows to point to the exact outline of the
+    nodes, as opposed to the center of the nodes. To find this point, we first
+    decompose the vector pointing from the center of the source to the center
+    of the destination node into X and Y, and then normalize the components by
+    dividing by the length of the vector. This gives us an X and Y unit delta
+    that, when multiplied by the radius of the node (which is 10), gives us the
+    offset that must be added to one point of the edge, and subtracted from the
+    other.
+
+    If the length of the vector is less than 20 (i.e., if two nodes overlap),
+    then we fix the source and destination pointer at the center of the source
+    node. In practise this case is very hard to reproduce manually, as the
+    forces between the two nodes is then at its maximum.
+
+    It's important to notice that we call
+    \l{QGraphicsItem::prepareGeometryChange()}{prepareGeometryChange()} in this
+    function. The reason is that the variables \e sourcePoint and \e destPoint
+    are used directly when painting, and they are returned from the
+    \l{QGraphicsItem::boundingRect()}{boundingRect()} reimplementation. We must
+    always call
+    \l{QGraphicsItem::prepareGeometryChange()}{prepareGeometryChange()} before
+    changing what \l{QGraphicsItem::boundingRect()}{boundingRect()} returns,
+    and before these variables can be used by
+    \l{QGraphicsItem::paint()}{paint()}, to keep Graphics View's internal
+    bookkeeping clean. It's safest to call this function once, immediately
+    before any such variable is modified.
+
+    \snippet examples/graphicsview/elasticnodes/edge.cpp 3
+
+    The edge's bounding rectangle is defined as the smallest rectangle that
+    includes both the start and the end point of the edge. Because we draw an
+    arrow on each edge, we also need to compensate by adjusting with half the
+    arrow size and half the pen width in all directions. The pen is used to
+    draw the outline of the arrow, and we can assume that half of the outline
+    can be drawn outside of the arrow's area, and half will be drawn inside.
+
+    \snippet examples/graphicsview/elasticnodes/edge.cpp 4
+
+    We start the reimplementation of \l{QGraphicsItem::paint()}{paint()} by
+    checking a few preconditions. Firstly, if either the source or destination
+    node is not set, then we return immediately; there is nothing to draw.
+
+    At the same time, we check if the length of the edge is approximately 0,
+    and if it is, then we also return.
+
+    \snippet examples/graphicsview/elasticnodes/edge.cpp 5
+
+    We draw the line using a pen that has round joins and caps. If you run the
+    example, zoom in and study the edge in detail, you will see that there are
+    no sharp/square edges.
+
+    \snippet examples/graphicsview/elasticnodes/edge.cpp 6
+
+    We proceed to drawing one arrow at each end of the edge. Each arrow is
+    drawn as a polygon with a black fill. The coordinates for the arrow are
+    determined using simple trigonometry.
+
+    \section1 GraphWidget Class Definition
+
+    \e GraphWidget is a subclass of QGraphicsView, which provides the main
+    window with scrollbars.
+
+    \snippet examples/graphicsview/elasticnodes/graphwidget.h 0
+
+    It provides a basic constructor that initializes the scene, an \e
+    itemMoved() function to notify changes in the scene's node graph, a few
+    event handlers, a reimplementation of
+    \l{QGraphicsView::drawBackground()}{drawBackground()}, and a helper
+    function for scaling the view by mouse or keyboard.
+
+    \snippet examples/graphicsview/elasticnodes/graphwidget.cpp 0
+
+    \e GraphicsWidget's constructor creates the scene, and because most items
+    move around most of the time, it sets QGraphicsScene::NoIndex. Then the
+    scene gets a fixed \l{QGraphicsScene::sceneRect}{scene rectangle}.
+    The scene is then assigned to the \e GraphWidget view.
+
+    The view enables QGraphicsView::CacheBackground to cache rendering of its
+    static and somewhat complex background. Because the graph renders a close
+    collection of small items that all move around, it's unnecessary for
+    Graphics View to waste time finding accurate update regions, so we set the
+    QGraphicsView::BoundingRectViewportUpdate viewport update mode. The default
+    would work fine, but this mode is noticably faster for this example.
+
+    To improve rendering quality, we set QPainter::Antialiasing.
+
+    The transformation anchor decides how the view should scroll when you
+    transform the view, or in our case, when we zoom in or out. We have chosen
+    QGraphicsView::AnchorUnderMouse, which centers the view on the point under
+    the mouse cursor. This makes it easy to zoom towards a point in the scene
+    by moving the mouse over it, and then rolling the mouse wheel.
+
+    Finally we give the window a minimum size that matches the scene's default
+    size, and set a suitable window title.
+
+    \snippet examples/graphicsview/elasticnodes/graphwidget.cpp 1
+
+    The last part of the constructor creates the grid of nodes and edges, and
+    gives each node an initial position.
+
+    \snippet examples/graphicsview/elasticnodes/graphwidget.cpp 2
+
+    \e GraphWidget is notified of node movement through this \e itemMoved()
+    function. Its job is simply to restart the main timer in case it's not
+    running already. The timer is designed to stop when the graph stabilizes,
+    and start once it's unstable again.
+
+    \snippet examples/graphicsview/elasticnodes/graphwidget.cpp 3
+
+    This is \e GraphWidget's key event handler. The arrow keys move the center
+    node around, the '+' and '-' keys zoom in and out by calling \e
+    scaleView(), and the enter and space keys randomize the positions of the
+    nodes. All other key events (e.g., page up and page down) are handled by
+    QGraphicsView's default implementation.
+
+    \snippet examples/graphicsview/elasticnodes/graphwidget.cpp 4
+
+    The timer event handler's job is to run the whole force calculation
+    machinery as a smooth animation. Each time the timer is triggered, the
+    handler will find all nodes in the scene, and call \e
+    Node::calculateForces() on each node, one at a time. Then, in a final step
+    it will call \e Node::advance() to move all nodes to their new positions.
+    By checking the return value of \e advance(), we can decide if the grid
+    stabilized (i.e., no nodes moved). If so, we can stop the timer.
+
+    \snippet examples/graphicsview/elasticnodes/graphwidget.cpp 5
+
+    In the wheel event handler, we convert the mouse wheel delta to a scale
+    factor, and pass this factor to \e scaleView(). This approach takes into
+    account the speed that the wheel is rolled. The faster you roll the mouse
+    wheel, the faster the view will zoom.
+
+    \snippet examples/graphicsview/elasticnodes/graphwidget.cpp 6
+
+    The view's background is rendered in a reimplementation of
+    QGraphicsView::drawBackground(). We draw a large rectangle filled with a
+    linear gradient, with a drop shadow, and then render text in top. The text
+    is rendered twice to give a similar simple drop-shadow effect.
+
+    This background rendering is quite expensive; this is why the view enables
+    QGraphicsView::CacheBackground.
+
+    \snippet examples/graphicsview/elasticnodes/graphwidget.cpp 7
+
+    The \e scaleView() helper function checks that the scale factor stays
+    within certain limits (i.e., you cannot zoom too far in nor too far out),
+    and then applies this scale.
+
+    \section1 The main() Function
+
+    In contrast to the complexity of the rest of this example, the \e main()
+    function is very simple: We create a QApplication instance, seed the
+    randomizer using qsrand(), and then create and show an instance of \e
+    GraphWidget. Because all nodes in the grid are moved initially, the \e
+    GraphWidget timer will start immediately after control has returned to the
+    event loop.
 */
diff --git a/doc/src/getting-started/examples.qdoc b/doc/src/getting-started/examples.qdoc
index c96c70f..542f672 100644
--- a/doc/src/getting-started/examples.qdoc
+++ b/doc/src/getting-started/examples.qdoc
@@ -650,7 +650,7 @@
     \o \l{graphicsview/collidingmice}{Colliding Mice}\raisedaster
     \o \l{graphicsview/diagramscene}{Diagram Scene}\raisedaster
     \o \l{graphicsview/dragdroprobot}{Drag and Drop Robot}\raisedaster
-    \o \l{graphicsview/elasticnodes}{Elastic Nodes}
+    \o \l{graphicsview/elasticnodes}{Elastic Nodes}\raisedaster
     \o \l{graphicsview/portedasteroids}{Ported Asteroids}
     \o \l{graphicsview/portedcanvas}{Ported Canvas}
     \endlist
diff --git a/examples/graphicsview/elasticnodes/edge.cpp b/examples/graphicsview/elasticnodes/edge.cpp
index 25c769b..634fbee 100644
--- a/examples/graphicsview/elasticnodes/edge.cpp
+++ b/examples/graphicsview/elasticnodes/edge.cpp
@@ -49,6 +49,7 @@
 static const double Pi = 3.14159265358979323846264338327950288419717;
 static double TwoPi = 2.0 * Pi;
 
+//! [0]
 Edge::Edge(Node *sourceNode, Node *destNode)
     : arrowSize(10)
 {
@@ -59,33 +60,21 @@ Edge::Edge(Node *sourceNode, Node *destNode)
     dest->addEdge(this);
     adjust();
 }
+//! [0]
 
-Edge::~Edge()
-{
-}
-
+//! [1]
 Node *Edge::sourceNode() const
 {
     return source;
 }
 
-void Edge::setSourceNode(Node *node)
-{
-    source = node;
-    adjust();
-}
-
 Node *Edge::destNode() const
 {
     return dest;
 }
+//! [1]
 
-void Edge::setDestNode(Node *node)
-{
-    dest = node;
-    adjust();
-}
-
+//! [2]
 void Edge::adjust()
 {
     if (!source || !dest)
@@ -104,7 +93,9 @@ void Edge::adjust()
         sourcePoint = destPoint = line.p1();
     }
 }
+//! [2]
 
+//! [3]
 QRectF Edge::boundingRect() const
 {
     if (!source || !dest)
@@ -118,7 +109,9 @@ QRectF Edge::boundingRect() const
         .normalized()
         .adjusted(-extra, -extra, extra, extra);
 }
+//! [3]
 
+//! [4]
 void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
 {
     if (!source || !dest)
@@ -127,11 +120,15 @@ void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
     QLineF line(sourcePoint, destPoint);
     if (qFuzzyCompare(line.length(), qreal(0.)))
         return;
+//! [4]
 
+//! [5]
     // Draw the line itself
     painter->setPen(QPen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
     painter->drawLine(line);
+//! [5]
 
+//! [6]
     // Draw the arrows
     double angle = ::acos(line.dx() / line.length());
     if (line.dy() >= 0)
@@ -150,3 +147,4 @@ void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
     painter->drawPolygon(QPolygonF() << line.p1() << sourceArrowP1 << sourceArrowP2);
     painter->drawPolygon(QPolygonF() << line.p2() << destArrowP1 << destArrowP2);        
 }
+//! [6]
diff --git a/examples/graphicsview/elasticnodes/edge.h b/examples/graphicsview/elasticnodes/edge.h
index b6b951f..58cc89c 100644
--- a/examples/graphicsview/elasticnodes/edge.h
+++ b/examples/graphicsview/elasticnodes/edge.h
@@ -46,17 +46,14 @@
 
 class Node;
 
+//! [0]
 class Edge : public QGraphicsItem
 {
 public:
     Edge(Node *sourceNode, Node *destNode);
-    ~Edge();
 
     Node *sourceNode() const;
-    void setSourceNode(Node *node);
-
     Node *destNode() const;
-    void setDestNode(Node *node);
 
     void adjust();
 
@@ -74,5 +71,6 @@ private:
     QPointF destPoint;
     qreal arrowSize;
 };
+//! [0]
 
 #endif
diff --git a/examples/graphicsview/elasticnodes/graphwidget.cpp b/examples/graphicsview/elasticnodes/graphwidget.cpp
index 7c244be..8b419b8 100644
--- a/examples/graphicsview/elasticnodes/graphwidget.cpp
+++ b/examples/graphicsview/elasticnodes/graphwidget.cpp
@@ -43,14 +43,13 @@
 #include "edge.h"
 #include "node.h"
 
-#include <QDebug>
-#include <QGraphicsScene>
-#include <QWheelEvent>
+#include <QtGui>
 
 #include <math.h>
 
-GraphWidget::GraphWidget()
-    : timerId(0)
+//! [0]
+GraphWidget::GraphWidget(QWidget *parent)
+    : QGraphicsView(parent), timerId(0)
 {
     QGraphicsScene *scene = new QGraphicsScene(this);
     scene->setItemIndexMethod(QGraphicsScene::NoIndex);
@@ -60,8 +59,12 @@ GraphWidget::GraphWidget()
     setViewportUpdateMode(BoundingRectViewportUpdate);
     setRenderHint(QPainter::Antialiasing);
     setTransformationAnchor(AnchorUnderMouse);
-    setResizeAnchor(AnchorViewCenter);
+    scale(qreal(0.8), qreal(0.8));
+    setMinimumSize(400, 400);
+    setWindowTitle(tr("Elastic Nodes"));
+//! [0]
 
+//! [1]
     Node *node1 = new Node(this);
     Node *node2 = new Node(this);
     Node *node3 = new Node(this);
@@ -102,18 +105,18 @@ GraphWidget::GraphWidget()
     node7->setPos(-50, 50);
     node8->setPos(0, 50);
     node9->setPos(50, 50);
-
-    scale(qreal(0.8), qreal(0.8));
-    setMinimumSize(400, 400);
-    setWindowTitle(tr("Elastic Nodes"));
 }
+//! [1]
 
+//! [2]
 void GraphWidget::itemMoved()
 {
     if (!timerId)
         timerId = startTimer(1000 / 25);
 }
+//! [2]
 
+//! [3]
 void GraphWidget::keyPressEvent(QKeyEvent *event)
 {
     switch (event->key()) {
@@ -146,7 +149,9 @@ void GraphWidget::keyPressEvent(QKeyEvent *event)
         QGraphicsView::keyPressEvent(event);
     }
 }
+//! [3]
 
+//! [4]
 void GraphWidget::timerEvent(QTimerEvent *event)
 {
     Q_UNUSED(event);
@@ -171,12 +176,16 @@ void GraphWidget::timerEvent(QTimerEvent *event)
         timerId = 0;
     }
 }
+//! [4]
 
+//! [5]
 void GraphWidget::wheelEvent(QWheelEvent *event)
 {
     scaleView(pow((double)2, -event->delta() / 240.0));
 }
+//! [5]
 
+//! [6]
 void GraphWidget::drawBackground(QPainter *painter, const QRectF &rect)
 {
     Q_UNUSED(rect);
@@ -213,12 +222,15 @@ void GraphWidget::drawBackground(QPainter *painter, const QRectF &rect)
     painter->setPen(Qt::black);
     painter->drawText(textRect, message);
 }
+//! [6]
 
+//! [7]
 void GraphWidget::scaleView(qreal scaleFactor)
 {
-    qreal factor = matrix().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
+    qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
     if (factor < 0.07 || factor > 100)
         return;
 
     scale(scaleFactor, scaleFactor);
 }
+//! [7]
diff --git a/examples/graphicsview/elasticnodes/graphwidget.h b/examples/graphicsview/elasticnodes/graphwidget.h
index 6f67b4e..3f78a5f 100644
--- a/examples/graphicsview/elasticnodes/graphwidget.h
+++ b/examples/graphicsview/elasticnodes/graphwidget.h
@@ -46,12 +46,13 @@
 
 class Node;
 
+//! [0]
 class GraphWidget : public QGraphicsView
 {
     Q_OBJECT
 
 public:
-    GraphWidget();
+    GraphWidget(QWidget *parent = 0);
 
     void itemMoved();
 
@@ -67,5 +68,6 @@ private:
     int timerId;
     Node *centerNode;
 };
+//! [0]
 
 #endif
diff --git a/examples/graphicsview/elasticnodes/node.cpp b/examples/graphicsview/elasticnodes/node.cpp
index 774046c..495aa89 100644
--- a/examples/graphicsview/elasticnodes/node.cpp
+++ b/examples/graphicsview/elasticnodes/node.cpp
@@ -48,6 +48,7 @@
 #include "node.h"
 #include "graphwidget.h"
 
+//! [0]
 Node::Node(GraphWidget *graphWidget)
     : graph(graphWidget)
 {
@@ -56,7 +57,9 @@ Node::Node(GraphWidget *graphWidget)
     setCacheMode(DeviceCoordinateCache);
     setZValue(-1);
 }
+//! [0]
 
+//! [1]
 void Node::addEdge(Edge *edge)
 {
     edgeList << edge;
@@ -67,14 +70,18 @@ QList<Edge *> Node::edges() const
 {
     return edgeList;
 }
+//! [1]
 
+//! [2]
 void Node::calculateForces()
 {
     if (!scene() || scene()->mouseGrabberItem() == this) {
         newPos = pos();
         return;
     }
-    
+//! [2]
+
+//! [3]
     // Sum up all forces pushing this item away
     qreal xvel = 0;
     qreal yvel = 0;
@@ -83,37 +90,45 @@ void Node::calculateForces()
         if (!node)
             continue;
 
-        QLineF line(mapFromItem(node, 0, 0), QPointF(0, 0));
-        qreal dx = line.dx();
-        qreal dy = line.dy();
+        QPointF vec = mapToItem(node, 0, 0);
+        qreal dx = vec.x();
+        qreal dy = vec.y();
         double l = 2.0 * (dx * dx + dy * dy);
         if (l > 0) {
             xvel += (dx * 150.0) / l;
             yvel += (dy * 150.0) / l;
         }
     }
+//! [3]
 
+//! [4]
     // Now subtract all forces pulling items together
     double weight = (edgeList.size() + 1) * 10;
     foreach (Edge *edge, edgeList) {
-        QPointF pos;
+        QPointF vec;
         if (edge->sourceNode() == this)
-            pos = mapFromItem(edge->destNode(), 0, 0);
+            vec = mapToItem(edge->destNode(), 0, 0);
         else
-            pos = mapFromItem(edge->sourceNode(), 0, 0);
-        xvel += pos.x() / weight;
-        yvel += pos.y() / weight;
+            vec = mapToItem(edge->sourceNode(), 0, 0);
+        xvel -= vec.x() / weight;
+        yvel -= vec.y() / weight;
     }
-    
+//! [4]
+
+//! [5]
     if (qAbs(xvel) < 0.1 && qAbs(yvel) < 0.1)
         xvel = yvel = 0;
+//! [5]
 
+//! [6]
     QRectF sceneRect = scene()->sceneRect();
     newPos = pos() + QPointF(xvel, yvel);
     newPos.setX(qMin(qMax(newPos.x(), sceneRect.left() + 10), sceneRect.right() - 10));
     newPos.setY(qMin(qMax(newPos.y(), sceneRect.top() + 10), sceneRect.bottom() - 10));
 }
+//! [6]
 
+//! [7]
 bool Node::advance()
 {
     if (newPos == pos())
@@ -122,21 +137,27 @@ bool Node::advance()
     setPos(newPos);
     return true;
 }
+//! [7]
 
+//! [8]
 QRectF Node::boundingRect() const
 {
     qreal adjust = 2;
     return QRectF(-10 - adjust, -10 - adjust,
                   23 + adjust, 23 + adjust);
 }
+//! [8]
 
+//! [9]
 QPainterPath Node::shape() const
 {
     QPainterPath path;
     path.addEllipse(-10, -10, 20, 20);
     return path;
 }
+//! [9]
 
+//! [10]
 void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
 {
     painter->setPen(Qt::NoPen);
@@ -157,7 +178,9 @@ void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWid
     painter->setPen(QPen(Qt::black, 0));
     painter->drawEllipse(-10, -10, 20, 20);
 }
+//! [10]
 
+//! [11]
 QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value)
 {
     switch (change) {
@@ -172,7 +195,9 @@ QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value)
 
     return QGraphicsItem::itemChange(change, value);
 }
+//! [11]
 
+//! [12]
 void Node::mousePressEvent(QGraphicsSceneMouseEvent *event)
 {
     update();
@@ -184,3 +209,4 @@ void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
     update();
     QGraphicsItem::mouseReleaseEvent(event);
 }
+//! [12]
diff --git a/examples/graphicsview/elasticnodes/node.h b/examples/graphicsview/elasticnodes/node.h
index 0df579d..990346e 100644
--- a/examples/graphicsview/elasticnodes/node.h
+++ b/examples/graphicsview/elasticnodes/node.h
@@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE
 class QGraphicsSceneMouseEvent;
 QT_END_NAMESPACE
 
+//! [0]
 class Node : public QGraphicsItem
 {
 public:
@@ -80,5 +81,6 @@ private:
     QPointF newPos;
     GraphWidget *graph;
 };
+//! [0]
 
 #endif
-- 
cgit v0.12