push 0f6a7ee30aa1574a9ae1569d743dbd53d0ff58b0
[tagua/yd.git] / src / sprite.cpp
blob4a6449e8ecbe04db218ba2fb1313e4dd7301449f
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@gmail.com>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 */
11 #include <QImage>
12 #include <QPainter>
13 #include <KDebug>
14 #include <iostream>
15 #include <cmath>
16 #include <boost/random/uniform_smallint.hpp>
17 #include <vector>
18 #include "common.h"
19 #include <core/point.h>
20 #include "sprite.h"
23 using namespace boost;
25 struct ExplosionFragment {
26 QPoint m_polygon[6];
27 int m_pcount;
28 QPoint m_speed;
29 ExplosionFragment() : m_pcount(0) {}
32 /* inherit instead of typedef to ease forward declaration :) */
33 class SpriteExplosion : public std::vector<ExplosionFragment> {
37 Sprite::Sprite(const QPixmap& pix, KGameCanvasAbstract* canvas,
38 const QPoint& location)
39 : KGameCanvasPixmap(pix, canvas)
40 , m_pixmap(pix)
41 , m_explode_step(0.0)
42 , m_explosion(NULL)
43 , m_rotation(0.0)
44 , m_scale(1.0)
45 #ifdef DEBUG_PIECE
46 , m_dummy_opacity(255)
47 , m_dummy_visible(false)
48 #endif
50 moveTo(location);
51 lower();
53 #ifdef DEBUG_PIECE
54 update_from_dummy();
55 #endif
58 Sprite::~Sprite() {
59 if(m_explosion)
60 delete m_explosion;
63 Sprite* Sprite::duplicate() const {
64 return new Sprite(pixmap(), canvas(), pos() );
67 void Sprite::setThumb(const QImage& thumb) {
68 kDebug() << "setting thumb";
69 QPixmap pix = m_pixmap;
70 int width = pix.width() / 2;
71 int height = pix.height() / 2;
72 QPixmap thumb_pix = QPixmap::fromImage(
73 thumb.scaled(width, height,
74 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
77 QPainter painter(&pix);
78 painter.drawPixmap(pix.width() - width, 0, thumb_pix);
81 KGameCanvasPixmap::setPixmap(pix);
84 void Sprite::removeThumb() {
85 KGameCanvasPixmap::setPixmap(m_pixmap);
88 void Sprite::setPixmap(const QPixmap& pix) {
89 m_pixmap = pix;
90 KGameCanvasPixmap::setPixmap(pix);
93 void Sprite::setMovementAnimation(const shared_ptr<Animation>& animation) {
94 m_movement_animation = animation;
97 weak_ptr<Animation> Sprite::movementAnimation() const {
98 return m_movement_animation;
101 void Sprite::setFadeAnimation(const shared_ptr<FadeAnimation>& animation) {
102 m_fade_animation = animation;
105 weak_ptr<FadeAnimation> Sprite::fadeAnimation() const {
106 return m_fade_animation;
109 void Sprite::setExplosionStep(float f) {
110 m_explode_step = f;
111 changed();
114 void Sprite::setupExplosion(Random& random) {
115 if (m_explosion) {
116 delete m_explosion;
117 m_explosion = NULL;
119 m_explosion = createExplosion(random);
123 * this function generate many polygons that fill the image, + their random speeds
125 * Split points are some random point on the border (ordered accordin to the angle).
126 * The generated polygons are created cutting from each split point to (according
127 * to random's will) the center -or- a point in the mid way from the center to the
128 * previous or next split point.
130 SpriteExplosion* Sprite::createExplosion(Random& random) {
131 SpriteExplosion* retv = new SpriteExplosion;
132 int w = pixmap().width();
133 int h = pixmap().height();
134 float splits[40];
135 float splits_r[40];
136 float splits_i[40];
137 int split_side[40]; // the side of the image, 0 to 3
138 int split_start[40]; // -1 to 1
139 QPoint split_point[40];
140 QPoint split_start_point[40];
141 int num_splits = 0;
143 if(w==0 || h==0)
144 return retv;
146 /* generate few subsequent angles */
147 Random::RealGenerator random_float = random.rand(0.4, 0.7);
148 for(float s = 0; s < 2*M_PI-0.3; s += random_float()) {
149 splits[num_splits] = s;
150 splits_r[num_splits] = cos(s);
151 splits_i[num_splits] = sin(s);
152 num_splits++;
155 /* generate the random center and calculate the relative corners */
156 random_float = random.rand(0.4, 0.6);
157 QPoint center(int(w * random_float()), int(h * random_float()));
158 QPoint corners[4] = { -center, QPoint(w,0)-center,
159 QPoint(w,h)-center, QPoint(0,h)-center };
161 /* for each split angle, find the size of the rect that contains the split point */
162 for(int i=0;i<num_splits;i++) {
163 split_side[i] = -1;
165 for(int j=0;j<4;j++) {
166 int j1 = (j+1)%4;
168 /* the imaginary part of the products (as complex numbers) must be >0 and <0 */
169 if(corners[j].x()*splits_i[i]-corners[j].y()*splits_r[i] >= 0
170 && corners[j1].x()*splits_i[i]-corners[j1].y()*splits_r[i] < 0 ) {
172 split_side[i] = j;
173 float fact = (corners[j].x()*corners[j1].y()-corners[j].y()*corners[j1].x())/
174 (splits_r[i]*(corners[j1].y()-corners[j].y())-
175 splits_i[i]*(corners[j1].x()-corners[j].x()));
176 float x = splits_r[i]*fact;
177 float y = splits_i[i]*fact;
178 split_point[i] = QPoint(int(x+0.5), int(y+0.5));
179 break;
183 /* there should really be one :) */
184 Q_ASSERT(split_side[i] != -1);
187 /* generate the split points connections. Some will be connected with the center (0),
188 others will be connected to a mid-way point of the previous or next points (-1/+1) */
189 Random::IntegerGenerator coin = random.rand(0, 1);
190 Random::IntegerGenerator die3 = random.rand(0, 2);
191 split_start[0] = 0;
192 for(int i=1;i<num_splits;i++) {
193 if(split_start[i-1]==1)
194 split_start[i] = 0;
195 else if(split_start[i-1]==-1)
196 split_start[i] = coin();
197 else
198 split_start[i] = die3() - 1;
201 /* calculate such mid-way points */
202 random_float = random.rand(0.1, 0.3);
203 for(int i=0;i<num_splits;i++) {
204 int r = (i+split_start[i]);
205 r = r<0 ? num_splits-1 : r>=num_splits ? 0 : r;
206 if(r == i)
207 split_start_point[i] = QPoint();
208 else
209 split_start_point[i] = split_point[r] * random_float();
212 random_float = random.rand(0.5, 1.2);
213 for(int i=0;i<num_splits;i++) {
214 ExplosionFragment f;
215 int i1 = (i+1 == num_splits) ? 0 : i+1;
217 /* generate polygons, they have at most 6 sides */
218 f.m_polygon[f.m_pcount++] = center + split_point[i];
219 if(split_side[i] != split_side[i1])
220 f.m_polygon[f.m_pcount++] = center + corners[split_side[i1]];
221 f.m_polygon[f.m_pcount++] = center + split_point[i1];
223 if(split_start[i1]==+1)
224 f.m_polygon[f.m_pcount++] = center + split_start_point[i1];
226 if(split_start[i1]==-1)
227 f.m_polygon[f.m_pcount++] = center + split_start_point[i1];
228 else if(split_start[i]==+1)
229 f.m_polygon[f.m_pcount++] = center + split_start_point[i];
230 else
231 f.m_polygon[f.m_pcount++] = center;
233 if(split_start[i]==-1)
234 f.m_polygon[f.m_pcount++] = center + split_start_point[i];
236 /* generate a random speed */
237 f.m_speed = (split_point[i]+split_point[i1]) * random_float();
239 retv->push_back(f);
242 return retv;
245 void Sprite::setRotation(float f) {
246 m_rotation = f;
247 changed();
250 void Sprite::setScale(float f) {
251 m_scale = f;
252 changed();
255 void Sprite::paint(QPainter* p) {
256 QMatrix savem;
258 /* if scale/rotate change the painter matrix */
259 if(m_rotation != 0.0 || m_scale != 1.0) {
260 QRectF rect = pixmap().rect();
261 QPointF center(rect.width()*0.5, rect.height()*0.5);
262 savem = p->matrix();
263 p->translate(center+pos());
264 p->rotate(m_rotation*180.0/M_PI);
265 p->scale(m_scale, m_scale);
266 p->translate(-center-pos());
269 if(m_explosion) {
270 p->setPen(Qt::NoPen);
271 p->setBrush(pixmap());
272 p->translate(pos());
274 for(int i=0;i<int(m_explosion->size());i++) {
275 ExplosionFragment& f = (*m_explosion)[i];
276 QPoint delta = f.m_speed*m_explode_step;
278 p->translate(delta);
279 p->drawConvexPolygon(f.m_polygon, f.m_pcount);
280 p->translate(-delta);
282 p->translate(-pos());
284 else
285 KGameCanvasPixmap::paint(p);
287 if(m_rotation != 0.0 || m_scale != 1.0)
288 p->setMatrix(savem);
291 QRect Sprite::rect() const {
292 QRect retv;
294 /* if exploding, set the rect to the bounding rect of the pieces */
295 if(m_explosion) {
296 int x1=99999, x2=-99999, y1=99999, y2=-99999;
298 for(int i=0;i<int(m_explosion->size());i++) {
299 ExplosionFragment& f = (*m_explosion)[i];
300 QPoint delta = f.m_speed*m_explode_step;
302 for(int j=0;j<f.m_pcount;j++) {
303 x1 = std::min(x1, f.m_polygon[j].x()+delta.x());
304 y1 = std::min(y1, f.m_polygon[j].y()+delta.y());
305 x2 = std::max(x2, f.m_polygon[j].x()+delta.x());
306 y2 = std::max(y2, f.m_polygon[j].y()+delta.y());
310 retv = QRect(x1,y1,x2-x1,y2-y1).translated(pos());
312 else
313 retv = KGameCanvasPixmap::rect();
315 /* transform the rectangle as needed */
316 if(m_rotation != 0.0 || m_scale != 1.0) {
317 QRectF rect = pixmap().rect();
318 QPointF center(rect.width()*0.5, rect.height()*0.5);
319 QMatrix transf;
320 transf.translate(+center.x()+pos().x(), +center.y()+pos().y());
321 transf.rotate(m_rotation*180.0/M_PI);
322 transf.scale(m_scale, m_scale);
323 transf.translate(-center.x()-pos().x(), -center.y()-pos().y());
324 return transf.mapRect( retv );
326 return retv;