#include #include #include "bertl.h" static double mouse_x = 0.0; static double mouse_y = 0.0; static gboolean mouse_down = FALSE; static void circle_path(cairo_t *cr, _point_t p, double r) { cairo_arc(cr, p.x, p.y, r, 0, 2 * M_PI); } static void rect_path(cairo_t *cr, _rect_t r) { cairo_rectangle(cr, r.o.x, r.o.y, r.s.w, r.s.h); } void _move_to(cairo_t *cr, _point_t p) { cairo_move_to(cr, p.x, p.y); } void _line_to(cairo_t *cr, _point_t p) { cairo_line_to(cr, p.x, p.y); } void _curve_to(cairo_t *cr, _point_t c1, _point_t c2, _point_t p) { cairo_curve_to(cr, c1.x, c1.y, c2.x, c2.y, p.x, p.y); } show_point(cairo_t *cr, _point_t p, float r, float g, float b, char *str) { cairo_set_source_rgba(cr, r, g, b, 0.6); cairo_set_line_width(cr, 1.0); circle_path(cr, p, 3.0); cairo_stroke(cr); cairo_set_source_rgba(cr, r, g, b, 0.3); cairo_move_to(cr, p.x+5, p.y); cairo_show_text(cr, str); cairo_stroke(cr); } show_axis(cairo_t *cr, _point_t p, float r, float g, float b, float w, float h) { _point_t ap[4]; ap[0] = _move(p, _size(-w, 0)); ap[1] = _move(p, _size( w, 0)); ap[2] = _move(p, _size( 0, h)); ap[3] = _move(p, _size( 0,-h)); cairo_set_source_rgba(cr, r, g, b, 0.6); cairo_set_line_width(cr, 1.0); _move_to(cr, ap[0]); _line_to(cr, ap[1]); cairo_stroke(cr); _move_to(cr, ap[2]); _line_to(cr, ap[3]); cairo_stroke(cr); } /* p1 *----+ ep[0] p1 *----+ ep[0] | | * * hp[0] | | | | * hp[1] * hp[1] | | + ep[1,2] ep[1] + ep[2] | | * hp[4] hp[4] * | | | | * hp[5] * | | ep[3] +----* p2 ep[3] +----* p2 p1 *----+ ep[0] | * hp[0] | | * hp[1] hp[3] mp[1] hp[2] | ep[2] +----*---------*---------*----+ ep[1] | hp[4] * | | hp[5] * | ep[3] +----* p2 */ static int calc_connect_ep(_point_t p1, _point_t p2, _point_t *ep, double m) { _size_t d = _delta(p1, p2); double as, bs, cs, ms; int i, mode = 0; double l = sqrt(d.w*d.w + d.h*d.h); ms = MIN(m, l); as = 2*ms; if (d.w > fabs(d.h)) as = MAX(0, as - 2*ms*(M_PI/4-atan(fabs(d.h)/d.w))); ep[0] = _move(p1, _size(as, 0)); ep[3] = _move(p2, _size(-as, 0)); if (d.w < 0) { double xs = ms*0.58; double ys = d.h*0.113; ep[1] = _move(p1, _size(xs, ys)); ep[2] = _move(p2, _size(-xs, -ys)); mode = 1; } return mode; } static int calc_connect_hp(_point_t p1, _point_t p2, _point_t *ep, _point_t *hp, int mode, double m) { _size_t d = _delta(p1, p2); double as, bs, cx, cy, cz, ms; hp[0] = _midp(p1, ep[0]); hp[2] = _midp(p1, p2); hp[4] = _midp(p2, ep[3]); hp[1] = _midp(hp[0], hp[2]); hp[3] = _midp(hp[4], hp[2]); return mode; } #define MIMAX(s, a, b) (((s)>0)?MIN(a,b):MAX(a,b)) #define MAMIN(s, a, b) (((s)>0)?MAX(a,b):MIN(a,b)) static int calc_connect_sp(_point_t p1, _point_t p2, _point_t *ep, _point_t *hp, _point_t *sp, int mode, double m) { _size_t d = _delta(p1, p2); double as, bs, cx, cy, cz, ms; double l = sqrt(d.w*d.w + d.h*d.h); ms = MIN(m, l/2); /* remove symmetry */ double ds = sign(d.h); sp[0] = _point(ep[1].x, hp[0].y); sp[1] = _mixp(p1, sp[0], 0.333); sp[2] = _mixp(ep[1], sp[0], 0.333); sp[9] = _point(ep[2].x, hp[4].y); sp[8] = _mixp(p2, sp[9], 0.333); sp[7] = _mixp(ep[2], sp[9], 0.333); double dx = MIN(-d.w, 2*ms); double dy = ep[1].y - sp[2].y + ds*dx/2; dy = MIMAX(ds, d.h/2, dy/2); sp[3] = _move(ep[1], _size(0, dy)); sp[4] = _move(hp[1], _size(dx/2, ds*dx)); sp[4].x = MIN(sp[4].x, sp[3].x); sp[4].y = MIMAX(ds, sp[4].y, hp[2].y); sp[6] = _move(ep[2], _size(0, -dy)); sp[5] = _move(hp[3], _size(-dx/2, -ds*dx)); sp[5].x = MAX(sp[5].x, sp[6].x); sp[5].y = MAMIN(ds, sp[5].y, hp[2].y); ep[1].y += MAX(0, dy/3); ep[2].y -= MAX(0, dy/3); return mode; } static void draw_connect(cairo_t *cr, _point_t p1, _point_t p2, int type) { _point_t ep[4], hp[5], sp[10]; int mode; mode = calc_connect_ep(p1, p2, ep, 60); show_point(cr, p1, 0.8,0.0,0, "p1"); show_point(cr, p2, 0.0,0.8,0, "p2"); show_point(cr, ep[0], 0.6,0.0,0, "ep0"); show_point(cr, ep[3], 0.0,0.6,0, "ep3"); if (mode == 1) { calc_connect_hp(p1, p2, ep, hp, mode, 60); show_point(cr, hp[0], 0.6,0,0.6, "hp0"); show_point(cr, hp[2], 0.6,0,0.6, "hp2"); show_point(cr, hp[4], 0.6,0,0.6, "hp4"); show_point(cr, hp[1], 0,0.6,0.6, "hp1"); show_point(cr, hp[3], 0,0.6,0.6, "hp3"); cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.3); cairo_set_line_width(cr, 1.0); _move_to(cr, hp[0]); _line_to(cr, hp[4]); cairo_stroke(cr); calc_connect_sp(p1, p2, ep, hp, sp, mode, 60); show_point(cr, ep[1], 0,0.4,0.4, "ep1"); show_point(cr, ep[2], 0,0.4,0.4, "ep2"); show_axis(cr, ep[1], 0.6, 0.5, 0.0, 50, 100); show_axis(cr, ep[2], 0.6, 0.5, 0.0, 50, 100); show_point(cr, sp[0], 0.4,0,0.4, "sp0"); show_point(cr, sp[1], 0,0.4,0.4, "sp1"); show_point(cr, sp[2], 0.4,0,0.4, "sp2"); show_point(cr, sp[9], 0.4,0,0.4, "sp9"); show_point(cr, sp[8], 0,0.4,0.4, "sp8"); show_point(cr, sp[7], 0.4,0,0.4, "sp7"); show_point(cr, sp[3], 0,0.4,0.4, "sp3"); show_point(cr, sp[4], 0.4,0,0.4, "sp4"); show_point(cr, sp[5], 4.0,0.4,0.4, "sp5"); show_point(cr, sp[6], 4.0,0.4,0.4, "sp6"); show_point(cr, sp[7], 4.0,0.4,0.4, "sp7"); show_point(cr, sp[8], 4.0,0.4,0.4, "sp8"); cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.6); cairo_set_line_width(cr, 1.0); _move_to(cr, p1); _curve_to(cr, sp[1], sp[2], ep[1]); _curve_to(cr, sp[3], sp[4], hp[2]); _curve_to(cr, sp[5], sp[6], ep[2]); _curve_to(cr, sp[7], sp[8], p2); cairo_stroke(cr); } else { cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.6); cairo_set_line_width(cr, 1.0); _move_to(cr, p1); _curve_to(cr, ep[0], ep[3], p2); cairo_stroke(cr); } } static gboolean expose_event (GtkWidget *widget, GdkEventExpose *event) { cairo_t *cr = gdk_cairo_create(widget->window); double width = widget->allocation.width; double height = widget->allocation.height; g_assert(cr != NULL); _point_t p1 = _point(250, 250); _point_t p2 = _point(mouse_x, mouse_y); cairo_save(cr); cairo_translate(cr, 0.5, 0.5); draw_connect(cr, p1, p2, 0); cairo_restore(cr); return FALSE; } static gboolean button_press_event (GtkWidget *widget, GdkEventButton *event) { mouse_down = TRUE; gtk_widget_queue_draw(widget); return TRUE; } static gboolean button_release_event (GtkWidget *widget, GdkEventButton *event) { mouse_down = FALSE; gtk_widget_queue_draw(widget); return TRUE; } static gboolean motion_notify_event (GtkWidget *widget, GdkEventMotion *event) { int x, y; GdkModifierType state; if (event->is_hint) gdk_window_get_pointer (event->window, &x, &y, &state); else { x = event->x; y = event->y; state = event->state; } mouse_x = x; mouse_y = y; /* if (state & GDK_BUTTON1_MASK && pixmap != NULL) draw_brush (widget, x, y); */ gtk_widget_queue_draw(widget); return TRUE; } static void destroy (GtkWidget *widget, gpointer data) { gtk_main_quit(); } int main(int argc, char *argv[]) { GtkWidget *window, *drawing_area; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_resize (GTK_WINDOW (window), 600, 600); gtk_widget_show(window); drawing_area = gtk_drawing_area_new(); gtk_signal_connect(GTK_OBJECT(drawing_area), "expose_event", (GtkSignalFunc)expose_event, NULL); /* gtk_signal_connect(GTK_OBJECT(drawing_area),"configure_event", (GtkSignalFunc)configure_event, NULL); */ gtk_signal_connect(GTK_OBJECT(drawing_area), "motion_notify_event", (GtkSignalFunc)motion_notify_event, NULL); gtk_signal_connect(GTK_OBJECT(drawing_area), "button_press_event", (GtkSignalFunc)button_press_event, NULL); gtk_signal_connect(GTK_OBJECT(drawing_area), "button_release_event", (GtkSignalFunc)button_release_event, NULL); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL); gtk_widget_set_events(drawing_area, GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); gtk_widget_show(drawing_area); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(drawing_area)); gtk_main(); return 0; }