/*- * ---------------------------------------------------------------------------- * "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_ELLIPSE_INLINE = 0, CG_ELLIPSE_BRESENHAIM = 1, CG_ELLIPSE_CONIC_SECTION = 2, }; struct cg_ellipse { gdouble A_; gdouble B_; gdouble C_; gdouble D_; gdouble E_; gdouble F_; gdouble angle; gdouble Ox_; gdouble Oy_; } g_ellipse; typedef void (*cg_draw_ellipse_cb) (GdkDrawable *drawable, GdkGC *gc, struct cg_ellipse *ellipse); //static void cg_draw_ellipse_inline(GdkDrawable *drawable, GdkGC *gc, // struct cg_ellipse *ellipse); //static void cg_draw_ellipse_bresenheim(GdkDrawable *drawable, GdkGC *gc, // struct cg_ellipse *ellipse); //static void cg_draw_ellipse_conic(GdkDrawable *drawable, GdkGC *gc, // struct cg_ellipse *ellipse); //static void cg_ellipse_draw(enum cg_line_method method); void cg_ellipse_set_input(gpointer callback_data, guint callback_action, GtkWidget *menu_item) { if (GTK_CHECK_MENU_ITEM(menu_item)->active) { if (callback_action == 1) g_message("Ellipse - manual input\n"); else g_message("Ellipse - auto\n"); } } const char conic_text[] = "\ A conic section is a curve that can be represented with\n\ F(x, y) = A * x^2 + B * xy + C * y^2 + D * x + E * y + F = 0\n\ Enter values for A, B, C, D, E, F :\n"; void cg_ellipse_draw(GtkWidget *widget, GtkWidget *entry); static GtkWidget *dialog; static GtkWidget *float_entry[6]; static void cg_ellipse_manual_input(void) { gint i; GtkWidget *hbox, *button, *label; /* * Create a new dialog window for the scrolled window to be * packed into. */ dialog = gtk_dialog_new (); g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(gtk_widget_hide), NULL); gtk_container_set_border_width(GTK_CONTAINER(dialog), 0); //gtk_widget_set_size_request(dialog, 320, 240); label = gtk_label_new(conic_text); gtk_misc_set_alignment(GTK_MISC(label), 0, 0); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, FALSE, 0); gtk_widget_show(label); /* Add a hbox for the entries. */ hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0); for (i = 0; i < 6; i++) { float_entry[i] = gtk_entry_new_with_max_length(9); gtk_entry_set_text(GTK_ENTRY(float_entry[i]), "0"); gtk_editable_set_editable(GTK_EDITABLE(float_entry[i]), TRUE); gtk_entry_set_visibility(GTK_ENTRY(float_entry[i]), TRUE); gtk_box_pack_start(GTK_BOX(hbox), float_entry[i], FALSE, FALSE, 0); gtk_widget_show(float_entry[i]); } gtk_widget_show(hbox); /* And another for the ok and cancel buttons. */ hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0); button = gtk_button_new_from_stock(GTK_STOCK_OK); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); g_signal_connect_swapped (G_OBJECT (button), "clicked", G_CALLBACK (cg_ellipse_draw), G_OBJECT(dialog)); gtk_widget_show(button); button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); g_signal_connect_swapped (G_OBJECT(button), "clicked", G_CALLBACK (gtk_widget_hide), G_OBJECT(dialog)); /* This makes the "CANCEL" button default. */ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_widget_grab_default(button); gtk_widget_show(button); gtk_widget_show(hbox); gtk_widget_show (dialog); } static gboolean cg_is_ellipse(struct cg_ellipse *ellipse) { /* * http://en.wikipedia.org/wiki/Conic : * F(x, y) = A1*x^2 + A2*y^2 + A3*z^2 + 2*B1*xy + 2*B2*xz + 2*B3*yz = 0 * | A1 B1 B2 | * D = | B1 A2 B3 | discriminant(F) = det | A1 B1 | * | B2 B3 A3 | | B1 A2 | * F(x, y) is an ellipse <==> discriminant(F) > 0 */ gdouble dis; if (ellipse->A_ == 0 && ellipse->C_ == 0 && ellipse->B_ == 0) { g_message ("Not an ellipse - A=0, B=0, C=0!\n"); return (FALSE); } dis = (ellipse->B_ * ellipse->B_) - (4 * ellipse->A_ * ellipse->C_); if (dis >= 0) { g_message ("Not an ellipse!\n"); return (FALSE); } ellipse->Ox_ = (- ellipse->E_ * ellipse->B_ + 2 * ellipse->C_ + ellipse->D_) /dis; ellipse->Oy_ = -(2 * ellipse->Ox_ * ellipse->A_ + ellipse->D_) / ellipse->B_; // ellipse->Ox_ = ((ellipse->C_ * ellipse->D_ / 2) - (ellipse->E_ * ellipse->B_ /4)) / // (ellipse->B_ * ellipse->B_ /4 - ellipse->A_ * ellipse->C_); // ellipse->Oy_ = (- ellipse->Ox_ * (ellipse->A_ + ellipse->C_)) / (2 * ellipse->B_); // if (ellipse->B2_ * ellipse->Ox_ + ellipse->B3_ * ellipse->Oy_ + ellipse->A3_ == 0) { // g_message ("Bad center - not an ellipse!\n"); // return (FALSE); // } // g_message ("Ellipse - center (%f, %f)!\n", ellipse->Ox_, ellipse->Oy_); return (TRUE); } void cg_ellipse_draw_conic(GdkDrawable *drawable, GdkGC *gc, struct cg_ellipse *ellipse); void cg_ellipse_draw(GtkWidget *widget, GtkWidget *entry) { GtkWidget *toplevel; toplevel = gtk_widget_get_toplevel(GTK_WIDGET(drawing_area)); g_ellipse.A_ = strtod(gtk_entry_get_text(GTK_ENTRY(float_entry[0])), NULL); g_ellipse.B_ = strtod(gtk_entry_get_text(GTK_ENTRY(float_entry[1])), NULL); g_ellipse.C_ = strtod(gtk_entry_get_text(GTK_ENTRY(float_entry[2])), NULL); g_ellipse.D_ = strtod(gtk_entry_get_text(GTK_ENTRY(float_entry[3])), NULL); g_ellipse.E_ = strtod(gtk_entry_get_text(GTK_ENTRY(float_entry[4])), NULL); g_ellipse.F_ = strtod(gtk_entry_get_text(GTK_ENTRY(float_entry[5])), NULL); g_message("F(x, y) = %f * x^2 + %f * xy + %f * y^2 + %f * x + %f * y + %f = 0\n", g_ellipse.A_, g_ellipse.B_, g_ellipse.C_, g_ellipse.D_ , g_ellipse.E_, g_ellipse.F_); if (!cg_is_ellipse(&g_ellipse)) return; gtk_widget_hide(dialog); cg_ellipse_draw_conic(cg_pixmap, toplevel->style->fg_gc[GTK_WIDGET_STATE(widget)], &g_ellipse); } void cg_ellipse_inline(GtkWidget *w, gpointer data) { g_message ("Hello, cg_ellipse_inline!\n"); } void cg_ellipse_draw_bresenhaim(GdkDrawable *drawable, GdkGC *gc, gint x1_, gint y1_, gint x2_, gint y2_) { gdouble delta, trans_x, trans_y, x_i, y_i, r_x, r_y; gdk_draw_line(drawable, gc, x1_, y1_, x2_, y2_); /* Change to coordinate system with center - the center of the ellipse. */ trans_x = (x1_ + x2_) / 2; trans_y = (y1_ + y2_) / 2; x1_ = CG_CIRCLE_TRANSLATE(trans_x, x1_); y1_ = CG_CIRCLE_TRANSLATE(trans_y, y1_); x2_ = CG_CIRCLE_TRANSLATE(trans_x, x2_); y2_ = CG_CIRCLE_TRANSLATE(trans_y, y2_); /* Draw first area x_i = 0 until border */ r_x = fabs(x1_ - x2_) / 2; r_y = y_i = fabs(y1_ - y2_) / 2; delta = r_y * r_y - r_x * r_x * r_y + r_x * r_x / 4; for (x_i = 0; r_x * r_x * y_i > r_y * r_y * x_i; ) { cg_circle_draw_four_symmetric_pts(drawable, gc, trans_x, trans_y, x_i, y_i); x_i += 1; delta += r_y * r_y * (2 * x_i - 1); if (delta >= 0) { y_i -= 1; delta -= 2 * r_x * r_x * y_i; } } /* Draw the border point.*/ cg_circle_draw_four_symmetric_pts(drawable, gc, trans_x, trans_y, x_i, y_i); /* Draw the second area. */ delta = r_y * r_y * (x_i + 0.5) * (x_i + 0.5); delta += r_x * r_x * (y_i - 1) * (y_i - 1); delta -= r_x * r_x * r_y * r_y; for (y_i--; y_i >= 0; y_i--) { if (delta > 0) { cg_circle_draw_four_symmetric_pts(drawable, gc, trans_x, trans_y, x_i, y_i); } else { x_i += 1; cg_circle_draw_four_symmetric_pts(drawable, gc, trans_x, trans_y, x_i, y_i); delta += 2 * r_y * r_y * x_i; } delta += r_x * r_x - 2 * r_x * r_x * y_i; } cg_line_drawing_refresh(CG_CIRCLE_REV_TRANSLATE(trans_x, r_x + 1), CG_CIRCLE_REV_TRANSLATE(trans_y, r_y + 1), CG_CIRCLE_REV_TRANSLATE(trans_x, -r_x - 1), CG_CIRCLE_REV_TRANSLATE(trans_y, -r_y - 1)); } void cg_ellipse_draw_conic(GdkDrawable *drawable, GdkGC *gc, struct cg_ellipse *ellipse) { gdouble delta, a1, b1, A, B, F, x0, y0, sinphi, cosphi, u, t; gdouble a2, b2, x, y, d1, realx, realy; GtkRequisition req; delta = g_ellipse.A_ * g_ellipse.C_ - g_ellipse.B_ * g_ellipse.B_; if (delta <= 0) { g_message ("This is not an ellipse!\n"); return; } x0 = (g_ellipse.B_*g_ellipse.E_ - g_ellipse.D_*g_ellipse.C_)/delta; y0 = (g_ellipse.D_*g_ellipse.B_ - g_ellipse.A_*g_ellipse.E_)/delta; if (g_ellipse.A_ == g_ellipse.C_ && g_ellipse.B_ != 0) { sinphi = 1; cosphi = 0; } else if (g_ellipse.A_ == g_ellipse.C_) { sinphi = 0; cosphi = 1; } else { t = 2*g_ellipse.B_/(g_ellipse.C_ - g_ellipse.A_); u = 1/sqrt(1+t*t); cosphi = sqrt((1+u)/2); sinphi = sqrt((1-u)/2); } A = g_ellipse.A_*cosphi*cosphi - 2*g_ellipse.B_*cosphi*sinphi + g_ellipse.C_*sinphi*sinphi; B = g_ellipse.A_*sinphi*sinphi + 2*g_ellipse.B_*sinphi*cosphi + g_ellipse.C_*cosphi*cosphi; F = g_ellipse.A_*x0*x0 + 2*g_ellipse.B_*x0*y0 + g_ellipse.C_*y0*y0 + 2*g_ellipse.D_*x0 + 2*g_ellipse.E_*y0 + g_ellipse.F_; if (A < 0) { A = -A; B = -B; F = -F; } if (F >= 0) { g_message ("This is not a real ellipse!\n"); return; } a1 = sqrt(-F/A); b1 = sqrt(-F/B); a2 = a1 * a1; b2 = b1 * b1; x = 0; y = (int)(b1+0.5); d1 = 4*b2 - 4*a2*b1 + a2; bzero(&req, sizeof(GtkRequisition)); gtk_widget_size_request(drawing_area, &req); while (a2 * y > b2 * x) { realx = cosphi*x + sinphi*y + x0; realy = -sinphi*x + cosphi*y + y0; gdk_draw_point(drawable, gc, (gint)(realx+0.5+req.width/4), (gint)(realy+0.5+req.height/4)); realx = cosphi*(-x) + sinphi*y + x0; realy = -sinphi*(-x) + cosphi*y + y0; gdk_draw_point(drawable, gc, (gint)(realx+0.5+req.width/4), (gint)(realy+0.5+req.height/4)); realx = cosphi*x + sinphi*(-y) + x0; realy = -sinphi*x + cosphi*(-y) + y0; gdk_draw_point(drawable, gc, (gint)(realx+0.5+req.width/4), (gint)(realy+0.5+req.height/4)); realx = cosphi*(-x) + sinphi*(-y) + x0; realy = -sinphi*(-x) + cosphi*(-y) + y0; gdk_draw_point(drawable, gc, (gint)(realx+0.5+req.width/4), (gint)(realy+0.5+req.height/4)); if (d1 >= 0) { d1 += 4*b2*(2*x+3) + 8*a2*(1-y); y--; } else d1 += 4*b2*(2*x+3); x++; } d1 = b2*(2*x+1)*(2*x+1) + 4*a2*(y-1)*(y-1) - 4*a2*b2; while (1) { realx = cosphi*x + sinphi*y + x0; realy = -sinphi*x + cosphi*y + y0; gdk_draw_point(drawable, gc, (gint)(realx+0.5+req.width/4), (gint)(realy+0.5+req.height/4)); realx = cosphi*(-x) + sinphi*y + x0; realy = -sinphi*(-x) + cosphi*y + y0; gdk_draw_point(drawable, gc, (gint)(realx+0.5+req.width/4), (gint)(realy+0.5+req.height/4)); realx = cosphi*x + sinphi*(-y) + x0; realy = -sinphi*x + cosphi*(-y) + y0; gdk_draw_point(drawable, gc, (gint)(realx+0.5+req.width/4), (gint)(realy+0.5+req.height/4)); realx = cosphi*(-x) + sinphi*(-y) + x0; realy = -sinphi*(-x) + cosphi*(-y) + y0; gdk_draw_point(drawable, gc, (gint)(realx+0.5+req.width/4), (gint)(realy+0.5+req.height/4)); if (y == 0) break; if (d1 >= 0) d1 += 4*a2*(3-2*y); else { d1 += 8*b2*(x+1) + 4*a2*(3-2*y); x++; } y--; } cg_line_drawing_refresh(0, 0, req.width - 1, req.height - 1); } void cg_ellipse_bresenhaim(GtkWidget *w, gpointer data) { /* Be hackerish and use some drwaing line callbacks. */ /* The center of the ellipse is (X1, X2) and (X2, Y2) is the gradient. */ g_message ("Hello, cg_ellipse_bresenhaim!\n"); bzero(&cg_line, sizeof(cg_line)); cg_line.draw_cb = cg_ellipse_draw_bresenhaim; cg_line.button_id = g_signal_connect(GTK_OBJECT(drawing_area), "button_press_event", (GtkSignalFunc) cg_line_button_press_event, NULL); } void cg_ellipse_conic(GtkWidget *w, gpointer data) { /* GtkWidget *toplevel; */ g_message ("Hello, cg_ellipse_conic!\n"); bzero(&g_ellipse, sizeof(struct cg_ellipse)); cg_ellipse_manual_input(); /* toplevel = gtk_widget_get_toplevel(GTK_WIDGET(drawing_area)); */ /* cg_ellipse_draw_conic(cg_pixmap, toplevel->style->fg_gc[GTK_WIDGET_STATE(w)], &g_ellipse); */ }