Project

General

Profile

Statistics
| Revision:

root / branches / simulator / projects / simulator / simulator / gui / gtk_environment_view.c @ 1184

History | View | Annotate | Download (14.7 KB)

1
#include <math.h>
2
#include <stdlib.h>
3
#include <stdio.h>
4
#include <sys/time.h>
5
#include <string.h>
6

    
7
#include <gtk/gtkmain.h>
8
#include <gtk/gtksignal.h>
9
#include <gdk/gdkkeysyms.h>
10

    
11
#include "gtk_environment_view.h"
12

    
13
#include "robot.h"
14
#include "world.h"
15

    
16
#define ENVIRONMENT_VIEW_MIN_WIDTH                100
17
#define ENVIRONMENT_VIEW_MIN_HEIGHT                50
18

    
19
#define ROBOT_DIAMETER                                        30
20

    
21
static GtkWidgetClass* parent_class = NULL;
22

    
23
static void gtk_environment_view_class_init(GtkEnvironmentViewClass* environmentClass);
24
static void gtk_environment_view_init(GtkEnvironmentView* view);
25
static void gtk_environment_view_destroy(GtkObject* object);
26

    
27
static void gtk_environment_view_size_request(GtkWidget* widget,
28
                GtkRequisition* req);
29
static void gtk_environment_view_realize(GtkWidget* widget);
30
static void gtk_environment_view_size_allocate(GtkWidget* widget, GtkAllocation* a);
31
static gboolean gtk_environment_view_expose(GtkWidget* widget,
32
                GdkEventExpose* event);
33

    
34
static gboolean gtk_environment_view_mouse_down(GtkWidget* widget,
35
                                GdkEventButton* event);
36
static gboolean gtk_environment_view_mouse_release(GtkWidget* widget,
37
                                GdkEventButton* event);
38
static gboolean gtk_environment_view_mouse_move(GtkWidget* widget,
39
                                GdkEventMotion* event);
40
static gboolean gtk_environment_view_key_press(GtkWidget* widget,
41
                                GdkEventKey* event);
42

    
43

    
44
static void draw_world(GtkEnvironmentView* view, GdkDrawable* drawable, GdkGC* gc, world_t* world);
45
static void draw_robot(GtkEnvironmentView* view, GdkDrawable* drawable, GdkGC* gc, 
46
                        float x, float y, float angle, int selected);
47

    
48
static void to_world_coordinates(GtkEnvironmentView* view, int x, int y, float* world_x, float* world_y);
49
static void to_window_coordinates(GtkEnvironmentView* view, float x, float y, int* win_x, int* win_y);
50
static void to_window_scale(GtkEnvironmentView* view, float v, int* win_v);
51

    
52

    
53
GtkType gtk_environment_view_get_type(void)
54
{
55
        static GtkType environment_view_type = 0;
56
        if (!environment_view_type)
57
        {
58
                static const GtkTypeInfo environment_view_info =
59
                {
60
                        "GtkEnvironmentView",
61
                        sizeof(GtkEnvironmentView),
62
                        sizeof(GtkEnvironmentViewClass),
63
                        (GtkClassInitFunc)gtk_environment_view_class_init,
64
                        (GtkObjectInitFunc)gtk_environment_view_init,
65
                        NULL, NULL, (GtkClassInitFunc)NULL
66
                };
67

    
68
                environment_view_type = gtk_type_unique(GTK_TYPE_WIDGET,
69
                        &environment_view_info);
70
        }
71

    
72
        return environment_view_type;
73
}
74

    
75
static void gtk_environment_view_class_init(GtkEnvironmentViewClass* environmentClass)
76
{
77
        GtkObjectClass* object_class;
78
        GtkWidgetClass* widget_class;
79

    
80
        object_class = (GtkObjectClass*)environmentClass;
81
        widget_class = (GtkWidgetClass*)environmentClass;
82

    
83
        parent_class = gtk_type_class(gtk_widget_get_type());
84

    
85
        object_class->destroy = gtk_environment_view_destroy;
86

    
87
        widget_class->realize = gtk_environment_view_realize;
88
        widget_class->expose_event = gtk_environment_view_expose;
89
        widget_class->size_request = gtk_environment_view_size_request;
90
        widget_class->size_allocate = gtk_environment_view_size_allocate;
91
        widget_class->button_press_event = gtk_environment_view_mouse_down;
92
        widget_class->button_release_event = gtk_environment_view_mouse_release;
93
        widget_class->motion_notify_event = gtk_environment_view_mouse_move;
94
        widget_class->key_press_event = gtk_environment_view_key_press;
95
}
96

    
97
GtkWidget* gtk_environment_view_new(void)
98
{
99
        GtkEnvironmentView* view;
100
        view = gtk_type_new(gtk_environment_view_get_type());
101

    
102
        return GTK_WIDGET(view);
103
}
104

    
105
static void gtk_environment_view_init(GtkEnvironmentView* view)
106
{
107
        view->topLeftX = -2.0;
108
        view->topLeftY = -1.3;
109
        view->scale = 2.0 / 800.0;
110
        view->width = -1;
111
        view->height = -1;
112
        view->selected_robot = -1;
113

    
114
        GTK_WIDGET_SET_FLAGS(&(view->widget), GTK_DOUBLE_BUFFERED | GTK_CAN_FOCUS);
115

    
116
        view->mouseDown = 0;
117
}
118

    
119
static void gtk_environment_view_destroy(GtkObject* object)
120
{
121
        GtkEnvironmentView* view;
122
        g_return_if_fail(object != NULL);
123
        g_return_if_fail(GTK_IS_ENVIRONMENT_VIEW(object));
124

    
125
        view = GTK_ENVIRONMENT_VIEW(object);
126

    
127
        if (GTK_OBJECT_CLASS(parent_class)->destroy)
128
                (* GTK_OBJECT_CLASS(parent_class)->destroy) (object);
129
}
130

    
131
static void gtk_environment_view_realize(GtkWidget* widget)
132
{
133
        GtkEnvironmentView* view;
134
        GdkWindowAttr attributes;
135
        gint attributes_mask;
136

    
137
        g_return_if_fail( widget != NULL);
138
        g_return_if_fail( GTK_IS_ENVIRONMENT_VIEW(widget));
139

    
140
        GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
141
        view = GTK_ENVIRONMENT_VIEW(widget);
142

    
143
        attributes.x = widget->allocation.x;
144
        attributes.y = widget->allocation.y;
145
        attributes.width = widget->allocation.width;
146
        attributes.height = widget->allocation.height;
147
        attributes.wclass = GDK_INPUT_OUTPUT;
148
        attributes.window_type = GDK_WINDOW_CHILD;
149
        attributes.event_mask = gtk_widget_get_events(widget) |
150
                GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
151
                GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
152
                GDK_POINTER_MOTION_HINT_MASK;
153
        attributes.visual = gtk_widget_get_visual(widget);
154
        attributes.colormap = gtk_widget_get_colormap(widget);
155

    
156
        attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
157
        widget->window = gdk_window_new(widget->parent->window, &attributes,
158
                        attributes_mask);
159
        gdk_window_set_user_data(widget->window, widget);
160
        widget->style = gtk_style_attach(widget->style, widget->window);
161
        gtk_style_set_background(widget->style, widget->window,
162
                        GTK_STATE_NORMAL);
163
}
164

    
165
static void gtk_environment_view_size_request(GtkWidget* widget, 
166
                GtkRequisition* req)
167
{
168
        if (req->width < ENVIRONMENT_VIEW_MIN_WIDTH)
169
                req->width = ENVIRONMENT_VIEW_MIN_WIDTH;
170
        if (req->height < ENVIRONMENT_VIEW_MIN_HEIGHT)
171
                req->height = ENVIRONMENT_VIEW_MIN_HEIGHT;
172
}
173

    
174
static void gtk_environment_view_size_allocate(GtkWidget* widget, GtkAllocation* a)
175
{
176
        GtkEnvironmentView* view;
177

    
178
        g_return_if_fail(widget != NULL);
179
        g_return_if_fail(GTK_IS_ENVIRONMENT_VIEW(widget));
180
        g_return_if_fail(a != NULL);
181

    
182
        view = GTK_ENVIRONMENT_VIEW(widget);
183
        widget->allocation = *a;
184

    
185
        view->width = a->width;
186
        view->height = a->height;
187

    
188
        if (GTK_WIDGET_REALIZED(widget))
189
        {
190
                gdk_window_move_resize(widget->window, a->x, a->y, a->width, a->height);
191
        }
192
}
193

    
194
static gboolean gtk_environment_view_expose(GtkWidget* widget,
195
                GdkEventExpose* event)
196
{
197
        GtkEnvironmentView* view;
198

    
199
        if (widget == NULL || !GTK_IS_ENVIRONMENT_VIEW(widget) || event == NULL)
200
                return FALSE;
201
        if (event->count > 0)
202
                return FALSE;
203
        
204
        view = GTK_ENVIRONMENT_VIEW(widget);
205

    
206
        draw_world(view, widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], &world);
207
        
208
        robot_iterator_reset();
209
        Robot* r;
210
        while ((r = robot_iterator_next()) != NULL) {
211
                if (view->mouseDown && view->selected_robot == r->id)
212
                {
213
                        float x, y;
214
                        to_world_coordinates(view, view->mouseX, view->mouseY, &x, &y);
215
                        draw_robot(view, widget->window, 
216
                                widget->style->fg_gc[GTK_WIDGET_STATE(widget)], 
217
                    x, y, r->pose.theta, 1);
218
                }
219
                else
220
                        draw_robot(view, widget->window, 
221
                                widget->style->fg_gc[GTK_WIDGET_STATE(widget)], 
222
                    r->pose.x, r->pose.y, r->pose.theta, (view->selected_robot == r->id));
223
    }
224

    
225
        return FALSE;
226
}
227

    
228
static gboolean gtk_environment_view_mouse_down(GtkWidget* widget,
229
                                GdkEventButton* event)
230
{
231
        GtkEnvironmentView* view;
232
        Robot* r;
233
        float closestdist;
234
        int closest;
235
        float x, y;
236

    
237
        if (widget == NULL || !GTK_IS_ENVIRONMENT_VIEW(widget) || event == NULL)
238
                return FALSE;
239

    
240
        view = GTK_ENVIRONMENT_VIEW(widget);
241

    
242
        if (event->button != 1)
243
                return FALSE;
244

    
245
        view->mouseDownX = event->x;
246
        view->mouseDownY = event->y;
247
        view->mouseX = event->x;
248
        view->mouseY = event->y;
249
        view->mouseDown = 1;
250
        
251
        closest = -1;
252
        closestdist = 10e16;
253
        to_world_coordinates(view, event->x, event->y, &x, &y);
254
        robot_iterator_reset();
255
        while ((r = robot_iterator_next()) != NULL)
256
        {
257
                float dx = r->pose.x - x;
258
                float dy = r->pose.y - y;
259
                float dist = sqrt(dx * dx + dy * dy);
260
                if (dist < closestdist)
261
                {
262
                        closestdist = dist;
263
                        closest = r->id;
264
                }
265
        }
266

    
267
        view->selected_robot = closest;
268

    
269
        return FALSE;
270
}
271

    
272
static gboolean gtk_environment_view_mouse_release(GtkWidget* widget,
273
                                GdkEventButton* event)
274
{
275
        GtkEnvironmentView* view;
276
        Robot* r;
277

    
278
        if (widget == NULL || !GTK_IS_ENVIRONMENT_VIEW(widget) || event == NULL)
279
                return FALSE;
280

    
281
        view = GTK_ENVIRONMENT_VIEW(widget);
282

    
283
        if (event->button != 1 && event->button != 3)
284
                return FALSE;
285
        
286
        if (event->button == 1)
287
        {
288
                view->mouseDown = 0;
289
                robots_pause();
290
                r = robot_get(view->selected_robot);
291
                if (r != NULL)
292
                        to_world_coordinates(view, view->mouseX, view->mouseY,
293
                                &r->pose.x, &r->pose.y);
294
                robots_resume();
295
        }
296
        view->mouseX = event->x;
297
        view->mouseY = event->y;
298
        /*w = abs(view->mouseX - view->mouseDownX);
299
        h = abs(view->mouseY - view->mouseDownY);
300
        
301
        // if the box is big, zoom to the box
302
        if (event->button == 1 && (w >= 10 || h >= 10))
303
        {
304
                // we need to make sure the box is to scale
305
                // scale to height
306
                if (fabs((double)w / h) <= fabs((double)view->width / view->height))
307
                {
308
                        w = (int)((double)view->width / view->height * h);
309
                        if (view->mouseX < view->mouseDownX)
310
                                view->mouseX = view->mouseDownX - w;
311
                        else
312
                                view->mouseX = view->mouseDownX + w;
313
                }
314
                // scale to width
315
                else
316
                {
317
                        h = (int)((double)view->height / view->width * w);
318
                        if (view->mouseY < view->mouseDownY)
319
                                view->mouseY = view->mouseDownY - h;
320
                        else
321
                                view->mouseY = view->mouseDownY + h;
322
                }
323
        
324
                x1 = view->mouseX * view->scale + view->topLeftX;
325
                y1 = view->mouseY * view->scale + view->topLeftY;
326
                x2 = view->mouseDownX * view->scale + view->topLeftX;
327
                y2 = view->mouseDownY * view->scale + view->topLeftY;
328
                if (x2 < x1)
329
                        x1 = x2;
330
                if (y2 < y1)
331
                        y1 = y2;
332

333
                view->scale = (w * view->scale) / view->width;
334
                view->topLeftX = x1;
335
                view->topLeftY = y1;
336
        }
337
        else
338
        {
339
                x1 = view->mouseX * view->scale + view->topLeftX;
340
                y1 = view->mouseY * view->scale + view->topLeftY;
341

342
                // zoom in on left click, out on right click
343
                if (event->button == 1)
344
                        view->scale /= 2.0;
345
                else
346
                        view->scale *= 2.0;
347
                view->topLeftX = x1 - view->width / 2 * view->scale;
348
                view->topLeftY = y1 - view->height / 2 * view->scale;
349
        }*/
350

    
351
        return FALSE;
352
}
353

    
354
static gboolean gtk_environment_view_mouse_move(GtkWidget* widget,
355
                                GdkEventMotion* event)
356
{
357
        GtkEnvironmentView* view;
358

    
359
        if (widget == NULL || !GTK_IS_ENVIRONMENT_VIEW(widget) || event == NULL)
360
                return FALSE;
361

    
362
        view = GTK_ENVIRONMENT_VIEW(widget);
363

    
364
        view->mouseX = event->x;
365
        view->mouseY = event->y;
366

    
367
        if (!view->mouseDown)
368
                return FALSE;
369

    
370
        // we need to make sure the new view is to scale
371
        /*int w = abs(view->mouseX - view->mouseDownX);
372
        int h = abs(view->mouseY - view->mouseDownY);
373
        // scale to height
374
        if (fabs((double)w / h) <= fabs((double)view->width / view->height))
375
        {
376
                w = (int)((double)view->width / view->height * h);
377
                if (view->mouseX < view->mouseDownX)
378
                        view->mouseX = view->mouseDownX - w;
379
                else
380
                        view->mouseX = view->mouseDownX + w;
381
        }
382
        // scale to width
383
        else
384
        {
385
                h = (int)((double)view->height / view->width * w);
386
                if (view->mouseY < view->mouseDownY)
387
                        view->mouseY = view->mouseDownY - h;
388
                else
389
                        view->mouseY = view->mouseDownY + h;
390
        }*/
391

    
392
        gtk_widget_queue_draw_area(widget, 0, 0, view->width, view->height);
393

    
394
        return FALSE;
395
}
396

    
397
gboolean gtk_environment_view_key_press(GtkWidget* widget,
398
                                GdkEventKey* event)
399
{
400
        GtkEnvironmentView* view;
401

    
402
        if (widget == NULL || !GTK_IS_ENVIRONMENT_VIEW(widget) || event == NULL)
403
                return FALSE;
404

    
405
        view = GTK_ENVIRONMENT_VIEW(widget);
406

    
407
        if (event->keyval == GDK_Delete)
408
        {
409
                printf("Deleting.\n");
410
                robots_pause();
411
                if (view->selected_robot != -1)
412
                {
413
                        robot_destroy(view->selected_robot);
414
                }
415
                robots_resume();
416
        }
417
        return FALSE;
418
}
419

    
420
void gtk_environment_view_refresh(GtkWidget* view)
421
{
422
        gdk_threads_enter();
423
        gtk_widget_queue_draw(view);
424
        gdk_threads_leave();
425
}
426

    
427
void to_world_coordinates(GtkEnvironmentView* view, int x, int y, float* world_x, float* world_y)
428
{
429
        double leftx = world.win.p1.x;
430
        double upy = world.win.p1.x;
431
        double xwidth = world.win.p2.x - leftx;
432
        double yheight = world.win.p2.y - upy;
433
        int width = view->width, height = view->height;
434
        if (xwidth / width < yheight / height)
435
                width = (int)(height / yheight * xwidth);
436
        else
437
                height = (int)(width / xwidth * yheight);
438

    
439
        *world_x = (float)x / width * xwidth + leftx;
440
        *world_y = (float)y / height * yheight + upy;
441
}
442

    
443
void to_window_coordinates(GtkEnvironmentView* view, float x, float y, int* win_x, int* win_y)
444
{
445
        double leftx = world.win.p1.x;
446
        double upy = world.win.p1.x;
447
        double xwidth = world.win.p2.x - leftx;
448
        double yheight = world.win.p2.y - upy;
449
        int width = view->width, height = view->height;
450
        if (xwidth / width < yheight / height)
451
                width = (int)(height / yheight * xwidth);
452
        else
453
                height = (int)(width / xwidth * yheight);
454

    
455
        *win_x = (int)((x - leftx) / xwidth * width);
456
        *win_y = (int)((y - upy) / yheight * height);
457
}
458

    
459
void to_window_scale(GtkEnvironmentView* view, float v, int* win_v)
460
{
461
        double leftx = world.win.p1.x;
462
        double upy = world.win.p1.x;
463
        double xwidth = world.win.p2.x - leftx;
464
        double yheight = world.win.p2.y - upy;
465
        int width = view->width, height = view->height;
466
        if (xwidth / width < yheight / height)
467
                width = (int)(height / yheight * xwidth);
468
        else
469
                height = (int)(width / xwidth * yheight);
470
        
471
        *win_v = (int)(v / xwidth * width);
472
}
473

    
474
static void draw_world(GtkEnvironmentView* view, GdkDrawable* drawable, GdkGC* gc, world_t* world)
475
{
476
        int i, j;
477
        GdkPoint* points;
478
        poly_t* p;
479
        circle_t* c;
480
        int x, y, r;
481

    
482
        if (!drawable || !gc || !world)
483
                return;
484

    
485
        for (i = 0 ; i < world->max_objs; i++)
486
        {
487
                object_t* o = &(world->objs[i]);
488
                switch (o->id)
489
                {
490
                        case ID_POLY:
491
                                p = (poly_t*)o->props;
492
                                points = (GdkPoint*)malloc(sizeof(GdkPoint) * p->num_pts);
493
                                for (j = 0; j < p->num_pts; j++)
494
                                        to_window_coordinates(view, p->pts[j].x, p->pts[j].y, &(points[j].x), &(points[j].y));
495
                                switch(p->type)
496
                                {
497
                                        case POLY_CONNECTED:
498
                                                gdk_draw_polygon(drawable, gc, 1, points, p->num_pts);
499
                                                break;
500
                                        default:
501
                                                fprintf(stderr, "Detected unexpected type of polygon.\n");
502
                                                break;
503
                                }
504
                                free(points);
505
                                break;
506
                        case ID_CIRCLE:
507
                                c = (circle_t*)o->props;
508
                                to_window_scale(view, c->radius, &r);
509
                                to_window_coordinates(view, c->center.x, c->center.y, &x, &y);
510
                                gdk_draw_arc(drawable, gc, 1, 
511
                                                        (int)(x - (float)r / 2),
512
                                                        (int)(y - (float)r / 2), 
513
                                                        r, r, 0, 360*64);
514
                                break;
515
                        // no object
516
                        case ID_NULL:
517
                                break;
518
                        default:
519
                                fprintf(stderr, "Unexpected object type %d in world.\n", o->id);
520
                                break;
521
                }
522
        }
523
}
524

    
525
static void draw_robot(GtkEnvironmentView* view, GdkDrawable* drawable, GdkGC* gc, 
526
                        float x, float y, float angle, int selected)
527
{
528
        int rx, ry;
529
        int rox, roy;
530
        if (!drawable || !gc)
531
                return;
532

    
533
        selected = 0;
534

    
535
        to_window_scale(view, ROBOT_DIAMETER, &rx);
536
        to_window_scale(view, ROBOT_DIAMETER, &ry);
537
        to_window_coordinates(view, x, y, &rox, &roy);
538

    
539
        if (selected)
540
                gdk_gc_set_fill(gc, GDK_TILED);
541
        gdk_draw_arc(drawable, gc, selected, (int)(rox - (float)rx / 2), (int)(roy - (float)ry / 2), 
542
                rx, ry, 0, 360*64);
543
        if (selected)
544
                gdk_gc_set_fill(gc, GDK_SOLID);
545

    
546
        gdk_draw_line(drawable, gc, rox, roy, 
547
                rox + (rx / 1.8) * cos(-angle),
548
                roy - (ry / 1.8) * sin(-angle));
549
}
550