/*- * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 69): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. -Shteryana Shopova * ---------------------------------------------------------------------------- */ #include #include #include #include "cg.h" enum cg_line_method { CG_LINE_INLINE = 0, CG_LINE_SIMPLE = 1, CG_LINE_BRESENHAIM = 2, CG_LINE_PORTION = 3, }; static void cg_line_draw_simple(GdkDrawable *drawable, GdkGC *gc, gint x1_, gint y1_, gint x2_, gint y2_); static void cg_line_draw_bresenhaim(GdkDrawable *drawable, GdkGC *gc, gint x1_, gint y1_, gint x2_, gint y2_); static void cg_line_draw_portion(GdkDrawable *drawable, GdkGC *gc, gint x1_, gint y1_, gint x2_, gint y2_); static void cg_line_draw(enum cg_line_method method); /* Obligatory basic callbacks for the "Line" menu items. */ void cg_line_inline(GtkWidget *w, gpointer data) { g_message ("Hello, cg_line_inline!\n"); cg_line_draw(CG_LINE_INLINE); } void cg_line_simple(GtkWidget *w, gpointer data) { g_message ("Hello, cg_line_simple!\n"); cg_line_draw(CG_LINE_SIMPLE); } void cg_line_bresenhaim(GtkWidget *w, gpointer data) { g_message ("Hello, cg_line_bresenhaim!\n"); cg_line_draw(CG_LINE_BRESENHAIM); } void cg_line_portion(GtkWidget *w, gpointer data) { g_message ("Hello, cg_line_portion!\n"); cg_line_draw(CG_LINE_PORTION); } struct _cg_line cg_line; /* Refresh the area where changes were made. */ void cg_line_drawing_refresh(gint x1, gint y1, gint x2, gint y2) { GdkRectangle update_area; update_area.x = (x1 < x2 ? x1 : x2); update_area.y = (y1 < y2 ? y1 : y2); update_area.width = abs(x1 - x2 ) + 1; update_area.height = abs(y1 - y2) + 1; gtk_widget_draw(GTK_WIDGET(drawing_area), &update_area); } static gint cg_line_motion_notify_event(GtkWidget *widget, GdkEventMotion *event) { int x, y; GdkModifierType state; GtkWidget *toplevel; if (event->is_hint) gdk_window_get_pointer (event->window, &x, &y, &state); else { x = event->x; y = event->y; state = event->state; } if (cg_pixmap == NULL) return (TRUE); if (state & GDK_BUTTON1_MASK) { toplevel = gtk_widget_get_toplevel(GTK_WIDGET(drawing_area)); /* Clear previous line. */ (cg_line.draw_cb) (cg_pixmap, toplevel->style->white_gc, cg_line.X1, cg_line.Y1, cg_line.X2, cg_line.Y2); cg_line_drawing_refresh(cg_line.X1, cg_line.Y1, cg_line.X2, cg_line.Y2); } else toplevel = gtk_widget_get_toplevel(GTK_WIDGET(drawing_area)); cg_line.X2 = x; cg_line.Y2 = y; if (state & GDK_BUTTON1_MASK || state & GDK_BUTTON3_MASK) { (cg_line.draw_cb) (cg_pixmap, toplevel->style->fg_gc[GTK_WIDGET_STATE(widget)], cg_line.X1, cg_line.Y1, cg_line.X2, cg_line.Y2); cg_line_drawing_refresh(cg_line.X1, cg_line.Y1, cg_line.X2, cg_line.Y2); } return (TRUE); } static gint cg_line_button_release_event(GtkWidget *widget, GdkEventButton *event) { GtkWidget *toplevel; if (cg_pixmap == NULL || (event->button != 1 && event->button != 3)) return (TRUE); /* Unconnect the motion and the button release signals. */ g_signal_handler_disconnect(GTK_OBJECT(drawing_area), cg_line.motion_id); g_signal_handler_disconnect(GTK_OBJECT(drawing_area), cg_line.button_id); cg_line.X2 = event->x; cg_line.Y2 = event->y; toplevel = gtk_widget_get_toplevel(GTK_WIDGET(drawing_area)); (cg_line.draw_cb)(cg_pixmap, toplevel->style->fg_gc[GTK_WIDGET_STATE(widget)], cg_line.X1, cg_line.Y1, cg_line.X2, cg_line.Y2); cg_line_drawing_refresh(cg_line.X1, cg_line.Y1, cg_line.X2, cg_line.Y2); return (TRUE); } gint cg_line_button_press_event(GtkWidget *widget, GdkEventButton *event) { if (cg_pixmap == NULL || (event->button != 1 && event->button != 3)) return (TRUE); if (!cg_line.X1_set) { cg_line.X1 = event->x; cg_line.Y1 = event->y; cg_line.X1_set = TRUE; /* Unconnect the button press signal. */ g_signal_handler_disconnect(GTK_OBJECT(drawing_area), cg_line.button_id); /* Connect the motion and the button release signal handler. */ cg_line.button_id = g_signal_connect(GTK_OBJECT(drawing_area), "button_release_event", (GtkSignalFunc) cg_line_button_release_event, NULL); cg_line.motion_id = g_signal_connect(GTK_OBJECT(drawing_area), "motion_notify_event", (GtkSignalFunc) cg_line_motion_notify_event, NULL); } return (TRUE); } static void cg_line_draw(enum cg_line_method method) { bzero(&cg_line, sizeof(cg_line)); switch (method) { case CG_LINE_SIMPLE: cg_line.draw_cb = cg_line_draw_simple; break; case CG_LINE_BRESENHAIM: cg_line.draw_cb = cg_line_draw_bresenhaim; break; case CG_LINE_PORTION: cg_line.draw_cb = cg_line_draw_portion; break; default: cg_line.draw_cb = gdk_draw_line; break; } cg_line.button_id = g_signal_connect(GTK_OBJECT(drawing_area), "button_press_event", (GtkSignalFunc) cg_line_button_press_event, NULL); } /* Swap x and y axes if necessary. */ static gboolean cg_line_swap(gint *x1_, gint *y1_, gint *x2_, gint *y2_) { gint z; if (fabs((gdouble) (*y2_ - *y1_) / (gdouble) (*x2_ - *x1_)) <= 1.) return (FALSE); z = *x1_; *x1_ = *y1_; *y1_ = z; z = *x2_; *x2_ = *y2_; *y2_ = z; return (TRUE); } static void cg_line_draw_simple(GdkDrawable *drawable, GdkGC *gc, gint x1_, gint y1_, gint x2_, gint y2_) { gboolean swapped; gint i, inc_x, x_i, y_i; gdouble delta, y, inc_y; /* Swap if necessary and recalculate the delta. */ swapped = cg_line_swap(&x1_, &y1_, &x2_, &y2_); delta = fabs((gdouble) (y2_ - y1_) / (gdouble) (x2_ - x1_)); if ((i = (x2_ - x1_)) < 0) { i = abs(i); inc_x = -1; } else inc_x = 1; if ((y2_ - y1_) < 0) inc_y = 0 - delta; else inc_y = delta; for (x_i = x1_, y = y1_; i >= 0; i--, x_i += inc_x, y += inc_y) { y_i = (gint) y; if ((y - y_i) > 0.5) y_i += 1; else if ((y - y_i) < -0.5) y_i -= 1; if (swapped) gdk_draw_point(drawable, gc, y_i, x_i); else gdk_draw_point(drawable, gc, x_i, y_i); } } #define CG_LINE_TRANSLATE(delta, z) ((z) - (delta)) #define CG_LINE_REV_TRANSLATE(delta, z) ((z) + (delta)) static void cg_line_draw_bresenhaim(GdkDrawable *drawable, GdkGC *gc, gint x1_, gint y1_, gint x2_, gint y2_) { gboolean swapped; gint n, inc_x, inc_y, trans_x, trans_y, delta_i, x_i, y_i; /* Swap if necessary. */ swapped = cg_line_swap(&x1_, &y1_, &x2_, &y2_); /* Change to coordinate system with center (x1_, y1_). */ trans_x = x1_; trans_y = y1_; /* Translate the coordinates to the new system. */ x1_ = y1_ = 0; x2_ = CG_LINE_TRANSLATE(trans_x, x2_); y2_ = CG_LINE_TRANSLATE(trans_y, y2_); if ((n = x2_) < 0) { n = abs(n); inc_x = -1; } else inc_x = 1; if (y2_ < 0) inc_y = -1; else inc_y = 1; delta_i = 2 * abs(y2_) - abs(x2_); for (x_i = x1_, y_i = y1_; n >= 0; n--, x_i += inc_x) { if (swapped) { gdk_draw_point(drawable, gc, CG_LINE_REV_TRANSLATE(trans_y, y_i), CG_LINE_REV_TRANSLATE(trans_x, x_i)); } else { gdk_draw_point(drawable, gc, CG_LINE_REV_TRANSLATE(trans_x, x_i), CG_LINE_REV_TRANSLATE(trans_y, y_i)); } if (delta_i > 0) { y_i += inc_y; delta_i += 2 * abs(y2_) - 2 * abs(x2_); } else delta_i += 2 * abs(y2_); } } static void cg_line_draw_portion(GdkDrawable *drawable, GdkGC *gc, gint x1_, gint y1_, gint x2_, gint y2_) { gboolean swapped; gint n, inc_x, inc_y, trans_x, trans_y, x_i, x_i_end, y_i; /* Swap if necessary. */ swapped = cg_line_swap(&x1_, &y1_, &x2_, &y2_); /* Change to coordinate system with center (x1_, y1_). */ trans_x = x1_; trans_y = y1_; /* Translate the coordinates to the new system. */ x1_ = y1_ = 0; x2_ = CG_LINE_TRANSLATE(trans_x, x2_); y2_ = CG_LINE_TRANSLATE(trans_y, y2_); if (y2_ == 0) return; if ((n = y2_) < 0) { n = abs(n); inc_y = -1; } else inc_y = 1; if (x2_ < 0) inc_x = -1; else inc_x = 1; for (x_i = x1_, y_i = y1_; n >= 0; n--, y_i += inc_y, x_i = x_i_end + inc_x) { x_i_end = (x2_ * (2 * y_i + 1)) / (2 * y2_); if (swapped) { gdk_draw_line(drawable, gc, CG_LINE_REV_TRANSLATE(trans_y, y_i), CG_LINE_REV_TRANSLATE(trans_x, x_i), CG_LINE_REV_TRANSLATE(trans_y, y_i), CG_LINE_REV_TRANSLATE(trans_x, x_i_end)); } else { gdk_draw_line(drawable, gc, CG_LINE_REV_TRANSLATE(trans_x, x_i), CG_LINE_REV_TRANSLATE(trans_y, y_i), CG_LINE_REV_TRANSLATE(trans_x, x_i_end), CG_LINE_REV_TRANSLATE(trans_y, y_i)); } } }