plug-ins, pdb: remove the maze plug-in and add a PDB compat proc

This commit is contained in:
Michael Natterer 2015-09-04 00:31:37 +02:00
parent 3f02b2aaf2
commit a4a0ec9598
16 changed files with 225 additions and 2492 deletions

View File

@ -28,7 +28,7 @@
#include "internal-procs.h"
/* 772 procedures registered total */
/* 773 procedures registered total */
void
internal_procs_init (GimpPDB *pdb)

View File

@ -2397,6 +2397,73 @@ plug_in_make_seamless_invoker (GimpProcedure *procedure,
error ? *error : NULL);
}
static GimpValueArray *
plug_in_maze_invoker (GimpProcedure *procedure,
Gimp *gimp,
GimpContext *context,
GimpProgress *progress,
const GimpValueArray *args,
GError **error)
{
gboolean success = TRUE;
GimpDrawable *drawable;
gint16 width;
gint16 height;
guint8 tileable;
guint8 algorithm;
gint32 seed;
drawable = gimp_value_get_drawable (gimp_value_array_index (args, 2), gimp);
width = g_value_get_int (gimp_value_array_index (args, 3));
height = g_value_get_int (gimp_value_array_index (args, 4));
tileable = g_value_get_uint (gimp_value_array_index (args, 5));
algorithm = g_value_get_uint (gimp_value_array_index (args, 6));
seed = g_value_get_int (gimp_value_array_index (args, 7));
if (success)
{
if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
GIMP_PDB_ITEM_CONTENT, error) &&
gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
{
GeglNode *node;
GeglColor *fg_color;
GeglColor *bg_color;
GimpRGB color;
gimp_context_get_foreground (context, &color);
fg_color = gimp_gegl_color_new (&color);
gimp_context_get_background (context, &color);
bg_color = gimp_gegl_color_new (&color);
node = gegl_node_new_child (NULL,
"operation", "gegl:maze",
"x", width,
"y", height,
"algorithm-type", algorithm,
"tilable", tileable,
"seed", seed,
"fg-color", fg_color,
"bg-color", bg_color,
NULL);
g_object_unref (fg_color);
g_object_unref (bg_color);
gimp_drawable_apply_operation (drawable, progress,
C_("undo-type", "Maze"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
return gimp_procedure_get_return_values (procedure, success,
error ? *error : NULL);
}
static GimpValueArray *
plug_in_mblur_invoker (GimpProcedure *procedure,
Gimp *gimp,
@ -6452,6 +6519,84 @@ register_plug_in_compat_procs (GimpPDB *pdb)
gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* gimp-plug-in-maze
*/
procedure = gimp_procedure_new (plug_in_maze_invoker);
gimp_object_set_static_name (GIMP_OBJECT (procedure),
"plug-in-maze");
gimp_procedure_set_static_strings (procedure,
"plug-in-maze",
"Draw a labyrinth",
"Generates a maze using either the depth-first search method or Prim's algorithm. Can make tileable mazes too.",
"Compatibility procedure. Please see 'gegl:maze' for credits.",
"Compatibility procedure. Please see 'gegl:maze' for credits.",
"2015",
NULL);
gimp_procedure_add_argument (procedure,
g_param_spec_enum ("run-mode",
"run mode",
"The run mode",
GIMP_TYPE_RUN_MODE,
GIMP_RUN_INTERACTIVE,
GIMP_PARAM_READWRITE));
gimp_procedure_add_argument (procedure,
gimp_param_spec_image_id ("image",
"image",
"Input image (unused)",
pdb->gimp, FALSE,
GIMP_PARAM_READWRITE));
gimp_procedure_add_argument (procedure,
gimp_param_spec_drawable_id ("drawable",
"drawable",
"Input drawable",
pdb->gimp, FALSE,
GIMP_PARAM_READWRITE));
gimp_procedure_add_argument (procedure,
gimp_param_spec_int16 ("width",
"width",
"Width of the passages",
1, 1024, 1,
GIMP_PARAM_READWRITE));
gimp_procedure_add_argument (procedure,
gimp_param_spec_int16 ("height",
"height",
"Height of the passages",
1, 1024, 1,
GIMP_PARAM_READWRITE));
gimp_procedure_add_argument (procedure,
gimp_param_spec_int8 ("tileable",
"tileable",
"Tileable maze? (TRUE or FALSE)",
0, 1, 0,
GIMP_PARAM_READWRITE));
gimp_procedure_add_argument (procedure,
gimp_param_spec_int8 ("algorithm",
"algorithm",
"Generation algorithm (0 = DEPTH FIRST, 1 = PRIM'S ALGORITHM)",
0, 1, 0,
GIMP_PARAM_READWRITE));
gimp_procedure_add_argument (procedure,
gimp_param_spec_int32 ("seed",
"seed",
"Random Seed",
G_MININT32, G_MAXINT32, 0,
GIMP_PARAM_READWRITE));
gimp_procedure_add_argument (procedure,
gimp_param_spec_int16 ("multiple",
"multiple",
"Multiple (use 57)",
G_MININT16, G_MAXINT16, 0,
GIMP_PARAM_READWRITE));
gimp_procedure_add_argument (procedure,
gimp_param_spec_int16 ("offset",
"offset",
"Offset (use 1)",
G_MININT16, G_MAXINT16, 0,
GIMP_PARAM_READWRITE));
gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* gimp-plug-in-mblur
*/

View File

@ -2312,7 +2312,6 @@ plug-ins/imagemap/images/Makefile
plug-ins/lighting/Makefile
plug-ins/lighting/images/Makefile
plug-ins/map-object/Makefile
plug-ins/maze/Makefile
plug-ins/pagecurl/Makefile
plug-ins/print/Makefile
plug-ins/pygimp/Makefile

View File

@ -52,7 +52,6 @@ SUBDIRS = \
imagemap \
lighting \
map-object \
maze \
pagecurl \
$(print) \
selection-to-path \

View File

@ -1,7 +0,0 @@
/Makefile.in
/Makefile
/.deps
/_libs
/.libs
/maze
/maze.exe

View File

@ -1,53 +0,0 @@
## Process this file with automake to produce Makefile.in
libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
if OS_WIN32
mwindows = -mwindows
endif
if HAVE_WINDRES
include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
maze_RC = maze.rc.o
endif
AM_LDFLAGS = $(mwindows)
libexecdir = $(gimpplugindir)/plug-ins
libexec_PROGRAMS = maze
maze_SOURCES = \
maze.c \
maze.h \
maze-algorithms.c \
maze-algorithms.h \
maze-dialog.c \
maze-dialog.h \
maze-utils.c \
maze-utils.h
AM_CPPFLAGS = \
-I$(top_srcdir) \
$(GTK_CFLAGS) \
$(GEGL_CFLAGS) \
-I$(includedir)
LDADD = \
$(libgimpui) \
$(libgimpwidgets) \
$(libgimpconfig) \
$(libgimp) \
$(libgimpcolor) \
$(libgimpmath) \
$(libgimpbase) \
$(GTK_LIBS) \
$(RT_LIBS) \
$(INTLLIBS) \
$(maze_RC)

View File

@ -1,646 +0,0 @@
/* $Id$
* Contains routines for generating mazes, somewhat intertwined with
* Gimp plug-in-maze specific stuff.
*
* Kevin Turner <acapnotic@users.sourceforge.net>
* http://gimp-plug-ins.sourceforge.net/maze/
*/
/* mazegen code from rec.games.programmer's maze-faq:
* * maz.c - generate a maze
* *
* * algorithm posted to rec.games.programmer by jallen@ic.sunysb.edu
* * program cleaned and reorganized by mzraly@ldbvax.dnet.lotus.com
* *
* * don't make people pay for this, or I'll jump up and down and
* * yell and scream and embarrass you in front of your friends...
*/
/* I've put a HTMLized version of the FAQ up at
* http://www.poboxes.com/kevint/gimp/maze-faq/maze-faq.html
*/
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <stdlib.h>
#include "libgimp/gimp.h"
#include "maze.h"
#include "maze-algorithms.h"
#include "libgimp/stdplugins-intl.h"
#define ABSMOD(A,B) (((A) < 0) ? (((B) + (A)) % (B)) : ((A) % (B)))
/* Since we are using a 1D array on 2D space, we need to do our own
calculations. (Ok, so there are ways of doing dynamically allocated
2D arrays, but we started this way, so let's stick with it. */
/* The difference between CELL_* and WALL_* is that cell moves two spaces,
while wall moves one. */
/* Macros assume that x and y will be defined where they are used. */
/* A return of -1 means "no such place, don't go there". */
#define CELL_UP(POS) ((POS) < (x*2) ? -1 : (POS) - x - x)
#define CELL_DOWN(POS) ((POS) >= x*(y-2) ? -1 : (POS) + x + x)
#define CELL_LEFT(POS) (((POS) % x) <= 1 ? -1 : (POS) - 2)
#define CELL_RIGHT(POS) (((POS) % x) >= (x - 2) ? -1 : (POS) + 2)
/* With walls, we don't need to check for boundaries, since we are
assured that there *is* a valid cell on the other side of the
wall. */
#define WALL_UP(POS) ((POS) - x)
#define WALL_DOWN(POS) ((POS) + x)
#define WALL_LEFT(POS) ((POS) - 1)
#define WALL_RIGHT(POS) ((POS) + 1)
/***** For tileable mazes *****/
#define CELL_UP_TILEABLE(POS) ((POS) < (x*2) ? x*(y-2)+(POS) : (POS) - x - x)
#define CELL_DOWN_TILEABLE(POS) ((POS) >= x*(y-2) ? (POS) - x*(y-2) : (POS) + x + x)
#define CELL_LEFT_TILEABLE(POS) (((POS) % x) <= 1 ? (POS) + x - 2 : (POS) - 2)
#define CELL_RIGHT_TILEABLE(POS) (((POS) % x) >= (x - 2) ? (POS) + 2 - x : (POS) + 2)
/* Up and left need checks, but down and right should never have to
wrap on an even sized maze. */
#define WALL_UP_TILEABLE(POS) ((POS) < x ? x*(y-1)+(POS) : (POS) - x)
#define WALL_DOWN_TILEABLE(POS) ((POS) + x)
#define WALL_LEFT_TILEABLE(POS) (((POS) % x) == 0 ? (POS) + x - 1 : (POS) - 1)
#define WALL_RIGHT_TILEABLE(POS) ((POS) + 1)
/* Down and right with checks.
#define WALL_DOWN_TILEABLE(POS) ((POS) >= x*(y-1) ? (POS) - x * (y-1) : (POS) + x)
#define WALL_RIGHT_TILEABLE(POS) (((POS) % x) == (x - 1) ? (POS) + 1 - x : (POS) + 1)
*/
/* The Incredible Recursive Maze Generation Routine */
/* Ripped from rec.programmers.games maze-faq */
/* Modified and commented by me, Kevin Turner. */
void
mazegen (gint pos,
guchar *maz,
gint x,
gint y,
gint rnd)
{
gchar d, i;
gint c = 0;
gint j = 1;
/* Punch a hole here... */
maz[pos] = IN;
/* If there is a wall two rows above us, bit 1 is 1. */
while ((d= (pos <= (x * 2) ? 0 : (maz[pos - x - x ] ? 0 : 1))
/* If there is a wall two rows below us, bit 2 is 1. */
| (pos >= x * (y - 2) ? 0 : (maz[pos + x + x] ? 0 : 2))
/* If there is a wall two columns to the right, bit 3 is 1. */
| (pos % x == x - 2 ? 0 : (maz[pos + 2] ? 0 : 4))
/* If there is a wall two colums to the left, bit 4 is 1. */
| ((pos % x == 1 ) ? 0 : (maz[pos-2] ? 0 : 8))))
{
/* Note if all bits are 0, d is false, we don't do this
while loop, we don't call ourselves again, so this branch
is done. */
/* I see what this loop does (more or less), but I don't know
_why_ it does it this way... I also haven't figured out exactly
which values of multiple will work and which won't. */
do
{
rnd = (rnd * mvals.multiple + mvals.offset);
i = 3 & (rnd / d);
if (++c > 100)
{ /* Break and try to salvage something */
i=99; /* if it looks like we're going to be */
break; /* here forever... */
}
}
while (!(d & (1 << i)));
/* ...While there's *not* a wall in direction i. */
/* (stop looping when there is) */
switch (i)
{
case 0: /* Go in the direction we just figured . . . */
j = -x;
break;
case 1:
j = x;
break;
case 2:
j = 1;
break;
case 3:
j = -1;
break;
case 99:
return; /* Hey neat, broken mazes! */
break; /* (Umm... Wow... Yeah, neat.) */
default:
g_warning ("maze: mazegen: Going in unknown direction.\n"
"i: %d, d: %d, seed: %d, mw: %d, mh: %d, mult: %d, offset: %d\n",
i, d,mvals.seed, x, y, mvals.multiple, mvals.offset);
break;
}
/* And punch a hole there. */
maz[pos + j] = 1;
/* Now, start again just past where we punched the hole... */
mazegen (pos + 2 * j, maz, x, y, rnd);
}
return;
}
/* Tileable mazes are my creation, based on the routine above. */
void
mazegen_tileable (gint pos,
guchar *maz,
gint x,
gint y,
gint rnd)
{
gchar d, i;
gint c = 0;
gint npos = 2;
/* Punch a hole here... */
maz[pos] = IN;
/* If there is a wall two rows above us, bit 1 is 1. */
while ((d= (maz[CELL_UP_TILEABLE(pos)] ? 0 : 1)
/* If there is a wall two rows below us, bit 2 is 1. */
| (maz[CELL_DOWN_TILEABLE(pos)] ? 0 : 2)
/* If there is a wall two columns to the right, bit 3 is 1. */
| (maz[CELL_RIGHT_TILEABLE(pos)] ? 0 : 4)
/* If there is a wall two colums to the left, bit 4 is 1. */
| (maz[CELL_LEFT_TILEABLE(pos)] ? 0 : 8)))
{
/* Note if all bits are 0, d is false, we don't do this
while loop, we don't call ourselves again, so this branch
is done. */
/* I see what this loop does (more or less), but I don't know
_why_ it does it this way... I also haven't figured out exactly
which values of multiple will work and which won't. */
do
{
rnd = (rnd * mvals.multiple + mvals.offset);
i = 3 & (rnd / d);
if (++c > 100)
{ /* Break and try to salvage something */
i=99; /* if it looks like we're going to be */
break; /* here forever... */
}
}
while (!(d & (1 << i)));
/* ...While there's *not* a wall in direction i. */
/* (stop looping when there is) */
switch (i)
{
case 0: /* Go in the direction we just figured . . . */
maz[WALL_UP_TILEABLE (pos)] = IN;
npos = CELL_UP_TILEABLE (pos);
break;
case 1:
maz[WALL_DOWN_TILEABLE (pos)] = IN;
npos = CELL_DOWN_TILEABLE (pos);
break;
case 2:
maz[WALL_RIGHT_TILEABLE (pos)] = IN;
npos = CELL_RIGHT_TILEABLE (pos);
break;
case 3:
maz[WALL_LEFT_TILEABLE (pos)] = IN;
npos = CELL_LEFT_TILEABLE (pos);
break;
case 99:
return; /* Hey neat, broken mazes! */
break; /* (Umm... Wow... Yeah, neat.) */
default:
g_warning ("maze: mazegen_tileable: Going in unknown direction.\n"
"i: %d, d: %d, seed: %d, mw: %d, mh: %d, mult: %d, offset: %d\n",
i, d,mvals.seed, x, y, mvals.multiple, mvals.offset);
break;
}
/* Now, start again just past where we punched the hole... */
mazegen_tileable (npos, maz, x, y, rnd);
}
return;
}
/* This function (as well as prim_tileable) make use of the somewhat
unclean practice of storing ints as pointers. I've been informed
that this may cause problems with 64-bit stuff. However, hopefully
it will be okay, since the only values stored are positive. If it
does break, let me know, and I'll go cry in a corner for a while
before I get up the strength to re-code it. */
void
prim (gint pos,
guchar *maz,
guint x,
guint y)
{
GSList *front_cells = NULL;
guint current;
gint up, down, left, right; /* Not unsigned, because macros return -1. */
guint progress = 0;
guint max_progress;
char d, i;
guint c = 0;
gint rnd = mvals.seed;
g_rand_set_seed (gr, rnd);
gimp_progress_init (_("Constructing maze using Prim's Algorithm"));
/* OUT is zero, so we should be already initalized. */
max_progress = x * y / 4;
/* Starting position has already been determined by the calling function. */
maz[pos] = IN;
/* For now, repeating everything four times seems manageable. But when
Gimp is extended to drawings in n-dimensional space instead of 2D,
this will require a bit of a re-write. */
/* Add frontier. */
up = CELL_UP (pos);
down = CELL_DOWN (pos);
left = CELL_LEFT (pos);
right = CELL_RIGHT (pos);
if (up >= 0)
{
maz[up] = FRONTIER;
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (up));
}
if (down >= 0)
{
maz[down] = FRONTIER;
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (down));
}
if (left >= 0)
{
maz[left] = FRONTIER;
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (left));
}
if (right >= 0)
{
maz[right] = FRONTIER;
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (right));
}
/* While frontier is not empty do the following... */
while (g_slist_length (front_cells) > 0)
{
/* Remove one cell at random from frontier and place it in IN. */
current = g_rand_int_range (gr, 0, g_slist_length (front_cells));
pos = GPOINTER_TO_INT (g_slist_nth (front_cells, current)->data);
front_cells = g_slist_remove (front_cells, GINT_TO_POINTER (pos));
maz[pos] = IN;
/* If the cell has any neighbors in OUT, remove them from
OUT and place them in FRONTIER. */
up = CELL_UP (pos);
down = CELL_DOWN (pos);
left = CELL_LEFT (pos);
right = CELL_RIGHT (pos);
d = 0;
if (up >= 0)
{
switch (maz[up])
{
case OUT:
maz[up] = FRONTIER;
front_cells = g_slist_prepend (front_cells,
GINT_TO_POINTER (up));
break;
case IN:
d = 1;
break;
default:
break;
}
}
if (down >= 0)
{
switch (maz[down])
{
case OUT:
maz[down] = FRONTIER;
front_cells = g_slist_prepend (front_cells,
GINT_TO_POINTER (down));
break;
case IN:
d = d | 2;
break;
default:
break;
}
}
if (left >= 0)
{
switch (maz[left])
{
case OUT:
maz[left] = FRONTIER;
front_cells = g_slist_prepend (front_cells,
GINT_TO_POINTER (left));
break;
case IN:
d = d | 4;
break;
default:
break;
}
}
if (right >= 0)
{
switch (maz[right])
{
case OUT:
maz[right] = FRONTIER;
front_cells = g_slist_prepend (front_cells,
GINT_TO_POINTER (right));
break;
case IN:
d = d | 8;
break;
default:
break;
}
}
/* The cell is guaranteed to have at least one neighbor in
IN (otherwise it would not have been in FRONTIER); pick
one such neighbor at random and connect it to the new
cell (ie knock out a wall). */
if (!d)
{
g_warning ("maze: prim: Lack of neighbors.\n"
"seed: %d, mw: %d, mh: %d, mult: %d, offset: %d\n",
mvals.seed, x, y, mvals.multiple, mvals.offset);
break;
}
c = 0;
do
{
rnd = (rnd * mvals.multiple + mvals.offset);
i = 3 & (rnd / d);
if (++c > 100)
{ /* Break and try to salvage something */
i = 99; /* if it looks like we're going to be */
break; /* here forever... */
}
}
while (!(d & (1 << i)));
switch (i)
{
case 0:
maz[WALL_UP (pos)] = IN;
break;
case 1:
maz[WALL_DOWN (pos)] = IN;
break;
case 2:
maz[WALL_LEFT (pos)] = IN;
break;
case 3:
maz[WALL_RIGHT (pos)] = IN;
break;
case 99:
break;
default:
g_warning ("maze: prim: Going in unknown direction.\n"
"i: %d, d: %d, seed: %d, mw: %d, mh: %d, mult: %d, offset: %d\n",
i, d, mvals.seed, x, y, mvals.multiple, mvals.offset);
}
if (progress++ % PRIMS_PROGRESS_UPDATE)
gimp_progress_update ((double) progress / (double) max_progress);
}
gimp_progress_update (1.0);
g_slist_free (front_cells);
}
void
prim_tileable (guchar *maz,
guint x,
guint y)
{
GSList *front_cells=NULL;
guint current, pos;
guint up, down, left, right;
guint progress = 0;
guint max_progress;
char d, i;
guint c = 0;
gint rnd = mvals.seed;
g_rand_set_seed (gr, rnd);
gimp_progress_init (_("Constructing tileable maze using Prim's Algorithm"));
/* OUT is zero, so we should be already initalized. */
max_progress = x * y / 4;
/* Pick someplace to start. */
pos = x * 2 * g_rand_int_range (gr, 0, y/2) + 2 * g_rand_int_range(gr, 0, x/2);
maz[pos] = IN;
/* Add frontier. */
up = CELL_UP_TILEABLE (pos);
down = CELL_DOWN_TILEABLE (pos);
left = CELL_LEFT_TILEABLE (pos);
right = CELL_RIGHT_TILEABLE (pos);
maz[up] = maz[down] = maz[left] = maz[right] = FRONTIER;
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (up));
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (down));
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (left));
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (right));
/* While frontier is not empty do the following... */
while (g_slist_length (front_cells) > 0)
{
/* Remove one cell at random from frontier and place it in IN. */
current = g_rand_int_range (gr, 0, g_slist_length (front_cells));
pos = GPOINTER_TO_UINT (g_slist_nth (front_cells, current)->data);
front_cells = g_slist_remove (front_cells, GUINT_TO_POINTER (pos));
maz[pos] = IN;
/* If the cell has any neighbors in OUT, remove them from
OUT and place them in FRONTIER. */
up = CELL_UP_TILEABLE (pos);
down = CELL_DOWN_TILEABLE (pos);
left = CELL_LEFT_TILEABLE (pos);
right = CELL_RIGHT_TILEABLE (pos);
d = 0;
switch (maz[up])
{
case OUT:
maz[up] = FRONTIER;
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (up));
break;
case IN:
d = 1;
break;
default:
break;
}
switch (maz[down])
{
case OUT:
maz[down] = FRONTIER;
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (down));
break;
case IN:
d = d | 2;
break;
default:
break;
}
switch (maz[left])
{
case OUT:
maz[left] = FRONTIER;
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (left));
break;
case IN:
d = d | 4;
break;
default:
break;
}
switch (maz[right])
{
case OUT:
maz[right] = FRONTIER;
front_cells = g_slist_append (front_cells, GINT_TO_POINTER (right));
break;
case IN:
d = d | 8;
break;
default:
break;
}
/* The cell is guaranteed to have at least one neighbor in
IN (otherwise it would not have been in FRONTIER); pick
one such neighbor at random and connect it to the new
cell (ie knock out a wall). */
if (!d)
{
g_warning ("maze: prim's tileable: Lack of neighbors.\n"
"seed: %d, mw: %d, mh: %d, mult: %d, offset: %d\n",
mvals.seed, x, y, mvals.multiple, mvals.offset);
break;
}
c = 0;
do
{
rnd = (rnd * mvals.multiple + mvals.offset);
i = 3 & (rnd / d);
if (++c > 100)
{ /* Break and try to salvage something */
i = 99; /* if it looks like we're going to be */
break; /* here forever... */
}
}
while (!(d & (1 << i)));
switch (i)
{
case 0:
maz[WALL_UP_TILEABLE (pos)] = IN;
break;
case 1:
maz[WALL_DOWN_TILEABLE (pos)] = IN;
break;
case 2:
maz[WALL_LEFT_TILEABLE (pos)] = IN;
break;
case 3:
maz[WALL_RIGHT_TILEABLE (pos)] = IN;
break;
case 99:
break;
default:
g_warning ("maze: prim's tileable: Going in unknown direction.\n"
"i: %d, d: %d, seed: %d, mw: %d, mh: %d, mult: %d, offset: %d\n",
i, d, mvals.seed, x, y, mvals.multiple, mvals.offset);
}
if (progress++ % PRIMS_PROGRESS_UPDATE)
gimp_progress_update ((double) progress / (double) max_progress);
}
gimp_progress_update (1.0);
g_slist_free (front_cells);
}

View File

@ -1,41 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef __MAZE_ALGORITHMS_H__
#define __MAZE_ALGORITHMS_H__
void mazegen (gint pos,
guchar *maz,
gint x,
gint y,
gint rnd);
void mazegen_tileable (gint pos,
guchar *maz,
gint x,
gint y,
gint rnd);
void prim (gint pos,
guchar *maz,
guint x,
guint y);
void prim_tileable (guchar *maz,
guint x,
guint y);
#endif /* __MAZE_ALGORITHMS_H__ */

View File

@ -1,709 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; c-basic-offset: 2; -*- */
/*
* User interface for plug-in-maze.
*
* Implemented as a GIMP 0.99 Plugin by
* Kevin Turner <acapnotic@users.sourceforge.net>
* http://gimp-plug-ins.sourceforge.net/maze/
*/
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <stdlib.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include "maze.h"
#include "maze-dialog.h"
#include "libgimp/stdplugins-intl.h"
#define BORDER_TOLERANCE 1.00 /* maximum ratio of (max % divs) to width */
#define ENTRY_WIDTH 75
/* entscale stuff begin */
/* FIXME: Entry-Scale stuff is probably in libgimpui by now.
Should use that instead.*/
/* Indeed! By using the gimp_scale_entry you could get along without the
EntscaleIntData structure, since it has accessors to all objects (Sven) */
#define ENTSCALE_INT_SCALE_WIDTH 125
#define ENTSCALE_INT_ENTRY_WIDTH 40
#define MORE 1
#define LESS -1
typedef void (* EntscaleIntCallback) (gint value,
gpointer data);
typedef struct
{
GtkAdjustment *adjustment;
GtkWidget *entry;
gboolean constraint;
EntscaleIntCallback callback;
gpointer call_data;
} EntscaleIntData;
/* entscale stuff end */
/* one buffer fits all */
#define BUFSIZE 128
static gchar buffer[BUFSIZE];
/* Looking back, it would probably have been easier to completely
* re-write the whole entry/scale thing to work with the divbox stuff.
* It would undoubtably be cleaner code. But since I already *had*
* the entry/scale routines, I was under the (somewhat mistaken)
* impression that it would be easier to work with them... */
/* Now, it goes like this:
To update entscale (width) when div_entry changes:
entscale_int_new has been slightly modified to return a pointer to
its entry widget.
This is fed to divbox_new as a "friend", which is in turn fed to
the div_entry_callback routine. And that's not really so bad,
except...
Oh, well, maybe it isn't so bad. We can play with our friend's
userdata to block his callbacks so we don't get feedback loops,
that works nicely enough.
To update div_entry when entscale (width) changes:
The entry/scale setup graciously provides for callbacks. However,
this means we need to know about div_entry when we set up
entry/scale, which we don't... Chicken and egg problem. So we
set up a pointer to where div_entry will be, and pass this
through to divbox_new when it happens.
We need to block signal handlers for div_entry this time. We
happen to know that div_entry's callback data is our old
"friend", so we pull our friend out from where we stuck him in
the entry's userdata... Hopefully that does it.
*/
static void maze_message (const gchar *message);
static void div_button_callback (GtkWidget *button,
GtkWidget *entry);
static void div_entry_callback (GtkWidget *entry,
GtkWidget *friend);
static void height_width_callback (gint width,
GtkWidget **div_entry);
static GtkWidget * divbox_new (gint *max,
GtkWidget *friend,
GtkWidget **div_entry);
/* entscale stuff begin */
static GtkWidget * entscale_int_new (GtkWidget *table,
gint x,
gint y,
const gchar *caption,
gint *intvar,
gint min,
gint max,
gboolean constraint,
EntscaleIntCallback callback,
gpointer data);
static void entscale_int_scale_update (GtkAdjustment *adjustment,
gpointer data);
static void entscale_int_entry_update (GtkWidget *widget,
gpointer data);
#define ISODD(X) ((X) & 1)
/* entscale stuff end */
static GtkWidget *msg_label;
gboolean
maze_dialog (void)
{
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *vbox2;
GtkWidget *table;
GtkWidget *table2;
GtkWidget *tilecheck;
GtkWidget *width_entry;
GtkWidget *height_entry;
GtkWidget *hbox;
GtkWidget *frame;
GtkSizeGroup *group;
gboolean run;
gint trow = 0;
gimp_ui_init (PLUG_IN_BINARY, FALSE);
dialog = gimp_dialog_new (_("Maze"), PLUG_IN_ROLE,
NULL, 0,
gimp_standard_help_func, PLUG_IN_PROC,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
gimp_window_set_transient (GTK_WINDOW (dialog));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
vbox, FALSE, FALSE, 0);
/* The maze size frame */
frame = gimp_frame_new (_("Maze Size"));
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
table = gtk_table_new (6, 3, FALSE);
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
gtk_container_add (GTK_CONTAINER (frame), table);
gtk_widget_show (table);
group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
/* entscale == Entry and Scale pair function found in pixelize.c */
width_entry = entscale_int_new (table, 0, trow, _("Width (pixels):"),
&mvals.width,
1, sel_w/4, TRUE,
(EntscaleIntCallback) height_width_callback,
&width_entry);
trow++;
/* Number of Divisions entry */
hbox = divbox_new (&sel_w, width_entry, &width_entry);
g_snprintf (buffer, BUFSIZE, "%d", (sel_w / mvals.width));
gtk_entry_set_text (GTK_ENTRY (width_entry), buffer);
gimp_table_attach_aligned (GTK_TABLE (table), 0, trow,
_("Pieces:"), 0.0, 0.5,
hbox, 1, TRUE);
gtk_table_set_row_spacing (GTK_TABLE (table), trow, 12);
trow++;
height_entry = entscale_int_new (table, 0, trow, _("Height (pixels):"),
&mvals.height,
1, sel_h/4, TRUE,
(EntscaleIntCallback) height_width_callback,
&height_entry);
trow++;
hbox = divbox_new (&sel_h, height_entry, &height_entry);
g_snprintf (buffer, BUFSIZE, "%d", (sel_h / mvals.height));
gtk_entry_set_text (GTK_ENTRY (height_entry), buffer);
gimp_table_attach_aligned (GTK_TABLE (table), 0, trow,
_("Pieces:"), 0.0, 0.5,
hbox, 1, TRUE);
gtk_table_set_row_spacing (GTK_TABLE (table), trow, 12);
trow++;
g_object_unref (group);
/* The maze algorithm frame */
frame = gimp_frame_new (_("Algorithm"));
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_add (GTK_CONTAINER (frame), vbox2);
gtk_widget_show (vbox2);
/* Seed input box */
table2 = gtk_table_new (3, 3, FALSE);
gtk_table_set_col_spacings (GTK_TABLE (table2), 6);
gtk_table_set_row_spacings (GTK_TABLE (table2), 6);
gtk_box_pack_start (GTK_BOX (vbox2), table2, FALSE, FALSE, 0);
gtk_widget_show (table2);
hbox = gimp_random_seed_new (&mvals.seed, &mvals.random_seed);
gimp_table_attach_aligned (GTK_TABLE (table2), 0, 0,
_("Seed:"), 0.0, 0.5,
hbox, 1, TRUE);
/* Algorithm Choice */
frame =
gimp_int_radio_group_new (FALSE, NULL,
G_CALLBACK (gimp_radio_button_update),
&mvals.algorithm, mvals.algorithm,
_("Depth first"), DEPTH_FIRST, NULL,
_("Prim's algorithm"), PRIMS_ALGORITHM, NULL,
NULL);
gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
/* Tileable checkbox */
tilecheck = gtk_check_button_new_with_label (_("Tileable"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tilecheck), mvals.tile);
g_signal_connect (tilecheck, "clicked",
G_CALLBACK (gimp_toggle_button_update),
&mvals.tile);
gtk_box_pack_start (GTK_BOX (vbox2), tilecheck, FALSE, FALSE, 0);
msg_label = gtk_label_new (NULL);
gimp_label_set_attributes (GTK_LABEL (msg_label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
-1);
gtk_box_pack_start (GTK_BOX (vbox), msg_label, FALSE, FALSE, 0);
gtk_widget_show_all (dialog);
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
gtk_widget_destroy (dialog);
return run;
}
static GtkWidget*
divbox_new (gint *max,
GtkWidget *friend,
GtkWidget **div_entry)
{
GtkWidget *align;
GtkWidget *hbox;
GtkWidget *arrowl, *arrowr;
GtkWidget *buttonl, *buttonr;
#if DIVBOX_LOOKS_LIKE_SPINBUTTON
GtkWidget *buttonbox;
#endif
align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_container_add (GTK_CONTAINER (align), hbox);
#if DIVBOX_LOOKS_LIKE_SPINBUTTON
arrowl = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
arrowr = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_OUT);
#else
arrowl = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_IN);
arrowr = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_IN);
#endif
buttonl = gtk_button_new ();
buttonr = gtk_button_new ();
g_object_set_data (G_OBJECT (buttonl), "direction", GINT_TO_POINTER (LESS));
g_object_set_data (G_OBJECT (buttonr), "direction", GINT_TO_POINTER (MORE));
*div_entry = gtk_entry_new ();
g_object_set_data (G_OBJECT (*div_entry), "max", max);
g_object_set_data (G_OBJECT (*div_entry), "friend", friend);
gtk_container_add (GTK_CONTAINER (buttonl), arrowl);
gtk_container_add (GTK_CONTAINER (buttonr), arrowr);
gtk_widget_set_size_request (*div_entry, ENTRY_WIDTH, -1);
#if DIVBOX_LOOKS_LIKE_SPINBUTTON
buttonbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start (GTK_BOX (buttonbox), buttonr, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (buttonbox), buttonl, FALSE, FALSE, 0);
gtk_widget_show (buttonbox);
gtk_box_pack_start (GTK_BOX (hbox), *div_entry, FALSE, FALSE, 2);
gtk_box_pack_start (GTK_BOX (hbox), buttonbox, FALSE, FALSE, 0);
#else
gtk_box_pack_start (GTK_BOX (hbox), buttonl, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), *div_entry, FALSE, FALSE, 2);
gtk_box_pack_start (GTK_BOX (hbox), buttonr, FALSE, FALSE, 0);
#endif
gtk_widget_show_all (hbox);
g_signal_connect (buttonl, "clicked",
G_CALLBACK (div_button_callback),
*div_entry);
g_signal_connect (buttonr, "clicked",
G_CALLBACK (div_button_callback),
*div_entry);
g_signal_connect (*div_entry, "changed",
G_CALLBACK (div_entry_callback),
friend);
return align;
}
static void
div_button_callback (GtkWidget *button,
GtkWidget *entry)
{
const gchar *text;
gint max, divs;
gint direction;
direction = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button),
"direction"));
max = *((gint*) g_object_get_data (G_OBJECT (entry), "max"));
/* Tileable mazes shall have only an even number of divisions.
Other mazes have odd. */
/* Sanity check: */
if (mvals.tile && ISODD(max))
{
maze_message (_("Selection size is not even.\n"
"Tileable maze won't work perfectly."));
return;
}
text = gtk_entry_get_text (GTK_ENTRY (entry));
divs = atoi (text);
if (divs <= 3)
{
divs = mvals.tile ?
max - (ISODD(max) ? 1 : 0) :
max - (ISODD(max) ? 0 : 1);
}
else if (divs > max)
{
divs = mvals.tile ? 6 : 5;
}
/* Makes sure we're appropriately even or odd, adjusting in the
proper direction. */
divs += direction * (mvals.tile ? (ISODD(divs) ? 1 : 0) :
(ISODD(divs) ? 0 : 1));
if (mvals.tile)
{
if (direction > 0)
{
do
{
divs += 2;
if (divs > max)
divs = 4;
}
while (max % divs);
}
else
{ /* direction < 0 */
do
{
divs -= 2;
if (divs < 4)
divs = max - (max & 1);
}
while (max % divs);
} /* endif direction < 0 */
}
else
{ /* If not tiling, having a non-zero remainder doesn't bother us much. */
if (direction > 0)
{
do
{
divs += 2;
}
while ((max % divs > max / divs * BORDER_TOLERANCE) && divs < max);
}
else
{ /* direction < 0 */
do
{
divs -= 2;
}
while ((max % divs > max / divs * BORDER_TOLERANCE) && divs > 5);
} /* endif direction < 0 */
} /* endif not tiling */
if (divs <= 3)
{
divs = mvals.tile ?
max - (ISODD(max) ? 1 : 0) :
max - (ISODD(max) ? 0 : 1);
}
else if (divs > max)
{
divs = mvals.tile ? 4 : 5;
}
g_snprintf (buffer, BUFSIZE, "%d", divs);
gtk_entry_set_text (GTK_ENTRY (entry), buffer);
return;
}
static void
div_entry_callback (GtkWidget *entry,
GtkWidget *friend)
{
gint divs, width, max;
EntscaleIntData *userdata;
EntscaleIntCallback friend_callback;
divs = atoi (gtk_entry_get_text (GTK_ENTRY (entry)));
if (divs < 4) /* If this is under 4 (e.g. 0), something's weird. */
return; /* But it'll probably be ok, so just return and ignore. */
max = *((gint*) g_object_get_data (G_OBJECT (entry), "max"));
/* I say "width" here, but it could be height.*/
width = max / divs;
g_snprintf (buffer, BUFSIZE, "%d", width);
/* No tagbacks from our friend... */
userdata = g_object_get_data (G_OBJECT (friend), "userdata");
friend_callback = userdata->callback;
userdata->callback = NULL;
gtk_entry_set_text (GTK_ENTRY (friend), buffer);
userdata->callback = friend_callback;
}
static void
height_width_callback (gint width,
GtkWidget **div_entry)
{
gint divs, max;
gpointer data;
max = *((gint*) g_object_get_data(G_OBJECT(*div_entry), "max"));
divs = max / width;
g_snprintf (buffer, BUFSIZE, "%d", divs );
data = g_object_get_data (G_OBJECT(*div_entry), "friend");
g_signal_handlers_block_by_func (*div_entry,
entscale_int_entry_update,
data);
gtk_entry_set_text (GTK_ENTRY (*div_entry), buffer);
g_signal_handlers_unblock_by_func (*div_entry,
entscale_int_entry_update,
data);
}
static void
maze_message (const gchar *message)
{
gtk_label_set_text (GTK_LABEL (msg_label), message);
}
/* ==================================================================== */
/* As found in pixelize.c,
* hacked to return a pointer to the entry widget. */
/*
Entry and Scale pair 1.03
TODO:
- Do the proper thing when the user changes value in entry,
so that callback should not be called when value is actually not changed.
- Update delay
*/
/*
* entscale: create new entscale with label. (int)
* 1 row and 2 cols of table are needed.
* Input:
* x, y: starting row and col in table
* caption: label string
* intvar: pointer to variable
* min, max: the boundary of scale
* constraint: (bool) true iff the value of *intvar should be constraint
* by min and max
* callback: called when the value is actually changed
* call_data: data for callback func
*/
static GtkWidget*
entscale_int_new (GtkWidget *table,
gint x,
gint y,
const gchar *caption,
gint *intvar,
gint min,
gint max,
gboolean constraint,
EntscaleIntCallback callback,
gpointer call_data)
{
EntscaleIntData *userdata;
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *entry;
GtkWidget *scale;
GtkAdjustment *adjustment;
gint constraint_val;
userdata = g_new (EntscaleIntData, 1);
label = gtk_label_new (caption);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
/*
If the first arg of gtk_adjustment_new() isn't between min and
max, it is automatically corrected by gtk later with
"value-changed" signal. I don't like this, since I want to leave
*intvar untouched when `constraint' is false.
The lines below might look oppositely, but this is OK.
*/
userdata->constraint = constraint;
if( constraint )
constraint_val = *intvar;
else
constraint_val = ( *intvar < min ? min : *intvar > max ? max : *intvar );
userdata->adjustment = adjustment =
GTK_ADJUSTMENT (gtk_adjustment_new (constraint_val, min, max, 1.0, 1.0, 0.0));
scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adjustment);
gtk_widget_set_size_request (scale, ENTSCALE_INT_SCALE_WIDTH, -1);
gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
userdata->entry = entry = gtk_entry_new ();
gtk_widget_set_size_request (entry, ENTSCALE_INT_ENTRY_WIDTH, -1);
g_snprintf (buffer, BUFSIZE, "%d", *intvar);
gtk_entry_set_text (GTK_ENTRY (entry), buffer);
userdata->callback = callback;
userdata->call_data = call_data;
/* userdata is done */
g_object_set_data (G_OBJECT (adjustment), "userdata", userdata);
g_object_set_data (G_OBJECT (entry), "userdata", userdata);
/* now ready for signals */
g_signal_connect (entry, "changed",
G_CALLBACK (entscale_int_entry_update),
intvar);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (entscale_int_scale_update),
intvar);
g_signal_connect_swapped (entry, "destroy",
G_CALLBACK (g_free),
userdata);
/* start packing */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, TRUE, 0);
gtk_table_attach (GTK_TABLE (table), label, x, x+1, y, y+1,
GTK_FILL, GTK_FILL, 0, 0);
gtk_table_attach (GTK_TABLE (table), hbox, x+1, x+2, y, y+1,
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (label);
gtk_widget_show (entry);
gtk_widget_show (scale);
gtk_widget_show (hbox);
return entry;
}
static void
entscale_int_scale_update (GtkAdjustment *adjustment,
gpointer data)
{
EntscaleIntData *userdata;
GtkEntry *entry;
gint *intvar = data;
gint new_val;
userdata = g_object_get_data (G_OBJECT (adjustment), "userdata");
new_val = (gint) gtk_adjustment_get_value (adjustment);
*intvar = new_val;
entry = GTK_ENTRY (userdata->entry);
g_snprintf (buffer, BUFSIZE, "%d", (int) new_val);
/* avoid infinite loop (scale, entry, scale, entry ...) */
g_signal_handlers_block_by_func (entry,
entscale_int_entry_update,
data);
gtk_entry_set_text (entry, buffer);
g_signal_handlers_unblock_by_func (entry,
entscale_int_entry_update,
data);
if (userdata->callback)
(*userdata->callback) (*intvar, userdata->call_data);
}
static void
entscale_int_entry_update (GtkWidget *widget,
gpointer data)
{
EntscaleIntData *userdata;
GtkAdjustment *adjustment;
gint new_val, constraint_val;
gint *intvar = data;
userdata = g_object_get_data (G_OBJECT (widget), "userdata");
adjustment = userdata->adjustment;
new_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
constraint_val = new_val;
if (constraint_val < gtk_adjustment_get_lower (adjustment))
constraint_val = gtk_adjustment_get_lower (adjustment);
if (constraint_val > gtk_adjustment_get_upper (adjustment))
constraint_val = gtk_adjustment_get_upper (adjustment);
if ( userdata->constraint )
*intvar = constraint_val;
else
*intvar = new_val;
g_signal_handlers_block_by_func (adjustment,
entscale_int_scale_update,
data);
gtk_adjustment_set_value (adjustment, constraint_val);
g_signal_handlers_unblock_by_func (adjustment,
entscale_int_scale_update,
data);
if (userdata->callback)
(*userdata->callback) (*intvar, userdata->call_data);
}

View File

@ -1,24 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef __MAZE_DIALOG_H__
#define __MAZE_DIALOG_H__
gboolean maze_dialog (void);
#endif /* __MAZE_DIALOG_H__ */

View File

@ -1,155 +0,0 @@
/* $Id$
* These routines are useful for working with GIMP and need not be
* specific to plug-in-maze.
*
* Kevin Turner <acapnotic@users.sourceforge.net>
* http://gimp-plug-ins.sourceforge.net/maze/
*/
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <string.h>
#include "libgimp/gimp.h"
#include "maze-utils.h"
/* get_colors Returns the current foreground and background colors in
nice little arrays. It works nicely for RGB and grayscale images,
however handling of indexed images is somewhat broken. Patches
appreciated. */
void
get_colors (GimpDrawable *drawable,
guint8 *fg,
guint8 *bg)
{
GimpRGB foreground;
GimpRGB background;
gimp_context_get_foreground (&foreground);
gimp_context_get_background (&background);
fg[0] = fg[1] = fg[2] = fg[3] = 255;
bg[0] = bg[1] = bg[2] = bg[3] = 255;
switch (gimp_drawable_type (drawable->drawable_id))
{
case GIMP_RGB_IMAGE:
case GIMP_RGBA_IMAGE:
gimp_rgb_get_uchar (&foreground, &fg[0], &fg[1], &fg[2]);
gimp_rgb_get_uchar (&background, &bg[0], &bg[1], &bg[2]);
break;
case GIMP_GRAYA_IMAGE:
case GIMP_GRAY_IMAGE:
fg[0] = gimp_rgb_luminance_uchar (&foreground);
bg[0] = gimp_rgb_luminance_uchar (&background);
break;
case GIMP_INDEXEDA_IMAGE:
case GIMP_INDEXED_IMAGE: /* FIXME: Should use current fg/bg colors. */
g_warning("maze: get_colors: Indexed image. Using colors 15 and 0.\n");
fg[0] = 15; /* As a plugin, I protest. *I* shouldn't be the */
bg[0] = 0; /* one who has to deal with this colormapcrap. */
break;
default:
break;
}
}
/* Draws a solid color box in a GimpPixelRgn. */
/* Optimization assumptions:
* (Or, "Why Maze is Faster Than Checkerboard.")
*
* Assuming calling memcpy is faster than using loops.
* Row buffers are nice...
*
* Assume allocating memory for row buffers takes a significant amount
* of time. Assume drawbox will be called many times.
* Only allocate memory once.
*
* Do not assume the row buffer will always be the same size. Allow
* for reallocating to make it bigger if needed. However, I don't see
* reason to bother ever shrinking it again.
* (Under further investigation, assuming the row buffer never grows
* may be a safe assumption in this case.)
*
* Also assume that the program calling drawbox is short-lived, so
* memory leaks aren't of particular concern-- the memory allocated to
* the row buffer is never set free.
*/
/* Further optimizations that could be made...
* Currently, the row buffer is re-filled with every call. However,
* plug-ins such as maze and checkerboard only use two colors, and
* for the most part, have rows of the same size with every call.
* We could keep a row of each color on hand so we wouldn't have to
* re-fill it every time... */
void
drawbox (GimpPixelRgn *dest_rgn,
guint x,
guint y,
guint w,
guint h,
guint8 clr[4])
{
const guint bpp = dest_rgn->bpp;
const guint x_min = x * bpp;
/* x_max = dest_rgn->bpp * MIN(dest_rgn->w, (x + w)); */
/* rowsize = x_max - x_min */
const guint rowsize = bpp * MIN (dest_rgn->w, (x + w)) - x_min;
/* The maximum [xy] value is that of the far end of the box, or
* the edge of the region, whichever comes first. */
const guint y_max = dest_rgn->rowstride * MIN (dest_rgn->h, (y + h));
static guint8 *rowbuf;
static guint high_size = 0;
guint xx, yy;
/* Does the row buffer need to be (re)allocated? */
if (high_size == 0)
{
rowbuf = g_new (guint8, rowsize);
}
else if (rowsize > high_size)
{
rowbuf = g_renew (guint8, rowbuf, rowsize);
}
high_size = MAX (high_size, rowsize);
/* Fill the row buffer with the color. */
for (xx = 0; xx < rowsize; xx += bpp)
{
memcpy (&rowbuf[xx], clr, bpp);
}
/* Fill in the box in the region with rows... */
for (yy = dest_rgn->rowstride * y; yy < y_max; yy += dest_rgn->rowstride)
{
memcpy (&dest_rgn->data[yy + x_min], rowbuf, rowsize);
}
}

View File

@ -1,31 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __MAZE_UTILS_H__
#define __MAZE_UTILS_H__
void get_colors (GimpDrawable *drawable,
guint8 *fg,
guint8 *bg);
void drawbox (GimpPixelRgn *dest_rgn,
guint x,
guint y,
guint w,
guint h,
guint8 clr[4]);
#endif /* __MAZE_UTILS_H__ */

View File

@ -1,751 +0,0 @@
/* $Id$
* This is a plug-in for GIMP.
* It draws mazes...
*
* Implemented as a GIMP 0.99 Plugin by
* Kevin Turner <acapnotic@users.sourceforge.net>
* http://gimp-plug-ins.sourceforge.net/maze/
*
* Code generously borrowed from assorted GIMP plugins
* and used as a template to get me started on this one. :)
*
* TO DO:
* maze_face.c: Rework the divboxes to be more like spinbuttons.
*
* Maybe add an option to kill the outer border.
*
* Fix that stray line down there between maze wall and dead space border...
*
* handy.c: Make get_colors() work with indexed. * HELP! *
*
*/
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include "libgimp/gimp.h"
#include "libgimp/gimpui.h"
#include "maze.h"
#include "maze-algorithms.h"
#include "maze-dialog.h"
#include "maze-utils.h"
#include "libgimp/stdplugins-intl.h"
static void query (void);
static void run (const gchar *name,
gint nparams,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals);
static void maze (GimpDrawable *drawable);
static void mask_maze (gint32 selection_ID,
guchar *maz,
guint mw,
guint mh,
gint x1,
gint x2,
gint y1,
gint y2,
gint deadx,
gint deady);
const GimpPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
MazeValues mvals =
{
5, /* Passage width */
5, /* Passage height */
0, /* seed */
FALSE, /* Tileable? */
57, /* multiple * These two had "Experiment with this?" comments */
1, /* offset * in the maz.c source, so, lets expiriment. :) */
DEPTH_FIRST, /* Algorithm */
TRUE, /* random_seed */
};
GRand *gr;
gint sel_w;
gint sel_h;
MAIN ()
static void
query (void)
{
static const GimpParamDef args[] =
{
{ GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
{ GIMP_PDB_IMAGE, "image", "(unused)" },
{ GIMP_PDB_DRAWABLE, "drawable", "ID of drawable" },
/* If we did have parameters, these be them: */
{ GIMP_PDB_INT16, "width", "Width of the passages" },
{ GIMP_PDB_INT16, "height", "Height of the passages"},
{ GIMP_PDB_INT8, "tileable", "Tileable maze?"},
{ GIMP_PDB_INT8, "algorithm", "Generation algorithm"
"(0=DEPTH FIRST, 1=PRIM'S ALGORITHM)" },
{ GIMP_PDB_INT32, "seed", "Random Seed"},
{ GIMP_PDB_INT16, "multiple", "Multiple (use 57)" },
{ GIMP_PDB_INT16, "offset", "Offset (use 1)" }
};
gimp_install_procedure (PLUG_IN_PROC,
N_("Draw a labyrinth"),
"Generates a maze using either the depth-first "
"search method or Prim's algorithm. Can make "
"tileable mazes too.",
"Kevin Turner <kevint@poboxes.com>",
"Kevin Turner",
"1997, 1998",
N_("_Maze..."),
"RGB*, GRAY*, INDEXED*",
GIMP_PLUGIN,
G_N_ELEMENTS (args), 0,
args, NULL);
gimp_plugin_menu_register (PLUG_IN_PROC,
"<Image>/Filters/Render/Pattern");
}
static void
run (const gchar *name,
gint nparams,
const GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
{
static GimpParam values[1];
GimpDrawable *drawable;
GimpRunMode run_mode;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
gint x, y;
#ifdef MAZE_DEBUG
g_print("maze PID: %d\n",getpid());
#endif
run_mode = param[0].data.d_int32;
*nreturn_vals = 1;
*return_vals = values;
INIT_I18N ();
gr = g_rand_new ();
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = status;
drawable = gimp_drawable_get (param[2].data.d_drawable);
/* get the selection width and height for the GUI. Return if the
* selection and drawable do not intersect.
*/
if (! gimp_drawable_mask_intersect (drawable->drawable_id,
&x, &y, &sel_w, &sel_h))
return;
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:
/* Possibly retrieve data */
gimp_get_data (PLUG_IN_PROC, &mvals);
/* Acquire info with a dialog */
if (! maze_dialog ())
{
gimp_drawable_detach (drawable);
return;
}
break;
case GIMP_RUN_NONINTERACTIVE:
if (nparams != 10)
status = GIMP_PDB_CALLING_ERROR;
if (status == GIMP_PDB_SUCCESS)
{
mvals.width = (gint16) param[3].data.d_int16;
mvals.height = (gint16) param[4].data.d_int16;
mvals.tile = (gint8) param[5].data.d_int8;
mvals.algorithm = (gint8) param[6].data.d_int8;
mvals.seed = (guint32) param[7].data.d_int32;
mvals.multiple = (gint16) param[8].data.d_int16;
mvals.offset = (gint16) param[9].data.d_int16;
if (mvals.random_seed)
mvals.seed = g_random_int ();
}
break;
case GIMP_RUN_WITH_LAST_VALS:
/* Possibly retrieve data */
gimp_get_data (PLUG_IN_PROC, &mvals);
if (mvals.random_seed)
mvals.seed = g_random_int ();
break;
default:
break;
}
/* color, gray, or indexed... hmm, miss anything? ;) */
if (gimp_drawable_is_rgb (drawable->drawable_id) ||
gimp_drawable_is_gray (drawable->drawable_id) ||
gimp_drawable_is_indexed (drawable->drawable_id))
{
maze (drawable);
if (run_mode != GIMP_RUN_NONINTERACTIVE)
gimp_displays_flush ();
if (run_mode == GIMP_RUN_INTERACTIVE ||
run_mode == GIMP_RUN_WITH_LAST_VALS)
{
gimp_set_data (PLUG_IN_PROC, &mvals, sizeof (MazeValues));
}
}
else
{
status = GIMP_PDB_EXECUTION_ERROR;
}
values[0].data.d_status = status;
gimp_drawable_detach (drawable);
g_rand_free (gr);
}
#ifdef MAZE_DEBUG
void
maze_dump (guchar *maz,
gint mw,
gint mh)
{
short xx, yy;
int foo = 0;
for (yy = 0; yy < mh; yy++)
{
for (xx = 0; xx < mw; xx++)
g_print ("%3d ", maz[foo++]);
g_print ("\n");
}
}
void
maze_dumpX (guchar *maz,
gint mw,
gint mh)
{
short xx, yy;
int foo = 0;
for (yy = 0; yy < mh; yy++)
{
for (xx = 0; xx < mw; xx++)
g_print ("%c", maz[foo++] ? 'X' : '.');
g_print ("\n");
}
}
#endif
static void
maze (GimpDrawable * drawable)
{
GimpPixelRgn dest_rgn;
guint mw, mh;
gint deadx, deady;
guint cur_progress, max_progress;
gdouble per_progress;
gint x1, y1, x2, y2, x, y;
gint dx, dy, xx, yy;
gint maz_x, maz_xx, maz_row, maz_yy;
guint8 fg[4], bg[4];
gpointer pr;
gboolean active_selection;
guchar *maz;
guint pos;
/* Gets the input area... */
active_selection = gimp_drawable_mask_bounds (drawable->drawable_id,
&x1, &y1, &x2, &y2);
/***************** Maze Stuff Happens Here ***************/
mw = (x2-x1) / mvals.width;
mh = (y2-y1) / mvals.height;
if (!mvals.tile)
{
mw -= !(mw & 1); /* mazegen doesn't work with even-sized mazes. */
mh -= !(mh & 1); /* Note I don't warn the user about this... */
}
else
{
/* On the other hand, tileable mazes must be even. */
mw -= (mw & 1);
mh -= (mh & 1);
}
/* It will really suck if your tileable maze ends up with this dead
space around it. Oh well, life is hard. */
deadx = ((x2-x1) - mw * mvals.width) / 2;
deady = ((y2-y1) - mh * mvals.height) / 2;
maz = g_new0 (guchar, mw * mh);
#ifdef MAZE_DEBUG
printf("x: %d\ty: %d\nmw: %d\tmh: %d\ndx: %d\tdy: %d\nwidth:%d\theight: %d\n",
(x2 - x1), (y2 - y1), mw, mh, deadx, deady, mvals.width, mvals.height);
#endif
/* Sanity check: */
switch (mvals.algorithm)
{
case DEPTH_FIRST:
case PRIMS_ALGORITHM:
break;
default:
g_warning ("maze: Invalid algorithm choice %d", mvals.algorithm);
}
if (mvals.tile)
{
switch (mvals.algorithm)
{
case DEPTH_FIRST:
mazegen_tileable (0, maz, mw, mh, mvals.seed);
break;
case PRIMS_ALGORITHM:
prim_tileable (maz, mw, mh);
break;
default:
break;
}
}
else
{
/* not tileable */
if (active_selection)
{
/* Mask and draw mazes until there's no
* more room left. */
mask_maze (drawable->drawable_id,
maz, mw, mh, x1, x2, y1, y2, deadx, deady);
for (maz_yy = mw; maz_yy < (mh * mw); maz_yy += 2 * mw)
{
for (maz_xx = 1; maz_xx < mw; maz_xx += 2)
{
if (maz[maz_yy + maz_xx] == 0)
{
switch (mvals.algorithm)
{
case DEPTH_FIRST:
mazegen (maz_yy+maz_xx, maz, mw, mh, mvals.seed);
break;
case PRIMS_ALGORITHM:
prim (maz_yy+maz_xx, maz, mw, mh);
break;
default:
break;
}
}
}
}
}
else
{
/* No active selection. */
pos = mw + 1;
switch (mvals.algorithm)
{
case DEPTH_FIRST:
mazegen (pos, maz, mw, mh, mvals.seed);
break;
case PRIMS_ALGORITHM:
prim (pos, maz, mw, mh);
break;
default:
break;
}
}
}
/************** Begin Drawing *********************/
/* Initialize pixel region (?) */
gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, (x2 - x1), (y2 - y1),
TRUE, TRUE);
cur_progress = 0;
per_progress = 0.0;
max_progress = (x2 - x1) * (y2 - y1) / 100;
/* Get the foreground and background colors */
get_colors (drawable, fg, bg);
gimp_progress_init (_("Drawing maze"));
for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
pr != NULL;
pr = gimp_pixel_rgns_process (pr))
{
x = dest_rgn.x - x1 - deadx;
y = dest_rgn.y - y1 - deady;
/* First boxes by edge of tile must be handled specially
because they may have started on a previous tile,
unbeknownst to us. */
dx = mvals.width - (x % mvals.width);
dy = mvals.height - (y % mvals.height);
maz_x = x/mvals.width;
maz_row = mw * (y/mvals.height);
/* Draws the upper-left [split] box */
drawbox (&dest_rgn, 0, 0, dx, dy,
(maz[maz_row + maz_x] == IN) ? fg : bg);
maz_xx=maz_x + 1;
/* Draw the top row [split] boxes */
for (xx=dx; xx < dest_rgn.w; xx+=mvals.width)
{
drawbox (&dest_rgn, xx, 0, mvals.width, dy,
(maz[maz_row + maz_xx++] == IN) ? fg : bg);
}
maz_yy = maz_row + mw;
/* Left column */
for (yy = dy; yy < dest_rgn.h; yy += mvals.height)
{
drawbox (&dest_rgn, 0, yy, dx, mvals.height,
(maz[maz_yy + maz_x] == IN) ? fg : bg);
maz_yy += mw;
}
maz_x++;
/* Everything else */
for (yy = dy; yy < dest_rgn.h; yy += mvals.height)
{
maz_xx = maz_x; maz_row+=mw;
for (xx = dx; xx < dest_rgn.w; xx += mvals.width)
{
drawbox (&dest_rgn, xx, yy, mvals.width, mvals.height,
(maz[maz_row + maz_xx++] == IN) ? fg : bg);
}
}
cur_progress += dest_rgn.w * dest_rgn.h;
if (cur_progress > max_progress)
{
cur_progress = cur_progress - max_progress;
per_progress = per_progress + 0.01;
gimp_progress_update (per_progress);
}
}
gimp_progress_update (1.0);
gimp_drawable_flush (drawable);
gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
}
/* Shaped mazes: */
/* With
* Depth first: Nonzero cells will not be connected to or visited.
* Prim's Algorithm:
* Cells that are not IN will not be connected to.
* Cells that are not OUT will not be converted to FRONTIER.
*
* So we'll put unavailable cells in a non-zero non-in non-out class
* called MASKED.
*/
/* But first... A little discussion about cells. */
/* In the eyes of the generation algorithms, the world is made up of
* two sorts of things: Cells, and the walls between them. Walls can
* be knocked out, and then you have a passage between cells. The
* drawing routine has a simpler view of life... Everything is a
* pixel. Or a block of pixels. It makes no distinction between
* passages, walls, and cells.
*
* We may also make the distinction between two different types of
* passages: horizontal and vertical. With that in mind, a
* part of the world looks something like this:
*
* @-@-@-@- Where @ is a cell, | is a vertical passage, and - is a
* | | | | horizontal passsage.
* @-@-@-@-
* | | | | Remember, the maze generation routines will not rest
* until the maze is full, that is, every cell is connected
* to another. Already, we can determine a few things about the final
* maze. We know which blocks will be cells, which blocks may become
* passages (and we know what sort), and we also notice that there are
* some blocks that will never be either cells or passages.
*
* Now, back to our masking routine... To save a little time, lets
* just take sample points from the block. We'll sample a point from
* the top and the bottom of vertical passages, left/right for
* horizontal, and, hmm, left/right/top/bottom for cells. And of
* course, we needn't concern ourselves with the others. We could
* also sample the midpoint of each...
* Then what we'll do is see if the average is higher than some magic
* threshold number, and if so, we let maze happen there. Otherwise
* we mask it out.
*/
/* And, uh, that's on the TODO list. Looks like I spent so much time
* writing comments I haven't left enough to implement the code. :)
* Right now we only sample one point. */
static void
mask_maze (gint32 drawable_ID,
guchar *maz,
guint mw,
guint mh,
gint x1,
gint x2,
gint y1,
gint y2,
gint deadx,
gint deady)
{
gint32 selection_ID;
GimpPixelRgn sel_rgn;
gint xx0 = 0;
gint yy0 = 0;
gint xoff;
gint yoff;
guint xx = 0;
guint yy = 0;
guint foo = 0;
gint cur_row, cur_col;
gint x1half, x2half, y1half, y2half;
guchar *linebuf;
selection_ID =
gimp_image_get_selection (gimp_item_get_image (drawable_ID));
if (selection_ID == -1)
return;
gimp_pixel_rgn_init (&sel_rgn, gimp_drawable_get (selection_ID),
x1, y1, (x2-x1), (y2-y1),
FALSE, FALSE);
gimp_drawable_offsets (drawable_ID, &xoff, &yoff);
/* Special cases: If mw or mh < 3 */
/* FIXME (Currently works, but inefficiently.) */
/* mw && mh => 3 */
linebuf = g_new (guchar, sel_rgn.w * sel_rgn.bpp);
xx0 = x1 + deadx + mvals.width + xoff;
yy0 = y1 + deady + mvals.height + yoff;
x1half = mvals.width / 2;
x2half = mvals.width - 1;
y1half = mvals.height / 2;
y2half = mvals.height - 1;
/* Here, yy is with respect to the drawable (or something),
whereas xx is with respect to the row buffer. */
yy = yy0 + y1half;
for (cur_row=1; cur_row < mh; cur_row += 2)
{
gimp_pixel_rgn_get_row (&sel_rgn, linebuf, x1+xoff, yy, (x2 - x1));
cur_col = 1; xx = mvals.width;
while (cur_col < mw)
{
/* Cell: */
maz[cur_row * mw + cur_col] =
(linebuf[xx] + linebuf[xx + x1half] + linebuf[xx+x2half]) / 5;
cur_col += 1;
xx += mvals.width;
/* Passage: */
if (cur_col < mw)
maz[cur_row * mw + cur_col] =
(linebuf[xx] + linebuf[xx + x1half] + linebuf[xx+x2half]) / 3;
cur_col += 1;
xx += mvals.width;
} /* next col */
yy += 2 * mvals.height;
} /* next cur_row += 2 */
/* Done doing horizontal scans, change this from a row buffer to
a column buffer. */
g_free (linebuf);
linebuf = g_new (guchar, sel_rgn.h * sel_rgn.bpp);
/* Now xx is with respect to the drawable (or whatever),
and yy is with respect to the row buffer. */
xx=xx0 + x1half;
for (cur_col = 1; cur_col < mw; cur_col += 2)
{
gimp_pixel_rgn_get_col (&sel_rgn, linebuf, xx, y1, (y2-y1));
cur_row = 1; yy = mvals.height;
while (cur_row < mh)
{
/* Cell: */
maz[cur_row * mw + cur_col] +=
(linebuf[yy] + linebuf[yy+y2half]) / 5;
cur_row += 1;
yy += mvals.height;
/* Passage: */
if (cur_row < mh)
maz[cur_row * mw + cur_col] =
(linebuf[yy] + linebuf[yy + y1half] + linebuf[yy+y2half]) / 3;
cur_row += 1;
yy += mvals.height;
} /* next cur_row */
xx += 2 * mvals.width;
} /* next cur_col */
g_free (linebuf);
/* Do the alpha -> masked conversion. */
for (yy = 0; yy < mh; yy++)
{
for (xx = 0; xx < mw; xx++)
{
maz[foo] = ( maz[foo] < MAZE_ALPHA_THRESHOLD ) ? MASKED : OUT;
foo++;
}
}
}
/* The attempt to implement this with tiles: (it wasn't fun) */
#if 0
{
/* Tiles make my life decidedly difficult here. There are too
* many special cases... "What if a tile starts less/more than
* halfway through a block? What if we get a narrow edge-tile
* that..." etc, etc. I shall investigate other options.
* Possibly a row buffer, or can we use something other than this
* black-magic gimp_pixel_rgns_register call to get tiles of
* different sizes? Now that'd be nice... */
for (pr = gimp_pixel_rgns_register (1, &sel_rgn);
pr != NULL;
pr = gimp_pixel_rgns_process (pr))
{
/* This gives us coordinates relative to the starting point
* of the maze grid. Negative values happen here if there
* is dead space. */
x = sel_rgn.x - x1 - deadx;
y = sel_rgn.y - y1 - deady;
/* These coordinates are relative to the current tile. */
/* This starts us off at the first block boundary in the
* tile. */
/* e.g. with x=16 and width=10.
* 16 % 10 = 6
* 10 - 6 = 4
x: 6789!123456789!123456789!12
....|.........|.........|..
xx: 0123456789!123456789!123456
So to start on the boundary, begin at 4.
For the case x=0, 10-0=10. So xx0 will always between 1 and width. */
xx0 = mvals.width - (x % mvals.width);
yy0 = mvals.height - (y % mvals.height);
/* Find the corresponding row and column in the maze. */
maz_x = (x+xx0)/mvals.width;
maz_row = mw * ((y + yy0)/mvals.height);
for (yy = yy0 * sel_rgn.rowstride;
yy < sel_rgn.h * sel_rgn.rowstride;
yy += (mvals.height * sel_rgn.rowstride))
{
maz_xx = maz_x;
for (xx = xx0 * sel_rgn.bpp;
xx < sel_rgn.w;
xx += mvals.width * sel_rgn.bpp)
{
if (sel_rgn.data[yy+xx] < MAZE_ALPHA_THRESHOLD)
maz[maz_row+maz_xx]=MASKED;
maz_xx++;
}
maz_row += mw;
}
}
#ifdef MAZE_DEBUG
/* maze_dump(maz,mw,mh); */
#endif
}
#endif /* 0 */

View File

@ -1,68 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef __MAZE_H__
#define __MAZE_H__
/* The "divbox" really should look and act more like a spinbutton.
This flag is a small step in the direction toward the former, the
latter leaves much to be desired. */
#define DIVBOX_LOOKS_LIKE_SPINBUTTON FALSE
/* Don't update the progress for every cell when creating a maze.
Instead, update every . . . */
#define PRIMS_PROGRESS_UPDATE 256
/* Don't draw in anything that has less than
this value in the selection channel. */
#define MAZE_ALPHA_THRESHOLD 127
#define PLUG_IN_PROC "plug-in-maze"
#define PLUG_IN_BINARY "maze"
#define PLUG_IN_ROLE "gimp-maze"
typedef enum {
DEPTH_FIRST,
PRIMS_ALGORITHM
} MazeAlgoType;
typedef struct {
gint width;
gint height;
guint32 seed;
gboolean tile;
gint multiple;
gint offset;
MazeAlgoType algorithm;
gboolean random_seed;
/* Interface options. */
} MazeValues;
enum CellTypes {
OUT,
IN,
FRONTIER,
MASKED
};
extern MazeValues mvals;
extern gint sel_w;
extern gint sel_h;
extern GRand *gr;
#endif /* __MAZE_H__ */

View File

@ -211,10 +211,6 @@ plug-ins/lighting/lighting-ui.c
plug-ins/map-object/map-object-apply.c
plug-ins/map-object/map-object-main.c
plug-ins/map-object/map-object-ui.c
plug-ins/maze/maze-algorithms.c
plug-ins/maze/maze-dialog.c
plug-ins/maze/maze.c
plug-ins/maze/maze.h
plug-ins/pagecurl/pagecurl.c
plug-ins/print/print-draw-page.c
plug-ins/print/print-page-layout.c

View File

@ -2338,6 +2338,84 @@ CODE
);
}
sub plug_in_maze {
$blurb = 'Draw a labyrinth';
$help = <<'HELP';
Generates a maze using either the depth-first search method or Prim's
algorithm. Can make tileable mazes too.
HELP
&std_pdb_compat('gegl:maze');
$date = '2015';
@inargs = (
{ name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
desc => 'The run mode' },
{ name => 'image', type => 'image', dead => 1,
desc => 'Input image (unused)' },
{ name => 'drawable', type => 'drawable',
desc => 'Input drawable' },
{ name => 'width', type => '1 <= int16 <= 1024',
desc => 'Width of the passages' },
{ name => 'height', type => '1 <= int16 <= 1024',
desc => 'Height of the passages' },
{ name => 'tileable', type => '0 <= int8 <= 1',
desc => 'Tileable maze? (TRUE or FALSE)' },
{ name => 'algorithm', type => '0 <= int8 <= 1',
desc => 'Generation algorithm (0 = DEPTH FIRST, 1 = PRIM\'S ALGORITHM)' },
{ name => 'seed', type => 'int32',
desc => 'Random Seed' },
{ name => 'multiple', type => 'int16', dead => 1,
desc => 'Multiple (use 57)' },
{ name => 'offset', type => 'int16', dead => 1,
desc => 'Offset (use 1)' }
);
%invoke = (
code => <<'CODE'
{
if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
GIMP_PDB_ITEM_CONTENT, error) &&
gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
{
GeglNode *node;
GeglColor *fg_color;
GeglColor *bg_color;
GimpRGB color;
gimp_context_get_foreground (context, &color);
fg_color = gimp_gegl_color_new (&color);
gimp_context_get_background (context, &color);
bg_color = gimp_gegl_color_new (&color);
node = gegl_node_new_child (NULL,
"operation", "gegl:maze",
"x", width,
"y", height,
"algorithm-type", algorithm,
"tilable", tileable,
"seed", seed,
"fg-color", fg_color,
"bg-color", bg_color,
NULL);
g_object_unref (fg_color);
g_object_unref (bg_color);
gimp_drawable_apply_operation (drawable, progress,
C_("undo-type", "Maze"),
node);
g_object_unref (node);
}
else
success = FALSE;
}
CODE
);
}
sub plug_in_mblur {
$blurb = 'Simulate movement using directional blur';
@ -4779,6 +4857,7 @@ CODE
plug_in_laplace
plug_in_lens_distortion
plug_in_make_seamless
plug_in_maze
plug_in_mblur
plug_in_mblur_inward
plug_in_mosaic