summaryrefslogtreecommitdiffstats
path: root/plug-ins/gfig/gfig-grid.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/gfig/gfig-grid.c')
-rw-r--r--plug-ins/gfig/gfig-grid.c540
1 files changed, 540 insertions, 0 deletions
diff --git a/plug-ins/gfig/gfig-grid.c b/plug-ins/gfig/gfig-grid.c
new file mode 100644
index 0000000..ac81859
--- /dev/null
+++ b/plug-ins/gfig/gfig-grid.c
@@ -0,0 +1,540 @@
+/*
+ * 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 <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-grid.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* For the isometric grid */
+#define SQRT3 1.73205080756887729353 /* Square root of 3 */
+#define SIN_1o6PI_RAD 0.5 /* Sine 1/6 Pi Radians */
+#define COS_1o6PI_RAD SQRT3 / 2 /* Cosine 1/6 Pi Radians */
+#define TAN_1o6PI_RAD 1 / SQRT3 /* Tangent 1/6 Pi Radians == SIN / COS */
+#define RECIP_TAN_1o6PI_RAD SQRT3 /* Reciprocal of Tangent 1/6 Pi Radians */
+
+gint grid_gc_type = GFIG_NORMAL_GC;
+
+static void draw_grid_polar (cairo_t *drawgc);
+static void draw_grid_sq (cairo_t *drawgc);
+static void draw_grid_iso (cairo_t *drawgc);
+
+static cairo_t * gfig_get_grid_gc (cairo_t *cr,
+ GtkWidget *widget,
+ gint gctype);
+
+static void find_grid_pos_polar (GdkPoint *p,
+ GdkPoint *gp);
+
+
+/********** PrimeFactors for Shaneyfelt-style Polar Grid **********
+ * Quickly factor any number up to 17160
+ * Correctly factors numbers up to 131 * 131 - 1
+ */
+typedef struct
+{
+ gint product;
+ gint remaining;
+ gint current;
+ gint next;
+ gint index;
+} PrimeFactors;
+
+static gchar primes[] = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
+ 59,61,67,71,73,79,83,89,97,101,103,107,109,113,127 };
+
+#define PRIMES_MAX_INDEX 30
+
+
+static gint
+prime_factors_get (PrimeFactors *this)
+{
+ this->current = this->next;
+ while (this->index <= PRIMES_MAX_INDEX)
+ {
+ if (this->remaining % primes[this->index] == 0) /* divisible */
+ {
+ this->remaining /= primes[this->index];
+ this->next = primes[this->index];
+ return this->current;
+ }
+ this->index++;
+ }
+ this->next = this->remaining;
+ this->remaining = 1;
+
+ return this->current;
+}
+
+static gint
+prime_factors_lookahead (PrimeFactors *this)
+{
+ return this->next;
+}
+
+static void
+prime_factors_reset (PrimeFactors *this)
+{
+ this->remaining = this->product;
+ this->index = 0;
+ prime_factors_get (this);
+}
+
+static PrimeFactors *
+prime_factors_new (gint n)
+{
+ PrimeFactors *this = g_new (PrimeFactors, 1);
+
+ this->product = n;
+ prime_factors_reset (this);
+
+ return this;
+}
+
+static void
+prime_factors_delete (PrimeFactors* this)
+{
+ g_free (this);
+}
+
+/********** ********** **********/
+
+static gdouble
+sector_size_at_radius (gdouble inner_radius)
+{
+ PrimeFactors *factors = prime_factors_new (selvals.opts.grid_sectors_desired);
+ gint current_sectors = 1;
+ gdouble sector_size = 2 * G_PI / current_sectors;
+
+ while ((current_sectors < selvals.opts.grid_sectors_desired)
+ && (inner_radius*sector_size
+ > (prime_factors_lookahead (factors) *
+ selvals.opts.grid_granularity)))
+ {
+ current_sectors *= prime_factors_get (factors);
+ sector_size = 2 * G_PI / current_sectors;
+ }
+
+ prime_factors_delete(factors);
+
+ return sector_size;
+}
+
+static void
+find_grid_pos_polar (GdkPoint *p,
+ GdkPoint *gp)
+{
+ gdouble cx = preview_width / 2.0;
+ gdouble cy = preview_height / 2.0;
+ gdouble px = p->x - cx;
+ gdouble py = p->y - cy;
+ gdouble x = 0;
+ gdouble y = 0;
+ gdouble r = sqrt (SQR (px) + SQR (py));
+
+ if (r >= selvals.opts.grid_radius_min * 0.5)
+ {
+ gdouble t;
+ gdouble sectorSize;
+
+ r = selvals.opts.grid_radius_interval
+ * (gint) (0.5 + ((r - selvals.opts.grid_radius_min) /
+ selvals.opts.grid_radius_interval))
+ + selvals.opts.grid_radius_min;
+
+ t = atan2 (py, px) + 2 * G_PI;
+ sectorSize = sector_size_at_radius (r);
+ t = selvals.opts.grid_rotation
+ + (gint) (0.5 + ((t - selvals.opts.grid_rotation) / sectorSize))
+ * sectorSize;
+ x = r * cos (t);
+ y = r * sin (t);
+ }
+
+ gp->x = x + cx;
+ gp->y = y + cy;
+}
+
+/* find_grid_pos - Given an x, y point return the grid position of it */
+/* return the new position in the passed point */
+
+void
+gfig_grid_colors (GtkWidget *widget)
+{
+}
+
+void
+find_grid_pos (GdkPoint *p,
+ GdkPoint *gp,
+ guint is_butt3)
+{
+ gint16 x = p->x;
+ gint16 y = p->y;
+ static GdkPoint cons_pnt;
+
+ if (selvals.opts.gridtype == RECT_GRID)
+ {
+ if (p->x % selvals.opts.gridspacing > selvals.opts.gridspacing/2)
+ x += selvals.opts.gridspacing;
+
+ if (p->y % selvals.opts.gridspacing > selvals.opts.gridspacing/2)
+ y += selvals.opts.gridspacing;
+
+ gp->x = (x/selvals.opts.gridspacing)*selvals.opts.gridspacing;
+ gp->y = (y/selvals.opts.gridspacing)*selvals.opts.gridspacing;
+
+ if (is_butt3)
+ {
+ if (abs (gp->x - cons_pnt.x) < abs (gp->y - cons_pnt.y))
+ gp->x = cons_pnt.x;
+ else
+ gp->y = cons_pnt.y;
+ }
+ else
+ {
+ /* Store the point since might be used later */
+ cons_pnt = *gp; /* Structure copy */
+ }
+ }
+ else if (selvals.opts.gridtype == POLAR_GRID)
+ {
+ find_grid_pos_polar (p,gp);
+ }
+ else if (selvals.opts.gridtype == ISO_GRID)
+ {
+ /*
+ * This really needs a picture to show the math...
+ *
+ * Consider an isometric grid with one of the sets of lines
+ * parallel to the y axis (vertical alignment). Further define
+ * that the origin of a Cartesian grid is at a isometric vertex.
+ * For simplicity consider the first quadrant only.
+ *
+ * - Let one line segment between vertices be r
+ * - Define the value of r as the grid spacing
+ * - Assign an integer n identifier to each vertical grid line
+ * along the x axis. with n=0 being the y axis. n can be any
+ * integer
+ * - Let m to be any integer
+
+ * - Let h be the spacing between vertical grid lines measured
+ * along the x axis. It follows from the isometric grid that
+ * h has a value of r * COS(1/6 Pi Rad)
+ *
+ * Consider a Vertex V at the Cartesian location [Xv, Yv]
+ *
+ * It follows that vertices belong to the set...
+ * V[Xv, Yv] = [ [ n * h ] ,
+ * [ m * r + ( 0.5 * r (n % 2) ) ] ]
+ * for all integers n and m
+ *
+ * Who cares? Me. It's useful in solving this problem:
+ * Consider an arbitrary point P[Xp,Yp], find the closest vertex
+ * in the set V.
+ *
+ * Restated this problem is "find values for m and n that are
+ * drive V closest to P"
+ *
+ * A Solution method (there may be a better one?):
+ *
+ * Step 1) bound n to the two closest values for Xp
+ * n_lo = (int) (Xp / h)
+ * n_hi = n_lo + 1
+ *
+ * Step 2) Consider the two closes vertices for each n_lo and
+ * n_hi. The further of the vertices in each pair can
+ * readily be discarded.
+ *
+ * m_lo_n_lo = (int) ( (Yp / r) - 0.5 (n_lo % 2) )
+ * m_hi_n_lo = m_lo_n_lo + 1
+ *
+ * m_lo_n_hi = (int) ( (Yp / r) - 0.5 (n_hi % 2) )
+ * m_hi_n_hi = m_hi_n_hi
+ *
+ * Step 3) compute the distance from P to V1 and V2. Snap to the
+ * closer point.
+ */
+
+ gint n_lo;
+ gint n_hi;
+ gint m_lo_n_lo;
+ gint m_hi_n_lo;
+ gint m_lo_n_hi;
+ gint m_hi_n_hi;
+ gint m_n_lo;
+ gint m_n_hi;
+ gdouble r;
+ gdouble h;
+ gint x1;
+ gint x2;
+ gint y1;
+ gint y2;
+
+ r = selvals.opts.gridspacing;
+ h = COS_1o6PI_RAD * r;
+
+ n_lo = (gint) x / h;
+ n_hi = n_lo + 1;
+
+ /* evaluate m candidates for n_lo */
+ m_lo_n_lo = (gint) ((y / r) - 0.5 * (n_lo % 2));
+ m_hi_n_lo = m_lo_n_lo + 1;
+
+ /* figure out which is the better candidate */
+ if (fabs ((m_lo_n_lo * r + (0.5 * r * (n_lo % 2))) - y) <
+ fabs ((m_hi_n_lo * r + (0.5 * r * (n_lo % 2))) - y))
+ {
+ m_n_lo = m_lo_n_lo;
+ }
+ else
+ {
+ m_n_lo = m_hi_n_lo;
+ }
+
+ /* evaluate m candidates for n_hi */
+ m_lo_n_hi = (gint) ( (y / r) - 0.5 * (n_hi % 2) );
+ m_hi_n_hi = m_lo_n_hi + 1;
+
+ /* figure out which is the better candidate */
+ if (fabs((m_lo_n_hi * r + (0.5 * r * (n_hi % 2))) - y) <
+ fabs((m_hi_n_hi * r + (0.5 * r * (n_hi % 2))) - y))
+ {
+ m_n_hi = m_lo_n_hi;
+ }
+ else
+ {
+ m_n_hi = m_hi_n_hi;
+ }
+
+ /* Now, which is closer to [x,y]? we can use a somewhat
+ * abbreviated form of the distance formula since we only care
+ * about relative values.
+ */
+
+ x1 = (gint) (n_lo * h);
+ y1 = (gint) (m_n_lo * r + (0.5 * r * (n_lo % 2)));
+ x2 = (gint) (n_hi * h);
+ y2 = (gint) (m_n_hi * r + (0.5 * r * (n_hi % 2)));
+
+ if (((x - x1) * (x - x1) + (y - y1) * (y - y1)) <
+ ((x - x2) * (x - x2) + (y - y2) * (y - y2)))
+ {
+ gp->x = x1;
+ gp->y = y1;
+ }
+ else
+ {
+ gp->x = x2;
+ gp->y = y2;
+ }
+ }
+}
+
+static void
+draw_grid_polar (cairo_t *cr)
+{
+ gdouble inner_radius;
+ gdouble outer_radius;
+ gdouble max_radius = sqrt (SQR (preview_width) + SQR (preview_height));
+ gint current_sectors = 1;
+ PrimeFactors *factors = prime_factors_new (selvals.opts.grid_sectors_desired);
+ for (inner_radius = 0, outer_radius = selvals.opts.grid_radius_min;
+ outer_radius <= max_radius;
+ inner_radius = outer_radius, outer_radius += selvals.opts.grid_radius_interval)
+ {
+ gdouble t;
+ gdouble sector_size = 2 * G_PI / current_sectors;
+ cairo_arc (cr,
+ 0.5 + preview_width / 2.0,
+ 0.5 + preview_height / 2.0,
+ outer_radius, 0, 2 * G_PI);
+ cairo_stroke (cr);
+
+ while ((current_sectors < selvals.opts.grid_sectors_desired)
+ && (inner_radius * sector_size
+ > prime_factors_lookahead (factors) * selvals.opts.grid_granularity ))
+ {
+ current_sectors *= prime_factors_get (factors);
+ sector_size = 2 * G_PI / current_sectors;
+ }
+
+ for (t = 0 ; t < 2 * G_PI ; t += sector_size)
+ {
+ gdouble normal_x = cos (selvals.opts.grid_rotation+t);
+ gdouble normal_y = sin (selvals.opts.grid_rotation+t);
+ cairo_move_to (cr,
+ 0.5 + (preview_width / 2.0 + inner_radius * normal_x),
+ 0.5 + (preview_height / 2.0 - inner_radius * normal_y));
+ cairo_line_to (cr,
+ 0.5 + (preview_width / 2.0 + outer_radius * normal_x),
+ 0.5 + (preview_height / 2.0 - outer_radius * normal_y));
+ cairo_stroke (cr);
+ }
+ }
+
+ prime_factors_delete (factors);
+}
+
+
+static void
+draw_grid_sq (cairo_t *cr)
+{
+ gint step;
+ gint loop;
+
+ /* Draw the horizontal lines */
+ step = selvals.opts.gridspacing;
+
+ for (loop = 0 ; loop < preview_height ; loop += step)
+ {
+ cairo_move_to (cr, 0 + .5, loop + .5);
+ cairo_line_to (cr, preview_width + .5, loop + .5);
+ }
+
+ /* Draw the vertical lines */
+
+ for (loop = 0 ; loop < preview_width ; loop += step)
+ {
+ cairo_move_to (cr, loop + .5, 0 + .5);
+ cairo_line_to (cr, loop + .5, preview_height + .5);
+ }
+ cairo_stroke (cr);
+}
+
+static void
+draw_grid_iso (cairo_t *cr)
+{
+ /* vstep is an int since it's defined from grid size */
+ gint vstep;
+ gdouble loop;
+ gdouble hstep;
+
+ gdouble diagonal_start;
+ gdouble diagonal_end;
+ gdouble diagonal_width;
+ gdouble diagonal_height;
+
+ vstep = selvals.opts.gridspacing;
+ hstep = selvals.opts.gridspacing * COS_1o6PI_RAD;
+
+ /* Draw the vertical lines - These are easy */
+ for (loop = 0 ; loop < preview_width ; loop += hstep)
+ {
+ cairo_move_to (cr, loop, 0);
+ cairo_line_to (cr, loop, preview_height);
+ }
+ cairo_stroke (cr);
+
+ /* draw diag lines at a Theta of +/- 1/6 Pi Rad */
+
+ diagonal_start = -(((int)preview_width * TAN_1o6PI_RAD) - (((int)(preview_width * TAN_1o6PI_RAD)) % vstep));
+
+ diagonal_end = preview_height + (preview_width * TAN_1o6PI_RAD);
+ diagonal_end -= ((int)diagonal_end) % vstep;
+
+ diagonal_width = preview_width;
+ diagonal_height = preview_width * TAN_1o6PI_RAD;
+
+ /* Draw diag lines */
+ for (loop = diagonal_start ; loop < diagonal_end ; loop += vstep)
+ {
+ cairo_move_to (cr, 0, loop);
+ cairo_line_to (cr, diagonal_width, loop + diagonal_height);
+
+ cairo_move_to (cr, 0, loop);
+ cairo_line_to (cr, diagonal_width, loop - diagonal_height);
+ }
+ cairo_stroke (cr);
+}
+
+static cairo_t *
+gfig_get_grid_gc (cairo_t *cr, GtkWidget *w, gint gctype)
+{
+ switch (gctype)
+ {
+ default:
+ case GFIG_NORMAL_GC:
+ cairo_set_source_rgb (cr, .92, .92, .92);
+ break;
+ case GFIG_BLACK_GC:
+ cairo_set_source_rgb (cr, 0., 0., 0.);
+ break;
+ case GFIG_WHITE_GC:
+ cairo_set_source_rgb (cr, 1., 1., 1.);
+ break;
+ case GFIG_GREY_GC:
+ cairo_set_source_rgb (cr, .5, .5, .5);
+ break;
+ case GFIG_DARKER_GC:
+ cairo_set_source_rgb (cr, .25, .25, .25);
+ break;
+ case GFIG_LIGHTER_GC:
+ cairo_set_source_rgb (cr, .75, .75, .75);
+ break;
+ case GFIG_VERY_DARK_GC:
+ cairo_set_source_rgb (cr, .125, .125, .125);
+ break;
+ }
+
+ return cr;
+}
+
+void
+draw_grid (cairo_t *cr)
+{
+ /* Get the size of the preview and calc where the lines go */
+ /* Draw in prelight to start with... */
+ /* Always start in the upper left corner for rect.
+ */
+
+ if ((preview_width < selvals.opts.gridspacing &&
+ preview_height < selvals.opts.gridspacing))
+ {
+ /* Don't draw if they don't fit */
+ return;
+ }
+
+ if (selvals.opts.drawgrid)
+ gfig_get_grid_gc (cr, gfig_context->preview, grid_gc_type);
+ else
+ return;
+
+ cairo_set_line_width (cr, 1.);
+ if (selvals.opts.gridtype == RECT_GRID)
+ draw_grid_sq (cr);
+ else if (selvals.opts.gridtype == POLAR_GRID)
+ draw_grid_polar (cr);
+ else if (selvals.opts.gridtype == ISO_GRID)
+ draw_grid_iso (cr);
+}
+
+