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