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