summaryrefslogtreecommitdiffstats
path: root/draw.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'draw.cpp')
-rw-r--r--draw.cpp307
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;
+}