wmcube: imported Upstream version 0.99-pre1
[dockapps.git] / wmcube / wmapp / example2 / wmradar.cc
blob8344c1eab3e0d4fe32312649036d8943e173e3cb
1 #include <cstdlib>
2 #include <cmath>
3 #include <sstream>
4 #include <iomanip>
5 #include "wmradar.h"
6 #include "../wmapp.h"
8 using std::sin;
9 using std::cos;
10 using std::atan2;
12 WMRadar::WMRadar(WMTextBar* t, WMSlider* s)
13 : angle(0), text_score(t), speed(s)
15 setbuffered(true);
16 text_score->settext(" 0", false);
17 speed->settotal(50, false);
18 speed->setvalue(8, false);
21 void
22 WMRadar::real_display()
24 if (!p_icon())
25 seticon(false);
26 setcolor(WMColor(Background));
27 fill_rectangle(0, 0, b_width(), b_height()); //clear background
29 setcolor(WMColor(Dim));
30 for (double i = 0; i < 2 * M_PI; i += M_PI_4)
31 draw_line(b_width() / 2, b_height() / 2, (1 + cos(i)) * b_width() / 2,
32 (1 - sin(i)) * b_height() / 2); //draw radial lines
34 draw_arc(b_width() / 3, b_height() / 3, b_width() / 3 - 1, b_height() / 3 - 1,
35 0, 360 * 64);
36 draw_arc(b_width() / 6, b_height() / 6, 2 * b_width() / 3,
37 2 * b_height() / 3, 0, 360 * 64); //draw circles
39 setcolor(Color(255, 0, 0));
40 for (clist::const_iterator i = collisions.begin(); i != collisions.end(); ++i)
41 fill_arc(i->x - 1, i->y - 1, 3, 3, 0, 360 * 64); //draw collision points
43 std::vector<X::XPoint> points(3);
44 for (plist::const_iterator i = planes.begin(); i != planes.end(); ++i)
46 setcolor(Color(0, 255 * i->brightness, 0)); // achieve a fading effect
47 points[0].x = i->x;
48 points[0].y = i->y;
49 points[1].x = int(i->x) + 7 * cos(i->direction + 5.5 * M_PI / 6);
50 points[1].y = int(i->y) - 7 * sin(i->direction + 5.5 * M_PI / 6);
51 points[2].x = int(i->x) + 7 * cos(i->direction + 6.5 * M_PI / 6);
52 points[2].y = int(i->y) - 7 * sin(i->direction + 6.5 * M_PI / 6);
53 // construct an isoceles triangle pointing in plane's direction of motion
54 fill_polygon(points); // and draw it
57 setcolor(Color(0, 255, 0));
58 draw_line(b_width() / 2, b_height() / 2, (1 + cos(angle)) * b_width() / 2,
59 (1 - sin(angle)) * b_height() / 2); //draw radar line
61 WMImage::real_display(); //update window
64 double rand(double low, double high)
66 return low + std::rand() * (high - low) / RAND_MAX;
69 void
70 WMRadar::add_plane()
72 if (planes.size() > 4)
73 return;
75 double theta = rand(0, 2 * M_PI); //put plane somewhere along edge
76 plane p = { (1 + cos(theta)) * b_width() / 2,
77 (1 - sin(theta)) * b_height() / 2,
78 rand(theta + 3 * M_PI_4, theta + 5 * M_PI_4), //direction pointing inwards
79 0.5 }; //brightness
81 planes.push_back(p);
84 inline double dist2 (double x1, double y1, double x2, double y2)
85 { //return distance squared between points
86 return std::pow(x1 - x2, 2) + std::pow(y1 - y2, 2);
89 inline double norm_angle(double angle)
90 { //return angle shifted into the range [0, 2pi)
91 return angle - 2 * M_PI * std::floor(angle / (2 * M_PI));
94 bool
95 WMRadar::release(int, int x, int y)
97 if (wPressed)
99 wPressed = false;
100 if (contains(x, y) && planes.size())
102 int newx = x - b_left(),
103 newy = y - b_top();
104 plist::iterator closest;
105 double mindist2 = 10000;
106 for (plist::iterator i = planes.begin(); i != planes.end(); ++i)
108 double d2 = dist2(i->x, i->y, newx, newy);
109 if (d2 < mindist2)
111 mindist2 = d2;
112 closest = i;
115 closest->direction = norm_angle(atan2(double(int(closest->y) - newy),
116 double(newx - int(closest->x))));
117 //change nearest plane's direction towards (x, y)
119 return true;
121 else return false;
124 void
125 WMRadar::increment_time()
127 for (plist::iterator i = planes.begin(); i != planes.end();)
128 { //Collision test planes
129 if (!contains(i->x + left(), i->y + top()))
130 ++i;
131 else
133 plist::iterator j = i;
134 bool collided = false;
135 for (++j; j != planes.end();)
136 if (dist2(i->x, i->y, j->x, j->y) <= 0.75)
137 { //remove each collided plane and increment score
138 j = planes.erase(j);
139 collided = true;
140 increment_score();
142 else
143 ++j;
144 if (collided)
146 increment_score();
147 collision c = { i->x, i->y };
148 collisions.push_back(c); //make record of collision location
149 i = planes.erase(i);
151 else
152 ++i;
156 const double angleinc = M_PI / 125;
158 for (plist::iterator i = planes.begin(); i != planes.end();)
160 double incx = speed->value() * cos(i->direction) / 100;
161 double incy = -speed->value() * sin(i->direction) / 100;
162 double nextx = i->x + incx;
163 double nexty = i->y + incy;
165 //to check if a plane crossed the radar line, approximate the plane's path
166 //as an arc:
167 double& l_start_angle = angle; //make an interval of radar line's angles
168 const double l_end_angle = l_start_angle + angleinc;
170 //make an interval of the plane's angles
171 double p_start_angle = norm_angle(atan2(b_height() / 2 - i->y,
172 i->x - b_width() / 2));
173 double p_end_angle = norm_angle(atan2(b_height() / 2 - nexty,
174 nextx - b_width() / 2));
176 const double crossp = (b_width() / 2 - i->x) * incy
177 - (b_height() / 2 - i->y) * incx; //test whether plane is moving in
178 //a positive or a negative angle
179 if (crossp < 0.0) //make angle increase counterclockwise
181 const double temp = p_start_angle;
182 p_start_angle = p_end_angle;
183 p_end_angle = temp;
185 if (p_end_angle < p_start_angle)
186 p_end_angle += 2 * M_PI; //make sure end is > start
188 //intersect radar line interval and plane interval. If result is non-empty,
189 //there was an intersection.
190 if (std::min(p_end_angle, l_end_angle)
191 >= std::max(p_start_angle, l_start_angle))
192 i->brightness = 1.0; //if plane crossed radar line, make it bright
193 else
194 i->brightness *= 0.996; //otherwise decay brightness
197 i->x = nextx; //update plane's position
198 i->y = nexty;
199 if (!WMWidget::contains(i->x + b_left(), i->y + b_top()))
200 i = planes.erase(i); //cull planes outside of radar
201 else
202 ++i;
205 angle = norm_angle(angle + angleinc); //increment radar direction
207 display();
210 void
211 WMRadar::increment_score ()
213 using namespace std;
214 istringstream in(text_score->text()); //get string from textbar
215 int score;
216 in >> score; //extract score
217 ostringstream out;
218 out << setw(6) << setfill(' ') << score + 1; //increment and put in a string
219 text_score->settext(out.str()); //put back in textbar