mirror of https://github.com/GNOME/gimp.git
plug-ins, pdb: remove the maze plug-in and add a PDB compat proc
This commit is contained in:
parent
3f02b2aaf2
commit
a4a0ec9598
|
@ -28,7 +28,7 @@
|
|||
#include "internal-procs.h"
|
||||
|
||||
|
||||
/* 772 procedures registered total */
|
||||
/* 773 procedures registered total */
|
||||
|
||||
void
|
||||
internal_procs_init (GimpPDB *pdb)
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -52,7 +52,6 @@ SUBDIRS = \
|
|||
imagemap \
|
||||
lighting \
|
||||
map-object \
|
||||
maze \
|
||||
pagecurl \
|
||||
$(print) \
|
||||
selection-to-path \
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
/Makefile.in
|
||||
/Makefile
|
||||
/.deps
|
||||
/_libs
|
||||
/.libs
|
||||
/maze
|
||||
/maze.exe
|
|
@ -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)
|
|
@ -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);
|
||||
}
|
|
@ -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__ */
|
|
@ -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);
|
||||
}
|
|
@ -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__ */
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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__ */
|
|
@ -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 */
|
|
@ -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__ */
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue