/* * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This is a plug-in for the 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include "gfig.h" #include "gfig-arc.h" #include "gfig-bezier.h" #include "gfig-circle.h" #include "gfig-dobject.h" #include "gfig-ellipse.h" #include "gfig-line.h" #include "gfig-poly.h" #include "gfig-spiral.h" #include "gfig-star.h" #include "libgimp/stdplugins-intl.h" static Dobject *operation_obj; static GdkPoint *move_all_pnt; /* Point moving all from */ static void draw_one_obj (Dobject *obj); static void do_move_obj (Dobject *obj, GdkPoint *to_pnt); static void do_move_all_obj (GdkPoint *to_pnt); static void do_move_obj_pnt (Dobject *obj, GdkPoint *to_pnt); static void remove_obj_from_list (GFigObj *obj, Dobject *del_obj); static gint scan_obj_points (DobjPoints *opnt, GdkPoint *pnt); /* Delete a list of points */ void d_delete_dobjpoints (DobjPoints * pnts) { DobjPoints *next; DobjPoints *pnt2del = pnts; while (pnt2del) { next = pnt2del->next; g_free (pnt2del); pnt2del = next; } } DobjPoints* new_dobjpoint (gint x, gint y) { DobjPoints *npnt = g_new0 (DobjPoints, 1); npnt->pnt.x = x; npnt->pnt.y = y; return npnt; } DobjPoints * d_copy_dobjpoints (DobjPoints *pnts) { DobjPoints *ret = NULL; DobjPoints *head = NULL; DobjPoints *newpnt; DobjPoints *pnt2copy; for (pnt2copy = pnts; pnt2copy; pnt2copy = pnt2copy->next) { newpnt = new_dobjpoint (pnt2copy->pnt.x, pnt2copy->pnt.y); if (!ret) { head = ret = newpnt; } else { head->next = newpnt; head = newpnt; } } return ret; } static DobjPoints * get_diffs (Dobject *obj, gint *xdiff, gint *ydiff, GdkPoint *to_pnt) { DobjPoints *spnt; g_assert (obj != NULL); for (spnt = obj->points; spnt; spnt = spnt->next) { if (spnt->found_me) { *xdiff = spnt->pnt.x - to_pnt->x; *ydiff = spnt->pnt.y - to_pnt->y; return spnt; } } return NULL; } static gboolean inside_sqr (GdkPoint *cpnt, GdkPoint *testpnt) { /* Return TRUE if testpnt is near cpnt */ gint x = cpnt->x; gint y = cpnt->y; gint tx = testpnt->x; gint ty = testpnt->y; return (abs (x - tx) <= SQ_SIZE && abs (y - ty) < SQ_SIZE); } static gboolean scan_obj_points (DobjPoints *opnt, GdkPoint *pnt) { while (opnt) { if (inside_sqr (&opnt->pnt, pnt)) { opnt->found_me = TRUE; return TRUE; } opnt->found_me = FALSE; opnt = opnt->next; } return FALSE; } static Dobject * get_nearest_objs (GFigObj *obj, GdkPoint *pnt) { /* Nearest object to given point or NULL */ DAllObjs *all; Dobject *test_obj; gint count = 0; if (!obj) return NULL; for (all = obj->obj_list; all; all = all->next) { test_obj = all->obj; if (count == obj_show_single || obj_show_single == -1) if (scan_obj_points (test_obj->points, pnt)) { return test_obj; } count++; } return NULL; } void object_operation_start (GdkPoint *pnt, gint shift_down) { Dobject *new_obj; /* Find point in given object list */ operation_obj = get_nearest_objs (current_obj, pnt); /* Special case if shift down && move obj then moving all objs */ if (shift_down && selvals.otype == MOVE_OBJ) { move_all_pnt = g_malloc0 (sizeof (*move_all_pnt)); *move_all_pnt = *pnt; /* Structure copy */ setup_undo (); return; } if (!operation_obj) return;/* None to work on */ setup_undo (); switch (selvals.otype) { case MOVE_OBJ: if (operation_obj->type == BEZIER) { d_draw_bezier (operation_obj); tmp_bezier = operation_obj; d_draw_bezier (operation_obj); } break; case MOVE_POINT: if (operation_obj->type == BEZIER) { d_draw_bezier (operation_obj); tmp_bezier = operation_obj; d_draw_bezier (operation_obj); } /* If shift is down the break into sep lines */ if ((operation_obj->type == POLY || operation_obj->type == STAR) && shift_down) { switch (operation_obj->type) { case POLY: d_poly2lines (operation_obj); break; case STAR: d_star2lines (operation_obj); break; default: break; } /* Re calc which object point we are lookin at */ scan_obj_points (operation_obj->points, pnt); } break; case COPY_OBJ: /* Copy the "operation object" */ /* Then bung us into "copy/move" mode */ new_obj = (Dobject*) operation_obj->copyfunc (operation_obj); if (new_obj) { scan_obj_points (new_obj->points, pnt); add_to_all_obj (current_obj, new_obj); operation_obj = new_obj; selvals.otype = MOVE_COPY_OBJ; new_obj->drawfunc (new_obj); } break; case DEL_OBJ: remove_obj_from_list (current_obj, operation_obj); break; case MOVE_COPY_OBJ: /* Never when button down */ default: g_warning ("Internal error selvals.otype object operation start"); break; } } void object_operation_end (GdkPoint *pnt, gint shift_down) { if (selvals.otype != DEL_OBJ && operation_obj && operation_obj->type == BEZIER) { d_draw_bezier (operation_obj); tmp_bezier = NULL; /* use as switch */ d_draw_bezier (operation_obj); } operation_obj = NULL; if (move_all_pnt) { g_free (move_all_pnt); move_all_pnt = 0; } /* Special case - if copying mode MUST be copy when button up received */ if (selvals.otype == MOVE_COPY_OBJ) selvals.otype = COPY_OBJ; } /* Move object around */ void object_operation (GdkPoint *to_pnt, gint shift_down) { /* Must do diffent things depending on object type */ /* but must have object to operate on! */ /* Special case - if shift own and move_obj then move ALL objects */ if (move_all_pnt && shift_down && selvals.otype == MOVE_OBJ) { do_move_all_obj (to_pnt); return; } if (!operation_obj) return; switch (selvals.otype) { case MOVE_OBJ: case MOVE_COPY_OBJ: switch (operation_obj->type) { case LINE: case CIRCLE: case ELLIPSE: case POLY: case ARC: case STAR: case SPIRAL: case BEZIER: do_move_obj (operation_obj, to_pnt); break; default: /* Internal error */ g_warning ("Internal error in operation_obj->type"); break; } break; case MOVE_POINT: switch (operation_obj->type) { case LINE: case CIRCLE: case ELLIPSE: case POLY: case ARC: case STAR: case SPIRAL: case BEZIER: do_move_obj_pnt (operation_obj, to_pnt); break; default: /* Internal error */ g_warning ("Internal error in operation_obj->type"); break; } break; case DEL_OBJ: break; case COPY_OBJ: /* Should have been changed to MOVE_COPY_OBJ */ default: g_warning ("Internal error selvals.otype"); break; } } static void update_pnts (Dobject *obj, gint xdiff, gint ydiff) { DobjPoints *spnt; g_assert (obj != NULL); /* Update all pnts */ for (spnt = obj->points; spnt; spnt = spnt->next) { spnt->pnt.x -= xdiff; spnt->pnt.y -= ydiff; } } static void remove_obj_from_list (GFigObj *obj, Dobject *del_obj) { /* Nearest object to given point or NULL */ DAllObjs *all; DAllObjs *prev_all = NULL; g_assert (del_obj != NULL); all = obj->obj_list; while (all) { if (all->obj == del_obj) { /* Found the one to delete */ if (prev_all) prev_all->next = all->next; else obj->obj_list = all->next; /* Draw obj (which will actually undraw it! */ del_obj->drawfunc (del_obj); free_one_obj (del_obj); g_free (all); if (obj_show_single != -1) { /* We've just deleted the only visible one */ draw_grid_clear (); obj_show_single = -1; /* Show all again */ } return; } prev_all = all; all = all->next; } g_warning (_("Hey where has the object gone ?")); } static void do_move_all_obj (GdkPoint *to_pnt) { /* Move all objects in one go */ /* Undraw/then draw in new pos */ gint xdiff = move_all_pnt->x - to_pnt->x; gint ydiff = move_all_pnt->y - to_pnt->y; if (xdiff || ydiff) { DAllObjs *all; for (all = current_obj->obj_list; all; all = all->next) { Dobject *obj = all->obj; /* undraw ! */ draw_one_obj (obj); update_pnts (obj, xdiff, ydiff); /* Draw in new pos */ draw_one_obj (obj); } *move_all_pnt = *to_pnt; } } void do_save_obj (Dobject *obj, FILE *to) { DobjPoints *spnt; for (spnt = obj->points; spnt; spnt = spnt->next) { fprintf (to, "%d %d\n", spnt->pnt.x, spnt->pnt.y); } } static void do_move_obj (Dobject *obj, GdkPoint *to_pnt) { /* Move the whole line - undraw the line to start with */ /* Then draw in new pos */ gint xdiff = 0; gint ydiff = 0; get_diffs (obj, &xdiff, &ydiff, to_pnt); if (xdiff || ydiff) { /* undraw ! */ draw_one_obj (obj); update_pnts (obj, xdiff, ydiff); /* Draw in new pos */ draw_one_obj (obj); } } static void do_move_obj_pnt (Dobject *obj, GdkPoint *to_pnt) { /* Move the whole line - undraw the line to start with */ /* Then draw in new pos */ DobjPoints *spnt; gint xdiff = 0; gint ydiff = 0; spnt = get_diffs (obj, &xdiff, &ydiff, to_pnt); if ((!xdiff && !ydiff) || !spnt) return; /* undraw ! */ draw_one_obj (obj); spnt->pnt.x = spnt->pnt.x - xdiff; spnt->pnt.y = spnt->pnt.y - ydiff; /* Draw in new pos */ draw_one_obj (obj); } /* copy objs */ DAllObjs * copy_all_objs (DAllObjs *objs) { DAllObjs *nobj; DAllObjs *new_all_objs = NULL; DAllObjs *ret = NULL; while (objs) { nobj = g_new0 (DAllObjs, 1); if (!ret) { ret = new_all_objs = nobj; } else { new_all_objs->next = nobj; new_all_objs = nobj; } nobj->obj = (Dobject *) objs->obj->copyfunc (objs->obj); objs = objs->next; } return ret; } /* Screen refresh */ static void draw_one_obj (Dobject * obj) { obj->drawfunc (obj); } void draw_objects (DAllObjs *objs, gint show_single) { /* Show_single - only one object to draw Unless shift * is down in which case show all. */ gint count = 0; while (objs) { if (!show_single || count == obj_show_single || obj_show_single == -1) draw_one_obj (objs->obj); objs = objs->next; count++; } } void prepend_to_all_obj (GFigObj *fobj, DAllObjs *nobj) { DAllObjs *cobj; setup_undo (); /* Remember ME */ if (!fobj->obj_list) { fobj->obj_list = nobj; return; } cobj = fobj->obj_list; while (cobj->next) { cobj = cobj->next; } cobj->next = nobj; } static void scale_obj_points (DobjPoints *opnt, gdouble scale_x, gdouble scale_y) { while (opnt) { opnt->pnt.x = (gint) (opnt->pnt.x * scale_x); opnt->pnt.y = (gint) (opnt->pnt.y * scale_y); opnt = opnt->next; } } void add_to_all_obj (GFigObj *fobj, Dobject *obj) { DAllObjs *nobj; nobj = g_new0 (DAllObjs, 1); nobj->obj = obj; if (need_to_scale) scale_obj_points (obj->points, scale_x_factor, scale_y_factor); prepend_to_all_obj (fobj, nobj); } /* First button press -- start drawing object */ void object_start (GdkPoint *pnt, gint shift_down) { /* start for the current object */ if (!selvals.scaletoimage) { need_to_scale = 1; selvals.scaletoimage = 1; } else { need_to_scale = 0; } switch (selvals.otype) { case LINE: /* Shift means we are still drawing */ if (!shift_down || !obj_creating) draw_sqr (pnt); d_line_start (pnt, shift_down); break; case CIRCLE: draw_sqr (pnt); d_circle_start (pnt, shift_down); break; case ELLIPSE: draw_sqr (pnt); d_ellipse_start (pnt, shift_down); break; case POLY: draw_sqr (pnt); d_poly_start (pnt, shift_down); break; case ARC: d_arc_start (pnt, shift_down); break; case STAR: draw_sqr (pnt); d_star_start (pnt, shift_down); break; case SPIRAL: draw_sqr (pnt); d_spiral_start (pnt, shift_down); break; case BEZIER: if (!tmp_bezier) draw_sqr (pnt); d_bezier_start (pnt, shift_down); break; default: /* Internal error */ break; } } void object_end (GdkPoint *pnt, gint shift_down) { /* end for the current object */ /* Add onto global object list */ /* If shift is down may carry on drawing */ switch (selvals.otype) { case LINE: d_line_end (pnt, shift_down); draw_sqr (pnt); break; case CIRCLE: draw_sqr (pnt); d_circle_end (pnt, shift_down); break; case ELLIPSE: draw_sqr (pnt); d_ellipse_end (pnt, shift_down); break; case POLY: draw_sqr (pnt); d_poly_end (pnt, shift_down); break; case STAR: draw_sqr (pnt); d_star_end (pnt, shift_down); break; case ARC: draw_sqr (pnt); d_arc_end (pnt, shift_down); break; case SPIRAL: draw_sqr (pnt); d_spiral_end (pnt, shift_down); break; case BEZIER: d_bezier_end (pnt, shift_down); break; default: /* Internal error */ break; } if (need_to_scale) { need_to_scale = 0; selvals.scaletoimage = 0; } } void object_update (GdkPoint *pnt) { /* update for the current object */ /* New position xy */ switch (selvals.otype) { case LINE: d_update_line (pnt); break; case CIRCLE: d_update_circle (pnt); break; case ELLIPSE: d_update_ellipse (pnt); break; case POLY: d_update_poly (pnt); break; case STAR: d_update_star (pnt); break; case ARC: d_update_arc (pnt); break; case SPIRAL: d_update_spiral (pnt); break; case BEZIER: d_update_bezier (pnt); break; default: /* Internal error */ break; } }