#include "draw.h" #include "edit.h" #include "level.h" #include "geometry.h" #include #include #include #include static double scale = 100.0; static double xTranslate = 0.0, yTranslate = 0.0; static gboolean repaint = FALSE; static void drawGrid(cairo_t *cr, const RECTANGLE *rect) { double depth = log10(scale)-0.75; double depth2 = floor(depth); double step = pow(0.1, depth2); double d; int i; gchar buffer[20]; cairo_set_font_size(cr, 10.0/scale); for(i = 0; 0.4*(depth-depth2+i-1) < 0.5; i++) { d = MIN(0.4*(depth-depth2+i), 0.5); cairo_set_source_rgb(cr, d, d, d); for(d = rect->x - fmod(rect->x, step) - step; d <= rect->x+rect->width; d+=step) { cairo_move_to(cr, d, rect->y); cairo_line_to(cr, d, rect->y+rect->height); cairo_stroke(cr); if(step > 0.2) g_snprintf(buffer, sizeof(buffer), "%i", (int)rint(d)); else g_snprintf(buffer, sizeof(buffer), "%.*f", -(int)floor(log10(step*1.1)), d+step/10); buffer[sizeof(buffer)-1] = '\0'; cairo_move_to(cr, d+1/scale, rect->y+11/scale); cairo_show_text(cr, buffer); } for(d = rect->y - fmod(rect->y, step) - step; d <= rect->y+rect->height; d+=step) { cairo_move_to(cr, rect->x, d); cairo_line_to(cr, rect->x+rect->width, d); cairo_stroke(cr); if(step > 0.5) g_snprintf(buffer, sizeof(buffer), "%i", (int)rint(d)); else g_snprintf(buffer, sizeof(buffer), "%.*f", -(int)floor(log10(step*1.1)), d+step/10); buffer[sizeof(buffer)-1] = '\0'; cairo_move_to(cr, rect->x+3/scale, d+11/scale); cairo_show_text(cr, buffer); } step *= 10; } } static void room2path(cairo_t *cr, const ROOM *room, const RECTANGLE *rect) { int i; ROOM room2 = {0, NULL}; // no vertices if(room->nVertices == 0) return; if(rect) simplifyPolygon(room, rect, &room2); else room2 = *room; if(room2.nVertices == 0) return; cairo_new_sub_path(cr); for(i = 0; i < room2.nVertices; i++) { cairo_line_to(cr, room2.vertices[i].x, room2.vertices[i].y); } cairo_close_path(cr); if(rect && room2.vertices) free(room2.vertices); } gboolean drawTopView(GtkWidget *widget, GdkEventExpose *event, gpointer data) { cairo_t *cr; RECTANGLE rect = {-1, -1, widget->allocation.width+2, widget->allocation.height+2}; static GdkPixmap *pixmap = NULL; static double lastImageWidth = 0.0, lastImageHeight = 0.0; static gint lastWidth = 0.0, lastHeight = 0.0; int i; if(getLevel() == NULL) return FALSE; if(pixmap == NULL || lastImageWidth != getImageWidth() || lastImageHeight != getImageHeight() || lastWidth != widget->allocation.width || lastHeight != widget->allocation.height || repaint) { if(pixmap != NULL) g_object_unref(G_OBJECT(pixmap)); pixmap = gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1); lastImageWidth = getImageWidth(); lastImageHeight = getImageHeight(); lastWidth = widget->allocation.width; lastHeight = widget->allocation.height; repaint = FALSE; cr = gdk_cairo_create(GDK_DRAWABLE(pixmap)); cairo_set_source_rgb(cr, 0, 0, 0); cairo_rectangle(cr, 0, 0, widget->allocation.width, widget->allocation.height); cairo_fill(cr); cairo_translate(cr, getImageWidth()/2-xTranslate, getImageHeight()/2-yTranslate); cairo_scale(cr, scale, scale); cairo_device_to_user(cr, &rect.x, &rect.y); cairo_device_to_user_distance(cr, &rect.width, &rect.height); cairo_set_line_width(cr, 1.0/scale); cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER); drawGrid(cr, &rect); for(i = 0; i < getLevel()->nRooms; i++) { if(&getLevel()->rooms[i] != getActiveRoom()) room2path(cr, &getLevel()->rooms[i], &rect); } cairo_set_source_rgba(cr, 0.0, 0.7, 1.0, 0.3); cairo_fill_preserve(cr); cairo_set_source_rgba(cr, 0.0, 0.7, 1.0, 0.7); cairo_stroke(cr); if(getEditMode() == EDIT_MODE_SELECTED) { room2path(cr, getActiveRoom(), &rect); cairo_set_source_rgba(cr, 0.0, 0.7, 1.0, 0.2); cairo_fill_preserve(cr); cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.9); cairo_set_line_width(cr, 2.0/scale); cairo_stroke(cr); } cairo_destroy (cr); } gdk_draw_drawable(GDK_DRAWABLE(widget->window), widget->style->fg_gc[GTK_WIDGET_STATE(widget)], GDK_DRAWABLE(pixmap), 0, 0, 0, 0, -1, -1); if(getHoveredRoom() != NULL && getHoveredRoom() != getActiveRoom()) { cr = gdk_cairo_create(GDK_DRAWABLE(widget->window)); cairo_translate(cr, getImageWidth()/2-xTranslate, getImageHeight()/2-yTranslate); cairo_scale(cr, scale, scale); cairo_set_line_width(cr, 2.0/scale); cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER); room2path(cr, getHoveredRoom(), NULL); cairo_set_source_rgba(cr, 0.0, 0.7, 1.0, 0.7); cairo_stroke(cr); cairo_destroy (cr); } return FALSE; } double getScale() { return scale; } void setScale(double s) { scale = s; repaint = TRUE; } void imageToView(VERTEX *v) { v->x = v->x*scale+getImageWidth()/2-xTranslate; v->y = v->y*scale+getImageHeight()/2-yTranslate; } void viewToImage(VERTEX *v) { v->x = (v->x-getImageWidth()/2+xTranslate)/scale; v->y = (v->y-getImageHeight()/2+yTranslate)/scale; } double getImageWidth() { const LEVEL *level = getLevel(); double min = 0.0, max = 0.0; int i, j; if(level) { for(i = 0; i < level->nRooms; i++) { for(j = 0; j < level->rooms[i].nVertices; j++) { min = MIN(min, level->rooms[i].vertices[j].x); max = MAX(max, level->rooms[i].vertices[j].x); } } } return (max-min+10)*scale; } double getImageHeight() { const LEVEL *level = getLevel(); double min = 0.0, max = 0.0; int i, j; if(level) { for(i = 0; i < level->nRooms; i++) { for(j = 0; j < level->rooms[i].nVertices; j++) { min = MIN(min, level->rooms[i].vertices[j].y); max = MAX(max, level->rooms[i].vertices[j].y); } } } return (max-min+10)*scale; } double getXTranslate() { return xTranslate; } void setXTranslate(double x) { xTranslate = x; repaint = TRUE; } double getYTranslate() { return yTranslate; } void setYTranslate(double y) { yTranslate = y; repaint = TRUE; } void redraw() { repaint = TRUE; }