diff options
Diffstat (limited to '')
-rw-r--r-- | plug-ins/gfig/gfig-arc.c | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/plug-ins/gfig/gfig-arc.c b/plug-ins/gfig/gfig-arc.c new file mode 100644 index 0000000..5e8be02 --- /dev/null +++ b/plug-ins/gfig/gfig-arc.c @@ -0,0 +1,735 @@ +/* + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This is a plug-in for GIMP. + * + * Generates images containing vector type drawings. + * + * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "config.h" + +#include <math.h> +#include <stdlib.h> + +#include <gtk/gtk.h> + +#include <libgimp/gimp.h> + +#include "gfig.h" +#include "gfig-dobject.h" +#include "gfig-arc.h" + +#include "libgimp/stdplugins-intl.h" + +static gdouble dist (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + +static void mid_point (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *mx, + gdouble *my); + +static gdouble line_grad (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + +static gdouble line_cons (gdouble x, + gdouble y, + gdouble lgrad); + +static void line_definition (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *lgrad, + gdouble *lconst); + +static void arc_details (GdkPoint *vert_a, + GdkPoint *vert_b, + GdkPoint *vert_c, + GdkPoint *center_pnt, + gdouble *radius); + +static gdouble arc_angle (GdkPoint *pnt, + GdkPoint *center); + +static void arc_drawing_details (GfigObject *obj, + gdouble *minang, + GdkPoint *center_pnt, + gdouble *arcang, + gdouble *radius, + gboolean draw_cnts, + gboolean do_scale); + +static void d_draw_arc (GfigObject *obj, + cairo_t *cr); + +static void d_paint_arc (GfigObject *obj); + +static GfigObject *d_copy_arc (GfigObject *obj); + +static void d_update_arc_line (GdkPoint *pnt); +static void d_update_arc (GdkPoint *pnt); +static void d_arc_line_start (GdkPoint *pnt, + gboolean shift_down); +static void d_arc_line_end (GdkPoint *pnt, + gboolean shift_down); + +/* Distance between two points. */ +static gdouble +dist (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + + double s1 = x1 - x2; + double s2 = y1 - y2; + + return sqrt (s1 * s1 + s2 * s2); +} + +/* Mid point of line returned */ +static void +mid_point (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *mx, + gdouble *my) +{ + *mx = (x1 + x2) / 2.0; + *my = (y1 + y2) / 2.0; +} + +/* Careful about infinite grads */ +static gdouble +line_grad (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + double dx, dy; + + dx = x1 - x2; + dy = y1 - y2; + + return (dx == 0.0) ? 0.0 : dy / dx; +} + +/* Constant of line that goes through x, y with grad lgrad */ +static gdouble +line_cons (gdouble x, + gdouble y, + gdouble lgrad) +{ + return y - lgrad * x; +} + +/* Get grad & const for perpend. line to given points */ +static void +line_definition (gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + gdouble *lgrad, + gdouble *lconst) +{ + double grad1; + double midx, midy; + + grad1 = line_grad (x1, y1, x2, y2); + + if (grad1 == 0.0) + { +#ifdef DEBUG + printf ("Infinite grad....\n"); +#endif /* DEBUG */ + return; + } + + mid_point (x1, y1, x2, y2, &midx, &midy); + + /* Invert grad for perpen gradient */ + + *lgrad = -1.0 / grad1; + + *lconst = line_cons (midx, midy,*lgrad); +} + +/* Arch details + * Given three points get arc radius and the co-ords + * of center point. + */ + +static void +arc_details (GdkPoint *vert_a, + GdkPoint *vert_b, + GdkPoint *vert_c, + GdkPoint *center_pnt, + gdouble *radius) +{ + /* Only vertices are in whole numbers - everything else is in doubles */ + double ax, ay; + double bx, by; + double cx, cy; + + double len_a, len_b, len_c; + double sum_sides2; + double area; + double circumcircle_R; + double line1_grad = 0, line1_const = 0; + double line2_grad = 0, line2_const = 0; + double inter_x = 0.0, inter_y = 0.0; + int got_x = 0, got_y = 0; + + ax = (double) (vert_a->x); + ay = (double) (vert_a->y); + bx = (double) (vert_b->x); + by = (double) (vert_b->y); + cx = (double) (vert_c->x); + cy = (double) (vert_c->y); + + len_a = dist (ax, ay, bx, by); + len_b = dist (bx, by, cx, cy); + len_c = dist (cx, cy, ax, ay); + + sum_sides2 = (fabs (len_a) + fabs (len_b) + fabs (len_c))/2; + + /* Area */ + area = sqrt (sum_sides2 * (sum_sides2 - len_a) * + (sum_sides2 - len_b) * + (sum_sides2 - len_c)); + + /* Circumcircle */ + circumcircle_R = len_a * len_b * len_c / (4 * area); + *radius = circumcircle_R; + + /* Deal with exceptions - I hate exceptions */ + + if (ax == bx || ax == cx || cx == bx) + { + /* vert line -> mid point gives inter_x */ + if (ax == bx && bx == cx) + { + /* Straight line */ + double miny = ay; + double maxy = ay; + + if (by > maxy) + maxy = by; + + if (by < miny) + miny = by; + + if (cy > maxy) + maxy = cy; + + if (cy < miny) + miny = cy; + + inter_y = (maxy - miny) / 2 + miny; + } + else if (ax == bx) + { + inter_y = (ay - by) / 2 + by; + } + else if (bx == cx) + { + inter_y = (by - cy) / 2 + cy; + } + else + { + inter_y = (cy - ay) / 2 + ay; + } + got_y = 1; + } + + if (ay == by || by == cy || ay == cy) + { + /* Horz line -> midpoint gives inter_y */ + if (ax == bx && bx == cx) + { + /* Straight line */ + double minx = ax; + double maxx = ax; + + if (bx > maxx) + maxx = bx; + + if (bx < minx) + minx = bx; + + if (cx > maxx) + maxx = cx; + + if (cx < minx) + minx = cx; + + inter_x = (maxx - minx) / 2 + minx; + } + else if (ay == by) + { + inter_x = (ax - bx) / 2 + bx; + } + else if (by == cy) + { + inter_x = (bx - cx) / 2 + cx; + } + else + { + inter_x = (cx - ax) / 2 + ax; + } + got_x = 1; + } + + if (!got_x || !got_y) + { + /* At least two of the lines are not parallel to the axis */ + /*first line */ + if (ax != bx && ay != by) + line_definition (ax, ay, bx, by, &line1_grad, &line1_const); + else + line_definition (ax, ay, cx, cy, &line1_grad, &line1_const); + /* second line */ + if (bx != cx && by != cy) + line_definition (bx, by, cx, cy, &line2_grad, &line2_const); + else + line_definition (ax, ay, cx, cy, &line2_grad, &line2_const); + } + + /* Intersection point */ + + if (!got_x) + inter_x = (line2_const - line1_const) / (line1_grad - line2_grad); + if (!got_y) + inter_y = line1_grad * inter_x + line1_const; + + center_pnt->x = (gint) inter_x; + center_pnt->y = (gint) inter_y; +} + +static gdouble +arc_angle (GdkPoint *pnt, + GdkPoint *center) +{ + /* Get angle (in degrees) of point given origin of center */ + gint16 shift_x; + gint16 shift_y; + gdouble offset_angle; + + shift_x = pnt->x - center->x; + shift_y = -pnt->y + center->y; + offset_angle = atan2 (shift_y, shift_x); + + if (offset_angle < 0) + offset_angle += 2.0 * G_PI; + + return offset_angle * 360 / (2.0 * G_PI); +} + +static void +arc_drawing_details (GfigObject *obj, + gdouble *minang, + GdkPoint *center_pnt, + gdouble *arcang, + gdouble *radius, + gboolean draw_cnts, + gboolean do_scale) +{ + DobjPoints *pnt1 = NULL; + DobjPoints *pnt2 = NULL; + DobjPoints *pnt3 = NULL; + DobjPoints dpnts[3]; + gdouble ang1, ang2, ang3; + gdouble maxang; + + pnt1 = obj->points; + + if (!pnt1) + return; /* Not fully drawn */ + + pnt2 = pnt1->next; + + if (!pnt2) + return; /* Not fully drawn */ + + pnt3 = pnt2->next; + + if (!pnt3) + return; /* Still not fully drawn */ + + if (do_scale) + { + /* Adjust pnts for scaling */ + /* Warning struct copies here! and casting to double <-> int */ + /* Too complex fix me - to much hacking */ + gdouble xy[2]; + int j; + + dpnts[0] = *pnt1; + dpnts[1] = *pnt2; + dpnts[2] = *pnt3; + + pnt1 = &dpnts[0]; + pnt2 = &dpnts[1]; + pnt3 = &dpnts[2]; + + for (j = 0 ; j < 3; j++) + { + xy[0] = dpnts[j].pnt.x; + xy[1] = dpnts[j].pnt.y; + if (selvals.scaletoimage) + scale_to_original_xy (&xy[0], 1); + else + scale_to_xy (&xy[0], 1); + dpnts[j].pnt.x = xy[0]; + dpnts[j].pnt.y = xy[1]; + } + } + + arc_details (&pnt1->pnt, &pnt2->pnt, &pnt3->pnt, center_pnt, radius); + + ang1 = arc_angle (&pnt1->pnt, center_pnt); + ang2 = arc_angle (&pnt2->pnt, center_pnt); + ang3 = arc_angle (&pnt3->pnt, center_pnt); + + /* Find min/max angle */ + + maxang = ang1; + + if (ang3 > maxang) + maxang = ang3; + + *minang = ang1; + + if (ang3 < *minang) + *minang = ang3; + + if (ang2 > *minang && ang2 < maxang) + *arcang = maxang - *minang; + else + *arcang = maxang - *minang - 360; +} + +static void +d_draw_arc (GfigObject *obj, + cairo_t *cr) +{ + DobjPoints *pnt1, *pnt2, *pnt3; + GdkPoint center_pnt; + gdouble radius, minang, arcang; + + g_assert (obj != NULL); + + if (!obj) + return; + + pnt1 = obj->points; + pnt2 = pnt1 ? pnt1->next : NULL; + pnt3 = pnt2 ? pnt2->next : NULL; + + if (! pnt3) + return; + + draw_sqr (&pnt1->pnt, obj == gfig_context->selected_obj, cr); + draw_sqr (&pnt2->pnt, obj == gfig_context->selected_obj, cr); + draw_sqr (&pnt3->pnt, obj == gfig_context->selected_obj, cr); + + arc_drawing_details (obj, &minang, ¢er_pnt, &arcang, &radius, + TRUE, FALSE); + gfig_draw_arc (center_pnt.x, center_pnt.y, radius, radius, -minang, -(minang + arcang), cr); +} + +static void +d_paint_arc (GfigObject *obj) +{ + /* first point center */ + /* Next point is radius */ + gdouble *line_pnts; + gint seg_count = 0; + gint i = 0; + gdouble ang_grid; + gdouble ang_loop; + gdouble radius; + gint loop; + GdkPoint last_pnt = { 0, 0 }; + gboolean first = TRUE; + GdkPoint center_pnt; + gdouble minang, arcang; + + g_assert (obj != NULL); + + if (!obj) + return; + + /* No cnt pnts & must scale */ + arc_drawing_details (obj, &minang, ¢er_pnt, &arcang, &radius, + FALSE, TRUE); + + seg_count = 360; /* Should make a smoth-ish curve */ + + /* +3 because we MIGHT do pie selection */ + line_pnts = g_new0 (gdouble, 2 * seg_count + 3); + + /* Lines */ + ang_grid = 2.0 * G_PI / 360.0; + + if (arcang < 0.0) + { + /* Swap - since we always draw anti-clock wise */ + minang += arcang; + arcang = -arcang; + } + + minang = minang * (2.0 * G_PI / 360.0); /* min ang is in degrees - need in rads */ + + for (loop = 0 ; loop < abs ((gint)arcang) ; loop++) + { + gdouble lx, ly; + GdkPoint calc_pnt; + + ang_loop = (gdouble)loop * ang_grid + minang; + + lx = radius * cos (ang_loop); + ly = -radius * sin (ang_loop); /* y grows down screen and angs measured from x clockwise */ + + calc_pnt.x = RINT (lx + center_pnt.x); + calc_pnt.y = RINT (ly + center_pnt.y); + + /* Miss out duped pnts */ + if (!first) + { + if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y) + { + continue; + } + } + + line_pnts[i++] = calc_pnt.x; + line_pnts[i++] = calc_pnt.y; + last_pnt = calc_pnt; + + if (first) + { + first = FALSE; + } + } + + /* One go */ + if (obj->style.paint_type == PAINT_BRUSH_TYPE) + { + gfig_paint (selvals.brshtype, + gfig_context->drawable_id, + i, line_pnts); + } + + g_free (line_pnts); +} + +static GfigObject * +d_copy_arc (GfigObject *obj) +{ + GfigObject *nc; + + g_assert (obj->type == ARC); + + nc = d_new_object (ARC, obj->points->pnt.x, obj->points->pnt.y); + nc->points->next = d_copy_dobjpoints (obj->points->next); + + return nc; +} + +void +d_arc_object_class_init (void) +{ + GfigObjectClass *class = &dobj_class[ARC]; + + class->type = ARC; + class->name = "ARC"; + class->drawfunc = d_draw_arc; + class->paintfunc = d_paint_arc; + class->copyfunc = d_copy_arc; + class->update = d_update_arc; +} + +/* Update end point of line */ +static void +d_update_arc_line (GdkPoint *pnt) +{ + DobjPoints *spnt, *epnt; + /* Get last but one segment and undraw it - + * Then draw new segment in. + * always dealing with the static object. + */ + + /* Get start of segments */ + spnt = obj_creating->points; + + if (!spnt) + return; /* No points */ + + if ((epnt = spnt->next)) + { + g_free (epnt); + } + + epnt = new_dobjpoint (pnt->x, pnt->y); + spnt->next = epnt; +} + +static void +d_update_arc (GdkPoint *pnt) +{ + DobjPoints *pnt1 = NULL; + DobjPoints *pnt2 = NULL; + DobjPoints *pnt3 = NULL; + + /* First two points as line only become arch when third + * point is placed on canvas. + */ + + pnt1 = obj_creating->points; + + if (!pnt1 || + !(pnt2 = pnt1->next) || + !(pnt3 = pnt2->next)) + { + d_update_arc_line (pnt); + return; /* Not fully drawn */ + } + + /* Update a real curve */ + /* Nothing to be done ... */ +} + +static void +d_arc_line_start (GdkPoint *pnt, + gboolean shift_down) +{ + if (!obj_creating || !shift_down) + { + /* Must delete obj_creating if we have one */ + obj_creating = d_new_object (LINE, pnt->x, pnt->y); + } + else + { + /* Contniuation */ + d_update_arc_line (pnt); + } +} + +void +d_arc_start (GdkPoint *pnt, + gboolean shift_down) +{ + /* Draw lines to start with -- then convert to an arc */ + d_arc_line_start (pnt, TRUE); /* TRUE means multiple pointed line */ +} + +static void +d_arc_line_end (GdkPoint *pnt, + gboolean shift_down) +{ + if (shift_down) + { + if (tmp_line) + { + GdkPoint tmp_pnt = *pnt; + + if (need_to_scale) + { + tmp_pnt.x = pnt->x * scale_x_factor; + tmp_pnt.y = pnt->y * scale_y_factor; + } + + d_pnt_add_line (tmp_line, tmp_pnt.x, tmp_pnt.y, -1); + free_one_obj (obj_creating); + /* Must free obj_creating */ + } + else + { + tmp_line = obj_creating; + add_to_all_obj (gfig_context->current_obj, obj_creating); + } + + obj_creating = d_new_object (LINE, pnt->x, pnt->y); + } + else + { + if (tmp_line) + { + GdkPoint tmp_pnt = *pnt; + + if (need_to_scale) + { + tmp_pnt.x = pnt->x * scale_x_factor; + tmp_pnt.y = pnt->y * scale_y_factor; + } + + d_pnt_add_line (tmp_line, tmp_pnt.x, tmp_pnt.y, -1); + free_one_obj (obj_creating); + /* Must free obj_creating */ + } + else + { + add_to_all_obj (gfig_context->current_obj, obj_creating); + } + obj_creating = NULL; + tmp_line = NULL; + } + /*gtk_widget_queue_draw (gfig_context->preview);*/ +} + +void +d_arc_end (GdkPoint *pnt, + gboolean shift_down) +{ + /* Under control point */ + if (!tmp_line || + !tmp_line->points || + !tmp_line->points->next) + { + /* No arc created - yet. Must have three points */ + d_arc_line_end (pnt, TRUE); + } + else + { + /* Complete arc */ + /* Convert to an arc ... */ + tmp_line->type = ARC; + tmp_line->class = &dobj_class[ARC]; + d_arc_line_end (pnt, FALSE); + if (need_to_scale) + { + selvals.scaletoimage = 0; + } + gtk_widget_queue_draw (gfig_context->preview); + if (need_to_scale) + { + selvals.scaletoimage = 1; + } + } +} + |