summaryrefslogtreecommitdiffstats
path: root/plug-ins/gfig/gfig-arc.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/gfig/gfig-arc.c')
-rw-r--r--plug-ins/gfig/gfig-arc.c735
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, &center_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, &center_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;
+ }
+ }
+}
+