1 /*
2 * SpriteView.cpp
3 * toycars_vehicle_editor
4 *
5 * Created by Ruben Henner Zilibowitz on 23/02/09.
6 * Copyright 2009 Ruben Henner Zilibowitz. All rights reserved.
7 *
8 */
9
10 #include "SpriteView.h"
11 #include "FL/Fl_Image.H"
12 #include <cstdio>
13 #include <cmath>
14 #include "LoadSpriteFromImage.h"
15 #include "ScException.h"
16 #include <FL/fl_ask.H>
17 #include "SearchDirectories.h"
18
19 extern SearchDirectories *gSearchDirs;
20
SpriteView(int x,int y,int w,int h,const char * l)21 SpriteView::SpriteView(int x,int y,int w,int h,const char *l)
22 : Fl_Gl_Window(x,y,w,h,l),
23 vehicleSprite(NULL), wheelSprite(NULL),
24 rotation(0), steerAngle(0), scale(2), o(NULL),
25 setting(kNoSetting), mouseIsDown(false), mouseIsInside(false),
26 convexChoice(0), mouseConvexity(true)
27 {
28 }
29
~SpriteView()30 SpriteView::~SpriteView()
31 {
32 if (vehicleSprite)
33 delete vehicleSprite;
34 if (wheelSprite)
35 delete wheelSprite;
36 }
37
handle(int event)38 int SpriteView::handle(int event)
39 {
40 if (event == FL_PUSH) {
41 if (setting == kGeometry) {
42 if (!o->getGeometry()->empty()) {
43 std::list<Vec2D> &curConvex = (*o->getGeometry())[convexChoice];
44 if (Fl::event_button() == FL_LEFT_MOUSE && mouseConvexity)
45 curConvex.push_back(Vec2D(mousex,mousey));
46 else if (Fl::event_button() == FL_RIGHT_MOUSE && !curConvex.empty())
47 curConvex.pop_back();
48 }
49 }
50 mouseIsDown = true;
51 redraw();
52 return 1;
53 }
54 else if (event == FL_DRAG) {
55 redraw();
56 return 1;
57 }
58 else if (event == FL_RELEASE) {
59 mouseIsDown = false;
60 redraw();
61 return 1;
62 }
63 else if (event == FL_ENTER) {
64 mouseIsInside = true;
65 redraw();
66 return 1;
67 }
68 else if (event == FL_MOVE) {
69 redraw();
70 return 1;
71 }
72 else if (event == FL_LEAVE) {
73 mouseIsInside = false;
74 redraw();
75 return 1;
76 }
77
78 return Fl_Gl_Window::handle(event);
79 }
80
angleReflexive(const Vec2D & A,const Vec2D & B,const Vec2D & C)81 bool angleReflexive(const Vec2D &A, const Vec2D &B, const Vec2D &C)
82 {
83 Vec2D X(A.x - B.x, A.y - B.y);
84 Vec2D Y(C.x - B.x, C.y - B.y);
85 return X.x*Y.y - X.y*Y.x >= 0;
86 }
87
checkConvexity(const std::list<Vec2D> & convex,Vec2D mouse)88 bool checkConvexity(const std::list<Vec2D> &convex, Vec2D mouse)
89 {
90 if (convex.size() <= 2)
91 return true;
92
93 std::list<Vec2D>::const_iterator A = convex.end(); A--;
94 std::list<Vec2D>::const_iterator B = A; A--;
95 std::list<Vec2D>::const_iterator C = convex.begin();
96 std::list<Vec2D>::const_iterator D = C; D++;
97
98 // A,B,mouse
99 if (angleReflexive(*A, *B, mouse))
100 return false;
101 // B,mouse,C
102 if (angleReflexive(*B, mouse, *C))
103 return false;
104 // mouse,C,D
105 if (angleReflexive(mouse, *C, *D))
106 return false;
107
108 return true;
109 }
110
draw()111 void SpriteView::draw()
112 {
113 if (!valid()) {
114 glLoadIdentity(); glViewport(0,0,w(),h());
115 glOrtho(0,w(),0,h(),-1,1); glEnable(GL_BLEND);
116 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
117 glClearColor(1,1,1,1);
118 }
119
120 // load the vehicle sprite from the file is not loaded
121 if (!(o->getVehicleSpriteFilename()[0] == '\0' || vehicleSprite)) {
122 ScSprite *sprite;
123 try { sprite = loadSpriteFromImage(o->getVehicleSpriteFilename(), o->getVehicleFrameWidth(), o->getVehicleFrameHeight(), gSearchDirs); }
124 catch (ScException e) {
125 e.printMsg();
126 fl_alert("Error loading vehicle sprite.");
127 return;
128 }
129 if (sprite == NULL)
130 return;
131 vehicleSprite = sprite;
132 }
133
134 // load the wheel sprite from the file is not loaded
135 if (!(o->getWheelSpriteFilename()[0] == '\0' || wheelSprite)) {
136 ScSprite *sprite;
137 try { sprite = loadSpriteFromImage(o->getWheelSpriteFilename(), o->getWheelFrameWidth(), o->getWheelFrameHeight(), gSearchDirs); }
138 catch (ScException e) {
139 e.printMsg();
140 fl_alert("Error loading wheel sprite.");
141 return;
142 }
143 if (sprite == NULL)
144 return;
145 wheelSprite = sprite;
146 }
147
148 // draw sprites
149 glClear(GL_COLOR_BUFFER_BIT);
150 glPushMatrix();
151 glTranslated((w() - o->getVehicleFrameWidth()*scale)*0.5, (h() - o->getVehicleFrameHeight()*scale)*0.5, 0);
152 glScaled(scale, scale, 1);
153 if (o->getWheelsAboveSprite()) {
154 drawVehicleSprite();
155 drawWheelsSprites();
156 }
157 else {
158 drawWheelsSprites();
159 drawVehicleSprite();
160 }
161 glPopMatrix();
162
163 // draw edit guides
164 mousex = (-w()*0.5 + Fl::event_x() - x()) / scale;
165 mousey = (h()*0.5 - Fl::event_y() + y()) / scale;
166 glDisable(GL_TEXTURE_2D);
167 glEnable(GL_TEXTURE_1D);
168 glPushMatrix();
169 glTranslated(w()*0.5, h()*0.5, 0);
170 glRotated(rotation*360, 0, 0, 1);
171 glScaled(scale, scale, 1);
172 const int geomSize = o->getGeometry()->size();
173 switch (setting) {
174 case kGeometry:
175 mouseConvexity = true;
176 for (int i = 0; i < geomSize; i++) {
177 glBegin(GL_LINE_LOOP);
178 glColor3d(1,0,0);
179 const std::list<Vec2D> &l = (*o->getGeometry())[i];
180 for (std::list<Vec2D>::const_iterator it = l.begin(); it != l.end(); it++)
181 glVertex2d(it->x, it->y);
182 glEnd();
183 }
184 if (geomSize > 0 && mouseIsInside) {
185 const std::list<Vec2D> &curConvex = (*o->getGeometry())[convexChoice];
186 if (!curConvex.empty()) {
187 mouseConvexity = checkConvexity(curConvex, Vec2D(mousex,mousey));
188 if (mouseConvexity)
189 glColor3d(0,1,0);
190 else
191 glColor3d(1,0,0);
192 glBegin(GL_LINES);
193 glVertex2d(curConvex.back().x, curConvex.back().y);
194 glVertex2d(mousex, mousey);
195 glVertex2d(mousex, mousey);
196 glVertex2d(curConvex.front().x, curConvex.front().y);
197 glEnd();
198 }
199 }
200 break;
201 case kAxelWidth:
202 if (!mouseIsInside && !mouseIsDown)
203 mousey = o->getAxelWidth()*0.5;
204 glBegin(GL_LINES);
205 glColor3d(1, 0, 0);
206 glVertex2d(o->getFrontAxel(), 0);
207 glVertex2d(o->getFrontAxel(), mousey);
208 glColor3d(0, 1, 0);
209 glVertex2d(-o->getRearAxel(), 0);
210 glVertex2d(-o->getRearAxel(), mousey);
211 glEnd();
212 if (mouseIsDown) {
213 settingInput->value(mousey*2);
214 o->setAxelWidth(settingInput->value());
215 }
216 break;
217 case kFrontAxel:
218 if (!mouseIsInside && !mouseIsDown)
219 mousex = o->getFrontAxel();
220 glBegin(GL_LINES);
221 glColor3d(1, 0, 0);
222 glVertex2d(0, 0);
223 glVertex2d(mousex, 0);
224 glColor3d(0, 1, 0);
225 glVertex2d(mousex, -o->getAxelWidth()*0.5);
226 glVertex2d(mousex, o->getAxelWidth()*0.5);
227 glEnd();
228 if (mouseIsDown) {
229 settingInput->value(mousex);
230 o->setFrontAxel(settingInput->value());
231 }
232 break;
233 case kRearAxel:
234 if (!mouseIsInside && !mouseIsDown)
235 mousex = -o->getRearAxel();
236 glBegin(GL_LINES);
237 glColor3d(1, 0, 0);
238 glVertex2d(0, 0);
239 glVertex2d(mousex, 0);
240 glColor3d(0, 1, 0);
241 glVertex2d(mousex, -o->getAxelWidth()*0.5);
242 glVertex2d(mousex, o->getAxelWidth()*0.5);
243 glEnd();
244 if (mouseIsDown) {
245 settingInput->value(-mousex);
246 o->setRearAxel(settingInput->value());
247 }
248 break;
249 case kFrontWheelInset:
250 if (!mouseIsInside && !mouseIsDown)
251 mousey = o->getFrontWheelInset();
252 glBegin(GL_LINES);
253 // right wheel
254 glColor3d(0, 1, 0);
255 glVertex2d(o->getFrontAxel() - o->getFrontWheelRadius(), -o->getAxelWidth()*0.5);
256 glVertex2d(o->getFrontAxel() + o->getFrontWheelRadius(), -o->getAxelWidth()*0.5);
257 glVertex2d(o->getFrontAxel() + o->getFrontWheelRadius(), -o->getAxelWidth()*0.5);
258 glVertex2d(o->getFrontAxel() + o->getFrontWheelRadius(), -o->getAxelWidth()*0.5 + mousey);
259 glVertex2d(o->getFrontAxel() - o->getFrontWheelRadius(), -o->getAxelWidth()*0.5 + mousey);
260 glVertex2d(o->getFrontAxel() - o->getFrontWheelRadius(), -o->getAxelWidth()*0.5);
261 glColor3d(1, 0, 0);
262 glVertex2d(o->getFrontAxel() + o->getFrontWheelRadius(), -o->getAxelWidth()*0.5 + mousey);
263 glVertex2d(o->getFrontAxel() - o->getFrontWheelRadius(), -o->getAxelWidth()*0.5 + mousey);
264 // left wheel
265 glColor3d(0, 1, 0);
266 glVertex2d(o->getFrontAxel() - o->getFrontWheelRadius(), o->getAxelWidth()*0.5);
267 glVertex2d(o->getFrontAxel() + o->getFrontWheelRadius(), o->getAxelWidth()*0.5);
268 glVertex2d(o->getFrontAxel() + o->getFrontWheelRadius(), o->getAxelWidth()*0.5);
269 glVertex2d(o->getFrontAxel() + o->getFrontWheelRadius(), o->getAxelWidth()*0.5 - mousey);
270 glVertex2d(o->getFrontAxel() - o->getFrontWheelRadius(), o->getAxelWidth()*0.5 - mousey);
271 glVertex2d(o->getFrontAxel() - o->getFrontWheelRadius(), o->getAxelWidth()*0.5);
272 glColor3d(1, 0, 0);
273 glVertex2d(o->getFrontAxel() + o->getFrontWheelRadius(), o->getAxelWidth()*0.5 - mousey);
274 glVertex2d(o->getFrontAxel() - o->getFrontWheelRadius(), o->getAxelWidth()*0.5 - mousey);
275 glEnd();
276 if (mouseIsDown) {
277 settingInput->value(mousey);
278 o->setFrontWheelInset(settingInput->value());
279 }
280 break;
281 case kFrontWheelRadius:
282 if (!mouseIsInside && !mouseIsDown)
283 mousex = o->getFrontWheelRadius();
284 else
285 mousex -= o->getFrontAxel();
286 glBegin(GL_LINES);
287 // right wheel
288 glColor3d(0, 1, 0);
289 glVertex2d(o->getFrontAxel() - mousex, -o->getAxelWidth()*0.5);
290 glVertex2d(o->getFrontAxel() + mousex, -o->getAxelWidth()*0.5);
291 glVertex2d(o->getFrontAxel() + mousex, -o->getAxelWidth()*0.5);
292 glVertex2d(o->getFrontAxel() + mousex, -o->getAxelWidth()*0.5 + o->getFrontWheelInset());
293 glVertex2d(o->getFrontAxel() - mousex, -o->getAxelWidth()*0.5 + o->getFrontWheelInset());
294 glVertex2d(o->getFrontAxel() - mousex, -o->getAxelWidth()*0.5);
295 glColor3d(1, 0, 0);
296 glVertex2d(o->getFrontAxel() + mousex, -o->getAxelWidth()*0.5 + o->getFrontWheelInset());
297 glVertex2d(o->getFrontAxel() - mousex, -o->getAxelWidth()*0.5 + o->getFrontWheelInset());
298 // left wheel
299 glColor3d(0, 1, 0);
300 glVertex2d(o->getFrontAxel() - mousex, o->getAxelWidth()*0.5);
301 glVertex2d(o->getFrontAxel() + mousex, o->getAxelWidth()*0.5);
302 glVertex2d(o->getFrontAxel() + mousex, o->getAxelWidth()*0.5);
303 glVertex2d(o->getFrontAxel() + mousex, o->getAxelWidth()*0.5 - o->getFrontWheelInset());
304 glVertex2d(o->getFrontAxel() - mousex, o->getAxelWidth()*0.5 - o->getFrontWheelInset());
305 glVertex2d(o->getFrontAxel() - mousex, o->getAxelWidth()*0.5);
306 glColor3d(1, 0, 0);
307 glVertex2d(o->getFrontAxel() + mousex, o->getAxelWidth()*0.5 - o->getFrontWheelInset());
308 glVertex2d(o->getFrontAxel() - mousex, o->getAxelWidth()*0.5 - o->getFrontWheelInset());
309 glEnd();
310 if (mouseIsDown) {
311 settingInput->value(fabs(mousex));
312 o->setFrontWheelRadius(settingInput->value());
313 }
314 break;
315 case kRearWheelRadius:
316 if (!mouseIsInside && !mouseIsDown)
317 mousex = o->getRearWheelRadius();
318 else
319 mousex += o->getRearAxel();
320 glBegin(GL_LINES);
321 // right wheel
322 glColor3d(0, 1, 0);
323 glVertex2d(-o->getRearAxel() - mousex, -o->getAxelWidth()*0.5);
324 glVertex2d(-o->getRearAxel() + mousex, -o->getAxelWidth()*0.5);
325 glVertex2d(-o->getRearAxel() + mousex, -o->getAxelWidth()*0.5);
326 glVertex2d(-o->getRearAxel() + mousex, -o->getAxelWidth()*0.5);
327 glVertex2d(-o->getRearAxel() - mousex, -o->getAxelWidth()*0.5);
328 glVertex2d(-o->getRearAxel() - mousex, -o->getAxelWidth()*0.5);
329 glColor3d(1, 0, 0);
330 glVertex2d(-o->getRearAxel() + mousex, -o->getAxelWidth()*0.5);
331 glVertex2d(-o->getRearAxel() - mousex, -o->getAxelWidth()*0.5);
332 // left wheel
333 glColor3d(0, 1, 0);
334 glVertex2d(-o->getRearAxel() - mousex, o->getAxelWidth()*0.5);
335 glVertex2d(-o->getRearAxel() + mousex, o->getAxelWidth()*0.5);
336 glVertex2d(-o->getRearAxel() + mousex, o->getAxelWidth()*0.5);
337 glVertex2d(-o->getRearAxel() + mousex, o->getAxelWidth()*0.5);
338 glVertex2d(-o->getRearAxel() - mousex, o->getAxelWidth()*0.5);
339 glVertex2d(-o->getRearAxel() - mousex, o->getAxelWidth()*0.5);
340 glColor3d(1, 0, 0);
341 glVertex2d(-o->getRearAxel() + mousex, o->getAxelWidth()*0.5);
342 glVertex2d(-o->getRearAxel() - mousex, o->getAxelWidth()*0.5);
343 glEnd();
344 if (mouseIsDown) {
345 settingInput->value(fabs(mousex));
346 o->setRearWheelRadius(settingInput->value());
347 }
348 break;
349 }
350 glPopMatrix();
351 glDisable(GL_TEXTURE_1D);
352 glEnable(GL_TEXTURE_2D);
353 }
354
reset()355 void SpriteView::reset()
356 {
357 if (vehicleSprite) {
358 delete vehicleSprite;
359 vehicleSprite = NULL;
360 }
361 if (wheelSprite) {
362 delete wheelSprite;
363 wheelSprite = NULL;
364 }
365 rotation = 0;
366 steerAngle = 0;
367 scale = 2;
368 redraw();
369 }
370
resetVehicleSprite()371 void SpriteView::resetVehicleSprite()
372 {
373 if (vehicleSprite) {
374 delete vehicleSprite;
375 vehicleSprite = NULL;
376 }
377 redraw();
378 }
379
resetWheelSprite()380 void SpriteView::resetWheelSprite()
381 {
382 if (wheelSprite) {
383 delete wheelSprite;
384 wheelSprite = NULL;
385 }
386 redraw();
387 }
388
drawVehicleSprite()389 void SpriteView::drawVehicleSprite()
390 {
391 if (vehicleSprite) {
392 vehicleSprite->setFrame(fmod(vehicleSprite->getNumFrames()*rotation, vehicleSprite->getNumFrames()));
393 vehicleSprite->doDraw();
394 }
395 }
396
drawWheelsSprites()397 void SpriteView::drawWheelsSprites()
398 {
399 if (wheelSprite) {
400 double x, y;
401 glPushMatrix();
402 x = o->getFrontAxel();
403 y = o->getAxelWidth()*0.5 - o->getFrontWheelInset();
404 glTranslated(x*cos(rotation*2*M_PI) - y*sin(rotation*2*M_PI) + (o->getVehicleFrameWidth() - o->getWheelFrameWidth())*0.5
405 , x*sin(rotation*2*M_PI) + y*cos(rotation*2*M_PI) + (o->getVehicleFrameHeight() - o->getWheelFrameHeight())*0.5, 0);
406 wheelSprite->setFrame(fmod(wheelSprite->getNumFrames() + wheelSprite->getNumFrames() * (steerAngle + rotation), wheelSprite->getNumFrames()));
407 wheelSprite->doDraw();
408 glPopMatrix();
409 glPushMatrix();
410 x = o->getFrontAxel();
411 y = -o->getAxelWidth()*0.5 + o->getFrontWheelInset();
412 glTranslated(x*cos(rotation*2*M_PI) - y*sin(rotation*2*M_PI) + (o->getVehicleFrameWidth() - o->getWheelFrameWidth())*0.5
413 , x*sin(rotation*2*M_PI) + y*cos(rotation*2*M_PI) + (o->getVehicleFrameHeight() - o->getWheelFrameHeight())*0.5, 0);
414 wheelSprite->setFrame(fmod(wheelSprite->getNumFrames() + wheelSprite->getNumFrames() * (steerAngle + rotation + 0.5), wheelSprite->getNumFrames()));
415 wheelSprite->doDraw();
416 glPopMatrix();
417 }
418 }
419