1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of The Qt Company Ltd nor the names of its
21 **     contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "stickman.h"
42 #include "node.h"
43 
44 #include <QPainter>
45 #include <QTimer>
46 
47 #define _USE_MATH_DEFINES
48 #include <math.h>
49 
50 #ifndef M_PI
51 #define M_PI 3.14159265358979323846
52 #endif
53 
54 static const qreal Coords[NodeCount * 2] = {
55     0.0, -150.0, // head, #0
56 
57     0.0, -100.0, // body pentagon, top->bottom, left->right, #1 - 5
58     -50.0, -50.0,
59     50.0, -50.0,
60     -25.0, 50.0,
61     25.0, 50.0,
62 
63     -100.0, 0.0, // right arm, #6 - 7
64     -125.0, 50.0,
65 
66     100.0, 0.0, // left arm, #8 - 9
67     125.0, 50.0,
68 
69     -35.0, 75.0, // lower body, #10 - 11
70     35.0, 75.0,
71 
72     -25.0, 200.0, // right leg, #12 - 13
73     -30.0, 300.0,
74 
75     25.0, 200.0, // left leg, #14 - 15
76     30.0, 300.0
77 
78 };
79 
80 static const int Bones[BoneCount * 2] = {
81     0, 1, // neck
82 
83     1, 2, // body
84     1, 3,
85     1, 4,
86     1, 5,
87     2, 3,
88     2, 4,
89     2, 5,
90     3, 4,
91     3, 5,
92     4, 5,
93 
94     2, 6, // right arm
95     6, 7,
96 
97     3, 8, // left arm
98     8, 9,
99 
100     4, 10, // lower body
101     4, 11,
102     5, 10,
103     5, 11,
104     10, 11,
105 
106     10, 12, // right leg
107     12, 13,
108 
109     11, 14, // left leg
110     14, 15
111 
112 };
113 
StickMan()114 StickMan::StickMan()
115 {
116     m_sticks = true;
117     m_isDead = false;
118     m_pixmap = QPixmap("images/head.png");
119     m_penColor = Qt::white;
120     m_fillColor = Qt::black;
121 
122     // Set up start position of limbs
123     for (int i=0; i<NodeCount; ++i) {
124         m_nodes[i] = new Node(QPointF(Coords[i * 2], Coords[i * 2 + 1]), this);
125         connect(m_nodes[i], SIGNAL(positionChanged()), this, SLOT(childPositionChanged()));
126     }
127 
128     for (int i=0; i<BoneCount; ++i) {
129         int n1 = Bones[i * 2];
130         int n2 = Bones[i * 2 + 1];
131 
132         Node *node1 = m_nodes[n1];
133         Node *node2 = m_nodes[n2];
134 
135         QPointF dist = node1->pos() - node2->pos();
136         m_perfectBoneLengths[i] = sqrt(pow(dist.x(),2) + pow(dist.y(),2));
137     }
138 
139     startTimer(10);
140 }
141 
~StickMan()142 StickMan::~StickMan()
143 {
144 }
145 
childPositionChanged()146 void StickMan::childPositionChanged()
147 {
148     prepareGeometryChange();
149 }
150 
setDrawSticks(bool on)151 void StickMan::setDrawSticks(bool on)
152 {
153     m_sticks = on;
154     for (int i=0;i<nodeCount();++i) {
155         Node *node = m_nodes[i];
156         node->setVisible(on);
157     }
158 }
159 
boundingRect() const160 QRectF StickMan::boundingRect() const
161 {
162     // account for head radius=50.0 plus pen which is 5.0
163     return childrenBoundingRect().adjusted(-55.0, -55.0, 55.0, 55.0);
164 }
165 
nodeCount() const166 int StickMan::nodeCount() const
167 {
168     return NodeCount;
169 }
170 
node(int idx) const171 Node *StickMan::node(int idx) const
172 {
173     if (idx >= 0 && idx < NodeCount)
174         return m_nodes[idx];
175     else
176         return 0;
177 }
178 
timerEvent(QTimerEvent *)179 void StickMan::timerEvent(QTimerEvent *)
180 {
181     update();
182 }
183 
stabilize()184 void StickMan::stabilize()
185 {
186     static const qreal threshold = 0.001;
187 
188     for (int i=0; i<BoneCount; ++i) {
189         int n1 = Bones[i * 2];
190         int n2 = Bones[i * 2 + 1];
191 
192         Node *node1 = m_nodes[n1];
193         Node *node2 = m_nodes[n2];
194 
195         QPointF pos1 = node1->pos();
196         QPointF pos2 = node2->pos();
197 
198         QPointF dist = pos1 - pos2;
199         qreal length = sqrt(pow(dist.x(),2) + pow(dist.y(),2));
200         qreal diff = (length - m_perfectBoneLengths[i]) / length;
201 
202         QPointF p = dist * (0.5 * diff);
203         if (p.x() > threshold && p.y() > threshold) {
204             pos1 -= p;
205             pos2 += p;
206 
207             node1->setPos(pos1);
208             node2->setPos(pos2);
209         }
210     }
211 }
212 
posFor(int idx) const213 QPointF StickMan::posFor(int idx) const
214 {
215     return m_nodes[idx]->pos();
216 }
217 
218 //#include <QTime>
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)219 void StickMan::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
220 {
221   /*  static int frames = 0;
222     static QTime time;
223     if (frames++ % 100 == 0) {
224         frames = 1;
225         time.restart();
226     }
227 
228     if (time.elapsed() > 0) {
229         painter->setPen(Qt::white);
230         painter->drawText(0, 0, QString::number(frames / (time.elapsed() / 1000.0)));
231     }*/
232 
233     stabilize();
234     if (m_sticks) {
235         painter->setPen(Qt::white);
236         for (int i=0; i<BoneCount; ++i) {
237             int n1 = Bones[i * 2];
238             int n2 = Bones[i * 2 + 1];
239 
240             Node *node1 = m_nodes[n1];
241             Node *node2 = m_nodes[n2];
242 
243             painter->drawLine(node1->pos(), node2->pos());
244         }
245     } else {
246         // first bone is neck and will be used for head
247 
248         QPainterPath path;
249         path.moveTo(posFor(0));
250         path.lineTo(posFor(1));
251 
252         // right arm
253         path.lineTo(posFor(2));
254         path.lineTo(posFor(6));
255         path.lineTo(posFor(7));
256 
257         // left arm
258         path.moveTo(posFor(3));
259         path.lineTo(posFor(8));
260         path.lineTo(posFor(9));
261 
262         // body
263         path.moveTo(posFor(2));
264         path.lineTo(posFor(4));
265         path.lineTo(posFor(10));
266         path.lineTo(posFor(11));
267         path.lineTo(posFor(5));
268         path.lineTo(posFor(3));
269         path.lineTo(posFor(1));
270 
271         // right leg
272         path.moveTo(posFor(10));
273         path.lineTo(posFor(12));
274         path.lineTo(posFor(13));
275 
276         // left leg
277         path.moveTo(posFor(11));
278         path.lineTo(posFor(14));
279         path.lineTo(posFor(15));
280 
281         painter->setPen(QPen(m_penColor, 5.0, Qt::SolidLine, Qt::RoundCap));
282         painter->drawPath(path);
283 
284         {
285             int n1 = Bones[0];
286             int n2 = Bones[1];
287             Node *node1 = m_nodes[n1];
288             Node *node2 = m_nodes[n2];
289 
290             QPointF dist = node2->pos() - node1->pos();
291 
292             qreal sinAngle = dist.x() / sqrt(pow(dist.x(), 2) + pow(dist.y(), 2));
293             qreal angle = asin(sinAngle) * 180.0 / M_PI;
294 
295             QPointF headPos = node1->pos();
296             painter->translate(headPos);
297             painter->rotate(-angle);
298 
299             painter->setBrush(m_fillColor);
300             painter->drawEllipse(QPointF(0,0), 50.0, 50.0);
301 
302             painter->setBrush(m_penColor);
303             painter->setPen(QPen(m_penColor, 2.5, Qt::SolidLine, Qt::RoundCap));
304 
305             // eyes
306             if (m_isDead) {
307                 painter->drawLine(-30.0, -30.0, -20.0, -20.0);
308                 painter->drawLine(-20.0, -30.0, -30.0, -20.0);
309 
310                 painter->drawLine(20.0, -30.0, 30.0, -20.0);
311                 painter->drawLine(30.0, -30.0, 20.0, -20.0);
312             } else {
313                 painter->drawChord(QRectF(-30.0, -30.0, 25.0, 70.0), 30.0*16, 120.0*16);
314                 painter->drawChord(QRectF(5.0, -30.0, 25.0, 70.0), 30.0*16, 120.0*16);
315             }
316 
317             // mouth
318             if (m_isDead) {
319                 painter->drawLine(-28.0, 2.0, 29.0, 2.0);
320             } else {
321                 painter->setBrush(QColor(128, 0, 64 ));
322                 painter->drawChord(QRectF(-28.0, 2.0-55.0/2.0, 57.0, 55.0), 0.0, -180.0*16);
323             }
324 
325             // pupils
326             if (!m_isDead) {
327                 painter->setPen(QPen(m_fillColor, 1.0, Qt::SolidLine, Qt::RoundCap));
328                 painter->setBrush(m_fillColor);
329                 painter->drawEllipse(QPointF(-12.0, -25.0), 5.0, 5.0);
330                 painter->drawEllipse(QPointF(22.0, -25.0), 5.0, 5.0);
331             }
332         }
333     }
334 }
335 
336 
337 
338