diff options
Diffstat (limited to 'draw.cpp')
-rw-r--r-- | draw.cpp | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/draw.cpp b/draw.cpp new file mode 100644 index 0000000..3a09628 --- /dev/null +++ b/draw.cpp @@ -0,0 +1,307 @@ +#include "draw.h" +#include "edit.h" +#include "level.h" +#include "geometry.h" +#include <math.h> +#include <gtk/gtk.h> +#include <cairo/cairo.h> +#include <stdlib.h> + + +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 *string; + + + 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); + + if(step > 0.005) { + if(step > 0.5) + string = g_strdup_printf("%i", (int)rint(d)); + else + string = g_strdup_printf("%.*f", -(int)floor(log10(step*1.1)), d+step/10); + + cairo_move_to(cr, d+1/scale, rect->y+11/scale); + cairo_show_text(cr, string); + + g_free(string); + } + } + + 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); + + if(step > 0.005) { + if(step > 0.5) + string = g_strdup_printf("%i", (int)rint(d)); + else + string = g_strdup_printf("%.*f", -(int)floor(log10(step*1.1)), d+step/10); + + cairo_move_to(cr, rect->x+3/scale, d+11/scale); + cairo_show_text(cr, string); + + g_free(string); + } + } + + cairo_stroke(cr); + + step *= 10; + } +} + +static void polygon2path(cairo_t *cr, const POLYGON *polygon, const RECTANGLE *rect, gboolean close) { + int i; + POLYGON polygon2 = {0, NULL}; + + // no vertices + if(polygon->nVertices == 0) return; + + if(rect) + simplifyPolygon(polygon, rect, &polygon2); + else + polygon2 = *polygon; + + if(polygon2.nVertices == 0) return; + + cairo_new_sub_path(cr); + + for(i = 0; i < polygon2.nVertices; i++) { + cairo_line_to(cr, polygon2.vertices[i].x, polygon2.vertices[i].y); + } + + if(close) + cairo_close_path(cr); + + if(rect && polygon2.vertices) + free(polygon2.vertices); +} + +gboolean drawTopView(GtkWidget *widget, GdkEventExpose *event, gpointer data) { + cairo_t *cr; + VERTEX v1 = {-1, -1}, v2 = {widget->allocation.width+1, widget->allocation.height+1}; + RECTANGLE rect; + gboolean vertexOk; + static GdkPixmap *pixmap = NULL; + static double lastImageWidth = 0.0, lastImageHeight = 0.0; + static gint lastWidth = 0.0, lastHeight = 0.0; + static int lastEditMode = 0; + int i; + + + if(getLevel() == NULL) return FALSE; + + viewToImage(&v1); + viewToImage(&v2); + + rect.x = v1.x; + rect.y = v1.y; + rect.width = v2.x-v1.x; + rect.height = v2.y-v1.y; + + if(pixmap == NULL || fabs(lastImageWidth - getImageWidth()) >= 0.000001 || fabs(lastImageHeight - getImageHeight()) >= 0.000001 || + lastWidth != widget->allocation.width || lastHeight != widget->allocation.height || lastEditMode != getEditMode() || 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; + lastEditMode = getEditMode(); + repaint = FALSE; + + + cr = gdk_cairo_create(GDK_DRAWABLE(pixmap)); + + cairo_translate(cr, getImageWidth()/2-xTranslate, getImageHeight()/2-yTranslate); + cairo_scale(cr, scale, scale); + + cairo_set_line_width(cr, 1.0/scale); + cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); + + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_paint(cr); + + drawGrid(cr, &rect); + + for(i = 0; i < getLevel()->nRooms; i++) { + if(&getLevel()->rooms[i] != getActiveRoom()) { + polygon2path(cr, &getLevel()->rooms[i].polygon, &rect, TRUE); + } + } + + 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) { + polygon2path(cr, &getActiveRoom()->polygon, &rect, TRUE); + + 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_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); + 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); + + 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); + + if(getHoveredRoom() != NULL && getHoveredRoom() != getActiveRoom() && + (getEditMode() == EDIT_MODE_VIEW || getEditMode() == EDIT_MODE_SELECTED)) + { + polygon2path(cr, &getHoveredRoom()->polygon, &rect, TRUE); + + cairo_set_source_rgba(cr, 0.0, 0.7, 1.0, 0.7); + cairo_stroke(cr); + } + else if(getEditMode() == EDIT_MODE_ADD) { + polygon2path(cr, &getActiveRoom()->polygon, NULL, FALSE); + + if(isPolygonOk(&getActiveRoom()->polygon)) + cairo_set_source_rgba(cr, 0.0, 0.7, 1.0, 0.2); + else + cairo_set_source_rgba(cr, 1.0, 0.3, 0.3, 0.2); + cairo_fill_preserve(cr); + + + if(getActiveRoom()->polygon.nVertices && getHoveredVertex()) { + vertexOk = isVertexOk(getHoveredVertex()); + + if(vertexOk) + cairo_line_to(cr, getHoveredVertex()->x, getHoveredVertex()->y); + } + + cairo_set_line_width(cr, 2.0/scale); + cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_source_rgba(cr, 0.0, 0.7, 1.0, 0.7); + cairo_stroke(cr); + + if(getActiveRoom()->polygon.nVertices && getHoveredVertex() && !vertexOk) { + cairo_set_source_rgba(cr, 1.0, 0.3, 0.3, 0.7); + + i = getActiveRoom()->polygon.nVertices - 1; + cairo_move_to(cr, getActiveRoom()->polygon.vertices[i].x, getActiveRoom()->polygon.vertices[i].y); + cairo_line_to(cr, getHoveredVertex()->x, getHoveredVertex()->y); + + cairo_stroke(cr); + } + } + + cairo_destroy (cr); + + return FALSE; +} + + +double getScale() { + return scale; +} + +void setScale(double s) { + scale = MAX(0.005, MIN(s, 10000)); + 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].polygon.nVertices; j++) { + min = MIN(min, level->rooms[i].polygon.vertices[j].x); + max = MAX(max, level->rooms[i].polygon.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].polygon.nVertices; j++) { + min = MIN(min, level->rooms[i].polygon.vertices[j].y); + max = MAX(max, level->rooms[i].polygon.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; +} |