Removed AlgebraicNotation from the variant API.
[tagua/yd.git] / src / sprite.cpp
blob640f18309ac0cb756b67235907778ecf9554c9fc
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@sns.it>
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 <iostream>
14 #include <cmath>
15 #include <boost/random/uniform_smallint.hpp>
16 #include <vector>
17 #include "common.h"
18 #include "point.h"
19 #include "sprite.h"
22 using namespace boost;
24 struct ExplosionFragment {
25 QPoint m_polygon[6];
26 int m_pcount;
27 QPoint m_speed;
28 ExplosionFragment() : m_pcount(0) {}
31 /* inherit instead of typedef to ease forward declaration :) */
32 class SpriteExplosion : public std::vector<ExplosionFragment> {
36 Sprite::Sprite(const QPixmap& pix, KGameCanvasAbstract* canvas,
37 const QPoint& location)
38 : KGameCanvasPixmap(pix, canvas)
39 , m_pixmap(pix)
40 , m_explode_step(0.0)
41 , m_explosion(NULL)
42 , m_rotation(0.0)
43 , m_scale(1.0)
44 #ifdef DEBUG_PIECE
45 , m_dummy_opacity(255)
46 , m_dummy_visible(false)
47 #endif
49 moveTo(location);
50 lower();
52 #ifdef DEBUG_PIECE
53 update_from_dummy();
54 #endif
57 Sprite::~Sprite() {
58 if(m_explosion)
59 delete m_explosion;
62 Sprite* Sprite::duplicate() const {
63 return new Sprite(pixmap(), canvas(), pos() );
66 void Sprite::setThumb(const QImage& thumb) {
67 std::cout << "setting thumb" << std::endl;
68 QPixmap pix = m_pixmap;
69 int width = pix.width() / 2;
70 int height = pix.height() / 2;
71 QPixmap thumb_pix = QPixmap::fromImage(
72 thumb.scaled(width, height,
73 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
76 QPainter painter(&pix);
77 painter.drawPixmap(pix.width() - width, 0, thumb_pix);
80 KGameCanvasPixmap::setPixmap(pix);
83 void Sprite::removeThumb() {
84 KGameCanvasPixmap::setPixmap(m_pixmap);
87 void Sprite::setPixmap(const QPixmap& pix) {
88 m_pixmap = pix;
89 KGameCanvasPixmap::setPixmap(pix);
92 void Sprite::setMovementAnimation(const shared_ptr<Animation>& animation) {
93 m_movement_animation = animation;
96 weak_ptr<Animation> Sprite::movementAnimation() const {
97 return m_movement_animation;
100 void Sprite::setFadeAnimation(const shared_ptr<FadeAnimation>& animation) {
101 m_fade_animation = animation;
104 weak_ptr<FadeAnimation> Sprite::fadeAnimation() const {
105 return m_fade_animation;
108 void Sprite::setExplosionStep(float f) {
109 m_explode_step = f;
110 changed();
113 void Sprite::setupExplosion(Random& random) {
114 if (m_explosion) {
115 delete m_explosion;
116 m_explosion = NULL;
118 m_explosion = createExplosion(random);
122 * this function generate many polygons that fill the image, + their random speeds
124 * Split points are some random point on the border (ordered accordin to the angle).
125 * The generated polygons are created cutting from each split point to (according
126 * to random's will) the center -or- a point in the mid way from the center to the
127 * previous or next split point.
129 SpriteExplosion* Sprite::createExplosion(Random& random) {
130 SpriteExplosion* retv = new SpriteExplosion;
131 int w = pixmap().width();
132 int h = pixmap().height();
133 float splits[40];
134 float splits_r[40];
135 float splits_i[40];
136 int split_side[40]; // the side of the image, 0 to 3
137 int split_start[40]; // -1 to 1
138 QPoint split_point[40];
139 QPoint split_start_point[40];
140 int num_splits = 0;
142 if(w==0 || h==0)
143 return retv;
145 /* generate few subsequent angles */
146 Random::RealGenerator random_float = random.rand(0.4, 0.7);
147 for(float s = 0; s < 2*M_PI-0.3; s += random_float()) {
148 splits[num_splits] = s;
149 splits_r[num_splits] = cos(s);
150 splits_i[num_splits] = sin(s);
151 num_splits++;
154 /* generate the random center and calculate the relative corners */
155 random_float = random.rand(0.4, 0.6);
156 QPoint center(int(w * random_float()), int(h * random_float()));
157 QPoint corners[4] = { -center, QPoint(w,0)-center,
158 QPoint(w,h)-center, QPoint(0,h)-center };
160 /* for each split angle, find the size of the rect that contains the split point */
161 for(int i=0;i<num_splits;i++) {
162 split_side[i] = -1;
164 for(int j=0;j<4;j++) {
165 int j1 = (j+1)%4;
167 /* the imaginary part of the products (as complex numbers) must be >0 and <0 */
168 if(corners[j].x()*splits_i[i]-corners[j].y()*splits_r[i] >= 0
169 && corners[j1].x()*splits_i[i]-corners[j1].y()*splits_r[i] < 0 ) {
171 split_side[i] = j;
172 float fact = (corners[j].x()*corners[j1].y()-corners[j].y()*corners[j1].x())/
173 (splits_r[i]*(corners[j1].y()-corners[j].y())-
174 splits_i[i]*(corners[j1].x()-corners[j].x()));
175 float x = splits_r[i]*fact;
176 float y = splits_i[i]*fact;
177 split_point[i] = QPoint(int(x+0.5), int(y+0.5));
178 break;
182 /* there should really be one :) */
183 Q_ASSERT(split_side[i] != -1);
186 /* generate the split points connections. Some will be connected with the center (0),
187 others will be connected to a mid-way point of the previous or next points (-1/+1) */
188 Random::IntegerGenerator coin = random.rand(0, 1);
189 Random::IntegerGenerator die3 = random.rand(0, 2);
190 split_start[0] = 0;
191 for(int i=1;i<num_splits;i++) {
192 if(split_start[i-1]==1)
193 split_start[i] = 0;
194 else if(split_start[i-1]==-1)
195 split_start[i] = coin();
196 else
197 split_start[i] = die3() - 1;
200 /* calculate such mid-way points */
201 random_float = random.rand(0.1, 0.3);
202 for(int i=0;i<num_splits;i++) {
203 int r = (i+split_start[i]);
204 r = r<0 ? num_splits-1 : r>=num_splits ? 0 : r;
205 if(r == i)
206 split_start_point[i] = QPoint();
207 else
208 split_start_point[i] = split_point[r] * random_float();
211 random_float = random.rand(0.5, 1.2);
212 for(int i=0;i<num_splits;i++) {
213 ExplosionFragment f;
214 int i1 = (i+1 == num_splits) ? 0 : i+1;
216 /* generate polygons, they have at most 6 sides */
217 f.m_polygon[f.m_pcount++] = center + split_point[i];
218 if(split_side[i] != split_side[i1])
219 f.m_polygon[f.m_pcount++] = center + corners[split_side[i1]];
220 f.m_polygon[f.m_pcount++] = center + split_point[i1];
222 if(split_start[i1]==+1)
223 f.m_polygon[f.m_pcount++] = center + split_start_point[i1];
225 if(split_start[i1]==-1)
226 f.m_polygon[f.m_pcount++] = center + split_start_point[i1];
227 else if(split_start[i]==+1)
228 f.m_polygon[f.m_pcount++] = center + split_start_point[i];
229 else
230 f.m_polygon[f.m_pcount++] = center;
232 if(split_start[i]==-1)
233 f.m_polygon[f.m_pcount++] = center + split_start_point[i];
235 /* generate a random speed */
236 f.m_speed = (split_point[i]+split_point[i1]) * random_float();
238 retv->push_back(f);
241 return retv;
244 void Sprite::setRotation(float f) {
245 m_rotation = f;
246 changed();
249 void Sprite::setScale(float f) {
250 m_scale = f;
251 changed();
254 void Sprite::paint(QPainter* p) {
255 QMatrix savem;
257 /* if scale/rotate change the painter matrix */
258 if(m_rotation != 0.0 || m_scale != 1.0) {
259 QRectF rect = pixmap().rect();
260 QPointF center(rect.width()*0.5, rect.height()*0.5);
261 savem = p->matrix();
262 p->translate(center+pos());
263 p->rotate(m_rotation*180.0/M_PI);
264 p->scale(m_scale, m_scale);
265 p->translate(-center-pos());
268 if(m_explosion) {
269 p->setPen(Qt::NoPen);
270 p->setBrush(pixmap());
271 p->translate(pos());
273 for(int i=0;i<int(m_explosion->size());i++) {
274 ExplosionFragment& f = (*m_explosion)[i];
275 QPoint delta = f.m_speed*m_explode_step;
277 p->translate(delta);
278 p->drawConvexPolygon(f.m_polygon, f.m_pcount);
279 p->translate(-delta);
281 p->translate(-pos());
283 else
284 KGameCanvasPixmap::paint(p);
286 if(m_rotation != 0.0 || m_scale != 1.0)
287 p->setMatrix(savem);
290 QRect Sprite::rect() const {
291 QRect retv;
293 /* if exploding, set the rect to the bounding rect of the pieces */
294 if(m_explosion) {
295 int x1=99999, x2=-99999, y1=99999, y2=-99999;
297 for(int i=0;i<int(m_explosion->size());i++) {
298 ExplosionFragment& f = (*m_explosion)[i];
299 QPoint delta = f.m_speed*m_explode_step;
301 for(int j=0;j<f.m_pcount;j++) {
302 x1 = std::min(x1, f.m_polygon[j].x()+delta.x());
303 y1 = std::min(y1, f.m_polygon[j].y()+delta.y());
304 x2 = std::max(x2, f.m_polygon[j].x()+delta.x());
305 y2 = std::max(y2, f.m_polygon[j].y()+delta.y());
309 retv = QRect(x1,y1,x2-x1,y2-y1).translated(pos());
311 else
312 retv = KGameCanvasPixmap::rect();
314 /* transform the rectangle as needed */
315 if(m_rotation != 0.0 || m_scale != 1.0) {
316 QRectF rect = pixmap().rect();
317 QPointF center(rect.width()*0.5, rect.height()*0.5);
318 QMatrix transf;
319 transf.translate(+center.x()+pos().x(), +center.y()+pos().y());
320 transf.rotate(m_rotation*180.0/M_PI);
321 transf.scale(m_scale, m_scale);
322 transf.translate(-center.x()-pos().x(), -center.y()-pos().y());
323 return transf.mapRect( retv );
325 return retv;