mirror of https://github.com/GNOME/gimp.git
updated maze plugin
* plug-ins/maze: updated maze plugin * plug-ins/script-fu/scripts/circuit.scm: reflected maze pdb interface change -Yosh
This commit is contained in:
parent
5b1f6495d8
commit
a7b206b5c3
|
@ -1,3 +1,10 @@
|
|||
Sun May 17 17:31:05 PDT 1998 Manish Singh <yosh@gimp.org>
|
||||
|
||||
* plug-ins/maze: updated maze plugin
|
||||
|
||||
* plug-ins/script-fu/scripts/circuit.scm: reflected maze pdb
|
||||
interface change
|
||||
|
||||
Sun May 17 14:44:32 PDT 1998 Manish Singh <yosh@gimp.org>
|
||||
|
||||
* app/gimage.c: fix for merging indexed and indexed-alpha
|
||||
|
|
|
@ -5,7 +5,7 @@ pluginlibdir = $(gimpplugindir)/plug-ins
|
|||
pluginlib_PROGRAMS = maze
|
||||
|
||||
maze_SOURCES = \
|
||||
maze.c maze.h maze_face.c
|
||||
maze.c maze.h maze_face.c algorithms.c handy.c
|
||||
|
||||
INCLUDES = \
|
||||
$(X_CFLAGS) \
|
||||
|
|
|
@ -0,0 +1,586 @@
|
|||
/* algorithms.c
|
||||
* Contains routines for generating mazes, somewhat intertwined with
|
||||
* Gimp plug-in-maze specific stuff.
|
||||
*
|
||||
* Kevin Turner <kevint@poboxes.com>
|
||||
* http://www.poboxes.com/kevint/gimp/maze.html
|
||||
*/
|
||||
|
||||
/* 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 embarass 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "maze.h"
|
||||
#include "libgimp/gimp.h"
|
||||
#include "libgimp/gimpui.h"
|
||||
|
||||
extern MazeValues mvals;
|
||||
|
||||
void mazegen(gint pos,
|
||||
gchar *maz,
|
||||
gint x,
|
||||
gint y,
|
||||
gint rnd);
|
||||
void mazegen_tileable(gint pos,
|
||||
gchar *maz,
|
||||
gint x,
|
||||
gint y,
|
||||
gint rnd);
|
||||
void prim(guint pos,
|
||||
gchar *maz,
|
||||
guint x,
|
||||
guint y,
|
||||
gint rnd);
|
||||
void prim_tileable(gchar *maz,
|
||||
guint x,
|
||||
guint y,
|
||||
gint rnd);
|
||||
|
||||
#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, gchar *maz, gint x, gint y, gint rnd)
|
||||
{
|
||||
gchar d, i;
|
||||
gint c=0, 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) { /* This is simple enough. */
|
||||
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);
|
||||
|
||||
} /* End while(d=...) Loop */
|
||||
|
||||
/* This routine is quite quick, even for rather large mazes.
|
||||
For that reason, it doesn't need a progress bar. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Tileable mazes are my creation, based on the routine above. */
|
||||
void
|
||||
mazegen_tileable(gint pos, gchar *maz, gint x, gint y, gint rnd)
|
||||
{
|
||||
gchar d, i;
|
||||
gint c=0, 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) { /* This is simple enough. */
|
||||
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);
|
||||
|
||||
} /* End while(d=...) Loop */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
print_glist(gpointer data, gpointer user_data)
|
||||
{
|
||||
g_print("%d ",(guint)data);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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(guint pos, gchar *maz, guint x, guint y, gint rnd)
|
||||
{
|
||||
GSList *front_cells=NULL;
|
||||
guint current;
|
||||
gint up, down, left, right; /* Not unsigned, because macros return -1. */
|
||||
guint progress=0, max_progress;
|
||||
char d, i;
|
||||
guint c=0;
|
||||
|
||||
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,(gpointer)up);
|
||||
}
|
||||
if (down >= 0) {
|
||||
maz[up]=FRONTIER;
|
||||
front_cells=g_slist_append(front_cells,(gpointer)down);
|
||||
}
|
||||
if (left >= 0) {
|
||||
maz[up]=FRONTIER;
|
||||
front_cells=g_slist_append(front_cells,(gpointer)left);
|
||||
}
|
||||
if (right >= 0) {
|
||||
maz[up]=FRONTIER;
|
||||
front_cells=g_slist_append(front_cells,(gpointer)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 = rand() % g_slist_length(front_cells);
|
||||
pos = (guint)g_slist_nth(front_cells,current)->data;
|
||||
|
||||
front_cells=g_slist_remove(front_cells,(gpointer)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_append(front_cells,(gpointer)up);
|
||||
break;
|
||||
case IN:
|
||||
d=1;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
if (down>=0) {
|
||||
switch (maz[down]) {
|
||||
case OUT:
|
||||
maz[down]=FRONTIER;
|
||||
front_cells=g_slist_append(front_cells,(gpointer)down);
|
||||
break;
|
||||
case IN:
|
||||
d=d|2;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
if (left>=0) {
|
||||
switch (maz[left]) {
|
||||
case OUT:
|
||||
maz[left]=FRONTIER;
|
||||
front_cells=g_slist_append(front_cells,(gpointer)left);
|
||||
break;
|
||||
case IN:
|
||||
d=d|4;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
if (right>=0) {
|
||||
switch (maz[right]) {
|
||||
case OUT:
|
||||
maz[right]=FRONTIER;
|
||||
front_cells=g_slist_append(front_cells,(gpointer)right);
|
||||
break;
|
||||
case IN:
|
||||
d=d|8;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)]=1;
|
||||
break;
|
||||
case 1:
|
||||
maz[WALL_DOWN(pos)]=1;
|
||||
break;
|
||||
case 2:
|
||||
maz[WALL_LEFT(pos)]=1;
|
||||
break;
|
||||
case 3:
|
||||
maz[WALL_RIGHT(pos)]=1;
|
||||
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);
|
||||
|
||||
} /* while front_cells */
|
||||
|
||||
g_slist_free(front_cells);
|
||||
} /* prim */
|
||||
|
||||
void
|
||||
prim_tileable(gchar *maz, guint x, guint y, gint rnd)
|
||||
{
|
||||
GSList *front_cells=NULL;
|
||||
guint current, pos;
|
||||
guint up, down, left, right;
|
||||
guint progress=0, max_progress;
|
||||
char d, i;
|
||||
guint c=0;
|
||||
|
||||
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. */
|
||||
srand(rnd);
|
||||
pos = x * 2 * (rand() % y / 2) + 2 * (rand() % 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,(gpointer)up);
|
||||
front_cells=g_slist_append(front_cells,(gpointer)down);
|
||||
front_cells=g_slist_append(front_cells,(gpointer)left);
|
||||
front_cells=g_slist_append(front_cells,(gpointer)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 = rand() % g_slist_length(front_cells);
|
||||
pos = (guint)g_slist_nth(front_cells,current)->data;
|
||||
|
||||
front_cells=g_slist_remove(front_cells,(gpointer)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,(gpointer)up);
|
||||
break;
|
||||
case IN:
|
||||
d=1;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
switch (maz[down]) {
|
||||
case OUT:
|
||||
maz[down]=FRONTIER;
|
||||
front_cells=g_slist_append(front_cells,(gpointer)down);
|
||||
break;
|
||||
case IN:
|
||||
d=d|2;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
switch (maz[left]) {
|
||||
case OUT:
|
||||
maz[left]=FRONTIER;
|
||||
front_cells=g_slist_append(front_cells,(gpointer)left);
|
||||
break;
|
||||
case IN:
|
||||
d=d|4;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
switch (maz[right]) {
|
||||
case OUT:
|
||||
maz[right]=FRONTIER;
|
||||
front_cells=g_slist_append(front_cells,(gpointer)right);
|
||||
break;
|
||||
case IN:
|
||||
d=d|8;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
/* 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)]=1;
|
||||
break;
|
||||
case 1:
|
||||
maz[WALL_DOWN_TILEABLE(pos)]=1;
|
||||
break;
|
||||
case 2:
|
||||
maz[WALL_LEFT_TILEABLE(pos)]=1;
|
||||
break;
|
||||
case 3:
|
||||
maz[WALL_RIGHT_TILEABLE(pos)]=1;
|
||||
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);
|
||||
|
||||
} /* while front_cells */
|
||||
|
||||
g_slist_free(front_cells);
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
/* handy.c
|
||||
* These routines are useful for working with the GIMP and need not be
|
||||
* specific to plug-in-maze.
|
||||
*
|
||||
* Kevin Turner <kevint@poboxes.com>
|
||||
* http://www.poboxes.com/kevint/gimp/maze.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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "libgimp/gimp.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 (GDrawable * drawable,
|
||||
guint8 *fg,
|
||||
guint8 *bg);
|
||||
|
||||
/* drawbox draws a solid colored box in a GPixelRgn, hopefully fairly
|
||||
quickly. See comments below. */
|
||||
void drawbox (GPixelRgn *dest_rgn,
|
||||
guint x,
|
||||
guint y,
|
||||
guint w,
|
||||
guint h,
|
||||
guint8 clr[4]);
|
||||
|
||||
|
||||
void
|
||||
get_colors (GDrawable *drawable, guint8 *fg, guint8 *bg)
|
||||
{
|
||||
GParam *return_vals;
|
||||
gint nreturn_vals;
|
||||
|
||||
switch ( gimp_drawable_type (drawable->id) )
|
||||
{
|
||||
case RGBA_IMAGE: /* ASSUMPTION: Assuming the user wants entire */
|
||||
fg[3] = 255; /* area to be fully opaque. */
|
||||
bg[3] = 255;
|
||||
case RGB_IMAGE:
|
||||
|
||||
return_vals = gimp_run_procedure ("gimp_palette_get_foreground",
|
||||
&nreturn_vals,
|
||||
PARAM_END);
|
||||
|
||||
if (return_vals[0].data.d_status == STATUS_SUCCESS)
|
||||
{
|
||||
fg[0] = return_vals[1].data.d_color.red;
|
||||
fg[1] = return_vals[1].data.d_color.green;
|
||||
fg[2] = return_vals[1].data.d_color.blue;
|
||||
}
|
||||
else
|
||||
{
|
||||
fg[0] = 255;
|
||||
fg[1] = 255;
|
||||
fg[2] = 255;
|
||||
}
|
||||
return_vals = gimp_run_procedure ("gimp_palette_get_background",
|
||||
&nreturn_vals,
|
||||
PARAM_END);
|
||||
|
||||
if (return_vals[0].data.d_status == STATUS_SUCCESS)
|
||||
{
|
||||
bg[0] = return_vals[1].data.d_color.red;
|
||||
bg[1] = return_vals[1].data.d_color.green;
|
||||
bg[2] = return_vals[1].data.d_color.blue;
|
||||
}
|
||||
else
|
||||
{
|
||||
bg[0] = 0;
|
||||
bg[1] = 0;
|
||||
bg[2] = 0;
|
||||
}
|
||||
break;
|
||||
case GRAYA_IMAGE: /* and again */
|
||||
fg[1] = 255;
|
||||
bg[1] = 255;
|
||||
case GRAY_IMAGE:
|
||||
|
||||
return_vals = gimp_run_procedure ("gimp_palette_get_foreground",
|
||||
&nreturn_vals,
|
||||
PARAM_END);
|
||||
|
||||
if (return_vals[0].data.d_status == STATUS_SUCCESS)
|
||||
{
|
||||
fg[0] = 0.30 * return_vals[1].data.d_color.red +
|
||||
0.59 * return_vals[1].data.d_color.green +
|
||||
0.11 * return_vals[1].data.d_color.blue;
|
||||
}
|
||||
else
|
||||
{
|
||||
fg[0] = 255;
|
||||
}
|
||||
|
||||
return_vals = gimp_run_procedure ("gimp_palette_get_background",
|
||||
&nreturn_vals,
|
||||
PARAM_END);
|
||||
|
||||
if (return_vals[0].data.d_status == STATUS_SUCCESS)
|
||||
{
|
||||
bg[0] = 0.30 * return_vals[1].data.d_color.red +
|
||||
0.59 * return_vals[1].data.d_color.green +
|
||||
0.11 * return_vals[1].data.d_color.blue;
|
||||
}
|
||||
else
|
||||
{
|
||||
bg[0] = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case INDEXEDA_IMAGE:
|
||||
case 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 GPixelRgn. */
|
||||
/* 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( GPixelRgn *dest_rgn,
|
||||
guint x, guint y, guint w, guint h,
|
||||
guint8 clr[4])
|
||||
{
|
||||
guint xx, yy, y_max, x_min /*, x_max */;
|
||||
static guint8 *rowbuf;
|
||||
guint rowsize;
|
||||
static guint high_size=0;
|
||||
|
||||
/* The maximum [xy] value is that of the far end of the box, or
|
||||
* the edge of the region, whichever comes first. */
|
||||
|
||||
y_max = dest_rgn->rowstride * MIN(dest_rgn->h, (y + h));
|
||||
/* x_max = dest_rgn->bpp * MIN(dest_rgn->w, (x + w)); */
|
||||
|
||||
x_min = x * dest_rgn->bpp;
|
||||
|
||||
/* rowsize = x_max - x_min */
|
||||
rowsize = dest_rgn->bpp * MIN(dest_rgn->w, (x + w)) - x_min;
|
||||
|
||||
/* Does the row buffer need to be (re)allocated? */
|
||||
if (high_size == 0) {
|
||||
rowbuf = g_new(guint8, rowsize);
|
||||
} else if (rowsize > high_size) {
|
||||
g_realloc(rowbuf, rowsize * sizeof(guint8) );
|
||||
}
|
||||
|
||||
high_size = MAX(high_size, rowsize);
|
||||
|
||||
/* Fill the row buffer with the color. */
|
||||
for (xx= 0;
|
||||
xx < rowsize;
|
||||
xx+= dest_rgn->bpp) {
|
||||
memcpy (&rowbuf[xx], clr, dest_rgn->bpp);
|
||||
} /* next xx */
|
||||
|
||||
/* 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);
|
||||
} /* next yy */
|
||||
}
|
||||
|
||||
/* Alternate ways of doing things if you don't like memcpy. */
|
||||
#if 0
|
||||
for (xx= x * dest_rgn->bpp;
|
||||
xx < bar;
|
||||
xx+= dest_rgn->bpp) {
|
||||
#if 0
|
||||
for (bp=0; bp < dest_rgn->bpp; bp++) {
|
||||
dest_rgn->data[yy+xx+bp]=clr[bp];
|
||||
} /* next bp */
|
||||
#else
|
||||
memcpy (&dest_rgn->data[yy+xx], clr, dest_rgn->bpp);
|
||||
#endif
|
||||
} /* next xx */
|
||||
} /* next yy */
|
||||
}
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
/* maze.c, version 0.6.2, March 7, 1998.
|
||||
/* maze.c
|
||||
* This is a plug-in for the GIMP.
|
||||
* It draws mazes...
|
||||
*
|
||||
|
@ -6,66 +6,17 @@
|
|||
* Kevin Turner <kevint@poboxes.com>
|
||||
* http://www.poboxes.com/kevint/gimp/maze.html
|
||||
*
|
||||
* 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 embarass you in front of your friends...
|
||||
*
|
||||
* Code generously borrowed from assorted GIMP plugins
|
||||
* and used as a template to get me started on this one. :)
|
||||
*
|
||||
* Revision history:
|
||||
* 0.6.2 - drawbox rewritten with memcpy and a row buffer. By the way, Maze is now
|
||||
* faster than Checkerboard.
|
||||
* - Added Help button using extension_web_browser.
|
||||
* - Added DIVBOX_LOOKS_LIKE_SPINBUTTON flag. Doesn't look too hot, set to
|
||||
* FALSE by default.
|
||||
* 0.6.1 - Made use-time-for-random-seed a toggle button that remembers
|
||||
* its state, and moved seed to the second notebook page.
|
||||
* 0.6.0 - Width and height are now seperate options.
|
||||
* ^^ Note this changed the PDB interface. ^^
|
||||
*
|
||||
* - Added interface for selecting sizes by "divisions".
|
||||
* - Added "Time" button for random seed.
|
||||
* - Turned out that GParam shouldn't have been "fixed".
|
||||
* 0.5.0 - Added the long-awaited "tileable" option.
|
||||
* Required a change to PDB parameters.
|
||||
* - fixed some stuff with GParam values in run();
|
||||
* 0.4.2 - Applied Adrian Likins' patch to fix non-interactive stuff.
|
||||
* - -ansi and -pedantic-errors clean. Woo-hoo?
|
||||
* 0.4.1 - get_colors() now works properly for grayscale images.
|
||||
* I'd still like it to do indexed too, but I don't know
|
||||
* if that's worth breaking a sweat over.
|
||||
* - We're -Wall clean now. Woohoo!
|
||||
* 0.4.0 - Code for the painting of the maze has been almost completely rebuilt.
|
||||
* Hopefully it's a more sane and speedier approach.
|
||||
* Utilizes a new function, drawbox, which colors a solid rectangle.
|
||||
* (Good excercise, in any case.)
|
||||
* - Order of paramaters changed, defaults are used if not given.
|
||||
* - Discovery made that that was an utterly useless thing to do.
|
||||
* 0.3.0 - Maze is centered with dead space around outside
|
||||
* - Width slider works... And does stuff!
|
||||
* - Allows partial mazes to be generated with "broken" multiple
|
||||
* and offset values.
|
||||
* 0.1.99 - Has dialog box with seed, multiple, and offset.
|
||||
* 0.1.0 - First release. It works! :)
|
||||
*
|
||||
* TO DO:
|
||||
* Rework the divboxes to be more like spinbuttons.
|
||||
* maze_face.c: Rework the divboxes to be more like spinbuttons.
|
||||
*
|
||||
* Add an option to kill the outer border.
|
||||
* Maybe add an option to kill the outer border.
|
||||
*
|
||||
* Fix that stray line down there between maze wall and dead space border...
|
||||
*
|
||||
* Make get_colors() work with indexed. * HELP! *
|
||||
*
|
||||
* Also someday:
|
||||
* Maybe make it work with irregularly shaped selections?
|
||||
* Add different generation algorythms.
|
||||
* handy.c: Make get_colors() work with indexed. * HELP! *
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -82,13 +33,14 @@
|
|||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef MAZE_DEBUG
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <time.h> /* For random seeding */
|
||||
|
@ -103,21 +55,37 @@ static void run (gchar *name,
|
|||
gint *nreturn_vals,
|
||||
GParam **return_vals);
|
||||
static void maze (GDrawable * drawable);
|
||||
static gint mazegen(gint pos,
|
||||
gchar *maz,
|
||||
|
||||
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);
|
||||
|
||||
/* In algorithms.c */
|
||||
extern void mazegen(gint pos,
|
||||
guchar *maz,
|
||||
gint x,
|
||||
gint y,
|
||||
gint rnd);
|
||||
static gint mazegen_tileable(gint pos,
|
||||
gchar *maz,
|
||||
extern void mazegen_tileable(gint pos,
|
||||
guchar *maz,
|
||||
gint x,
|
||||
gint y,
|
||||
gint rnd);
|
||||
static void get_colors (GDrawable * drawable,
|
||||
extern void prim(guint pos,
|
||||
guchar *maz,
|
||||
guint x,
|
||||
guint y,
|
||||
gint rnd);
|
||||
extern void prim_tileable(guchar *maz,
|
||||
guint x,
|
||||
guint y,
|
||||
gint rnd);
|
||||
|
||||
/* In handy.c */
|
||||
extern void get_colors (GDrawable * drawable,
|
||||
guint8 *fg,
|
||||
guint8 *bg);
|
||||
|
||||
static void drawbox (GPixelRgn *dest_rgn,
|
||||
extern void drawbox (GPixelRgn *dest_rgn,
|
||||
guint x,
|
||||
guint y,
|
||||
guint w,
|
||||
|
@ -141,6 +109,7 @@ MazeValues mvals =
|
|||
FALSE, /* Tileable? */
|
||||
57, /* multiple * These two had "Experiment with this?" comments */
|
||||
1, /* offset * in the maz.c source, so, lets expiriment. :) */
|
||||
DEPTH_FIRST, /* Algorithm */
|
||||
/* Interface options */
|
||||
TRUE /* Time seed? */
|
||||
};
|
||||
|
@ -155,26 +124,28 @@ query ()
|
|||
static GParamDef args[] =
|
||||
{
|
||||
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
|
||||
{ PARAM_IMAGE, "image", "Input image (unused)" },
|
||||
{ PARAM_DRAWABLE, "drawable", "Input drawable" },
|
||||
{ PARAM_IMAGE, "image_ID", "(unused)" },
|
||||
{ PARAM_DRAWABLE, "drawable_ID", "ID of drawable" },
|
||||
/* If we did have parameters, these be them: */
|
||||
{ PARAM_INT32, "mazep_width", "Width of the passages" },
|
||||
{ PARAM_INT32, "mazep_height", "Height of the passages"},
|
||||
{ PARAM_INT8, "maze_tile", "Tileable maze?"},
|
||||
{ PARAM_INT32, "maze_rseed", "Random Seed"},
|
||||
{ PARAM_INT32, "maze_multiple", "Multiple (use 57)" },
|
||||
{ PARAM_INT32, "maze_offset", "Offset (use 1)" }
|
||||
{ PARAM_INT16, "width", "Width of the passages" },
|
||||
{ PARAM_INT16, "height", "Height of the passages"},
|
||||
{ PARAM_INT8, "tileable", "Tileable maze?"},
|
||||
{ PARAM_INT8, "algorithm", "Generation algorithm"
|
||||
"(0=DEPTH FIRST, 1=PRIM'S ALGORITHM)" },
|
||||
{ PARAM_INT32, "seed", "Random Seed"},
|
||||
{ PARAM_INT16, "multiple", "Multiple (use 57)" },
|
||||
{ PARAM_INT16, "offset", "Offset (use 1)" }
|
||||
};
|
||||
static GParamDef *return_vals = NULL;
|
||||
static int nargs = sizeof (args) / sizeof (args[0]);
|
||||
static int nreturn_vals = 0;
|
||||
|
||||
gimp_install_procedure ("plug_in_maze",
|
||||
"Generates a maze.",
|
||||
"Generates a maze using the depth-first search method.",
|
||||
"Draws a maze.",
|
||||
"Generates a maze using either the depth-first search method or Prim's algorithm. Can make tileable mazes too. See " MAZE_URL " for more help.",
|
||||
"Kevin Turner <kevint@poboxes.com>",
|
||||
"Kevin Turner",
|
||||
"1997",
|
||||
"1997, 1998",
|
||||
"<Image>/Filters/Render/Maze",
|
||||
"RGB*, GRAY*, INDEXED*",
|
||||
PROC_PLUG_IN,
|
||||
|
@ -226,18 +197,19 @@ run (gchar *name,
|
|||
break;
|
||||
|
||||
case RUN_NONINTERACTIVE:
|
||||
if (nparams != 9)
|
||||
if (nparams != 10)
|
||||
{
|
||||
status = STATUS_CALLING_ERROR;
|
||||
}
|
||||
if (status == STATUS_SUCCESS)
|
||||
{
|
||||
mvals.width = (gint) param[3].data.d_int32;
|
||||
mvals.height = (gint) param[4].data.d_int32;
|
||||
mvals.seed = (gint) param[5].data.d_int32;
|
||||
mvals.tile = (gboolean) param[6].data.d_int32;
|
||||
mvals.multiple = (gint) param[7].data.d_int32;
|
||||
mvals.offset = (gint) param[8].data.d_int32;
|
||||
mvals.width = (gint16) param[3].data.d_int32;
|
||||
mvals.height = (gint16) param[4].data.d_int32;
|
||||
mvals.tile = (gint8) param[5].data.d_int32;
|
||||
mvals.algorithm = (gint8) param[6].data.d_int32;
|
||||
mvals.seed = (gint32) param[7].data.d_int32;
|
||||
mvals.multiple = (gint16) param[8].data.d_int32;
|
||||
mvals.offset = (gint16) param[9].data.d_int32;
|
||||
}
|
||||
break;
|
||||
case RUN_WITH_LAST_VALS:
|
||||
|
@ -251,7 +223,6 @@ run (gchar *name,
|
|||
|
||||
/* color, gray, or indexed... hmm, miss anything? ;) */
|
||||
if (gimp_drawable_color (drawable->id) || gimp_drawable_gray (drawable->id) || gimp_drawable_indexed (drawable->id)) {
|
||||
gimp_progress_init ("Drawing Maze...");
|
||||
|
||||
maze (drawable);
|
||||
|
||||
|
@ -270,34 +241,56 @@ run (gchar *name,
|
|||
gimp_drawable_detach (drawable);
|
||||
}
|
||||
|
||||
#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( GDrawable * drawable)
|
||||
{
|
||||
GPixelRgn dest_rgn;
|
||||
gint mw, mh;
|
||||
guint mw, mh;
|
||||
gint deadx, deady;
|
||||
gint progress, max_progress;
|
||||
guint progress, max_progress;
|
||||
gint x1, y1, x2, y2, x, y;
|
||||
gint dx, dy, xx, yy;
|
||||
gint foo, bar, baz;
|
||||
gint maz_x, maz_xx, maz_row, maz_yy;
|
||||
guint8 fg[4],bg[4];
|
||||
gpointer pr;
|
||||
gboolean active_selection;
|
||||
|
||||
gchar *maz;
|
||||
guchar *maz;
|
||||
guint pos;
|
||||
|
||||
/* Gets the input area... */
|
||||
gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
|
||||
active_selection = gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
|
||||
|
||||
/* Initialize pixel region (?) */
|
||||
gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
|
||||
/***************** Maze Stuff Happens Here ***************/
|
||||
|
||||
progress = 0;
|
||||
max_progress = (x2 - x1) * (y2 - y1);
|
||||
|
||||
/* Get the foreground and background colors */
|
||||
get_colors(drawable,fg,bg);
|
||||
|
||||
/* Maze Stuff Happens Here */
|
||||
mw = (x2-x1) / mvals.width;
|
||||
mh = (y2-y1) / mvals.height;
|
||||
|
||||
|
@ -314,7 +307,7 @@ maze( GDrawable * drawable)
|
|||
deadx = ((x2-x1) - mw * mvals.width)/2;
|
||||
deady = ((y2-y1) - mh * mvals.height)/2;
|
||||
|
||||
maz = g_malloc0(mw * mh);
|
||||
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",
|
||||
|
@ -324,13 +317,75 @@ maze( GDrawable * drawable)
|
|||
if (mvals.timeseed)
|
||||
mvals.seed = time(NULL);
|
||||
|
||||
if (mvals.tile) {
|
||||
(void) mazegen_tileable((mw+1), maz, mw, mh, mvals.seed);
|
||||
} else {
|
||||
(void) mazegen((mw+1), maz, mw, mh, mvals.seed);
|
||||
/* (void) mazegen(((x2-x1)+1), maz, (x2-x1), (y2-y1), rnd); */
|
||||
/* Sanity check: */
|
||||
switch (mvals.algorithm) {
|
||||
case DEPTH_FIRST:
|
||||
break;
|
||||
case PRIMS_ALGORITHM:
|
||||
break;
|
||||
default:
|
||||
g_warning("maze: Invalid algorithm choice %d", mvals.algorithm);
|
||||
}
|
||||
/* It's done happening. Now go through and color dem pixels... */
|
||||
|
||||
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, mvals.seed);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
} else { /* not tileable */
|
||||
if (active_selection) { /* Mask and draw mazes until there's no
|
||||
* more room left. */
|
||||
mask_maze(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, mvals.seed);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
} /* switch */
|
||||
} /* if maz[] == 0 */
|
||||
} /* next maz_xx */
|
||||
} /* next maz_yy */
|
||||
} 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, mvals.seed);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
} /* switch */
|
||||
} /* no active selection */
|
||||
} /* not tileable */
|
||||
|
||||
/************** Begin Drawing *********************/
|
||||
|
||||
/* Initialize pixel region (?) */
|
||||
gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, (x2 - x1), (y2 - y1), TRUE, TRUE);
|
||||
|
||||
progress = 0;
|
||||
max_progress = (x2 - x1) * (y2 - y1);
|
||||
|
||||
/* 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;
|
||||
|
@ -345,375 +400,276 @@ maze( GDrawable * drawable)
|
|||
|
||||
dx = mvals.width - (x % mvals.width);
|
||||
dy = mvals.height - (y % mvals.height);
|
||||
foo = x/mvals.width;
|
||||
bar = mw * (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[foo+bar] ? fg : bg);
|
||||
(maz[maz_row+maz_x]==IN) ? fg : bg);
|
||||
|
||||
baz=foo+1;
|
||||
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[bar + baz++] ? fg : bg ); }
|
||||
(maz[maz_row + maz_xx++]==IN) ? fg : bg ); }
|
||||
|
||||
baz=bar+mw;
|
||||
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[foo + baz] ? fg : bg );
|
||||
baz += mw;
|
||||
(maz[maz_yy + maz_x]==IN) ? fg : bg );
|
||||
maz_yy += mw;
|
||||
}
|
||||
|
||||
foo++;
|
||||
maz_x++;
|
||||
/* Everything else */
|
||||
for(yy=dy; yy < dest_rgn.h; yy+=mvals.height) {
|
||||
baz = foo; bar+=mw;
|
||||
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[bar + baz++] ? fg : bg ); }
|
||||
(maz[maz_row + maz_xx++]==IN) ? fg : bg ); }
|
||||
}
|
||||
|
||||
progress += dest_rgn.w * dest_rgn.h;
|
||||
gimp_progress_update ((double) progress / (double) max_progress);
|
||||
/* Note the progess indicator doesn't indicate how much of the maze
|
||||
has been built... It just indicates how much has been drawn
|
||||
*after* building... Thing is, that's what takes longer. */
|
||||
/* Indicate progress in drawing. */
|
||||
}
|
||||
gimp_drawable_flush (drawable);
|
||||
gimp_drawable_merge_shadow (drawable->id, TRUE);
|
||||
gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
|
||||
}
|
||||
|
||||
/* Draws a solid color box in a GPixelRgn. */
|
||||
/* Optimization assumptions:
|
||||
* (Or, "Why Maze is Faster Than Checkerboard.")
|
||||
/* 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.
|
||||
*
|
||||
* 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.
|
||||
* So we'll put unavailable cells in a non-zero non-in non-out class
|
||||
* called MASKED.
|
||||
*/
|
||||
|
||||
/* 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... */
|
||||
/* 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
|
||||
drawbox( GPixelRgn *dest_rgn,
|
||||
guint x, guint y, guint w, guint h,
|
||||
guint8 clr[4])
|
||||
mask_maze(gint32 drawable_ID, guchar *maz, guint mw, guint mh,
|
||||
gint x1, gint x2, gint y1, gint y2, gint deadx, gint deady)
|
||||
{
|
||||
guint xx, yy, y_max, x_min /*, x_max */;
|
||||
static guint8 *rowbuf;
|
||||
guint rowsize;
|
||||
static guint high_size=0;
|
||||
gint32 selection_ID;
|
||||
GPixelRgn sel_rgn;
|
||||
gint xx0=0, yy0=0, xoff, yoff;
|
||||
guint xx=0, yy=0;
|
||||
guint foo=0;
|
||||
|
||||
/* The maximum [xy] value is that of the far end of the box, or
|
||||
* the edge of the region, whichever comes first. */
|
||||
gint cur_row, cur_col;
|
||||
gint x1half, x2half, y1half, y2half;
|
||||
guchar *linebuf;
|
||||
|
||||
y_max = dest_rgn->rowstride * MIN(dest_rgn->h, (y + h));
|
||||
/* x_max = dest_rgn->bpp * MIN(dest_rgn->w, (x + w)); */
|
||||
if ((selection_ID =
|
||||
gimp_image_get_selection(gimp_drawable_image_id(drawable_ID)))
|
||||
== -1)
|
||||
return;
|
||||
|
||||
x_min = x * dest_rgn->bpp;
|
||||
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);
|
||||
|
||||
/* rowsize = x_max - x_min */
|
||||
rowsize = dest_rgn->bpp * MIN(dest_rgn->w, (x + w)) - x_min;
|
||||
/* Special cases: If mw or mh < 3 */
|
||||
/* FIXME (Currently works, but inefficiently.) */
|
||||
|
||||
/* Does the row buffer need to be (re)allocated? */
|
||||
if (high_size == 0) {
|
||||
rowbuf = g_new(guint8, rowsize);
|
||||
} else if (rowsize > high_size) {
|
||||
g_realloc(rowbuf, rowsize * sizeof(guint8) );
|
||||
}
|
||||
/* mw && mh => 3 */
|
||||
|
||||
high_size = MAX(high_size, rowsize);
|
||||
linebuf = g_new(guchar, sel_rgn.w * sel_rgn.bpp);
|
||||
|
||||
/* Fill the row buffer with the color. */
|
||||
for (xx= 0;
|
||||
xx < rowsize;
|
||||
xx+= dest_rgn->bpp) {
|
||||
memcpy (&rowbuf[xx], clr, dest_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++;
|
||||
} /* next xx */
|
||||
} /* next yy*/
|
||||
|
||||
/* 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);
|
||||
} /* next yy */
|
||||
}
|
||||
|
||||
/* The old drawbox code, preserved here 'just in case' something
|
||||
doesn't go right. */
|
||||
} /* mask_maze */
|
||||
|
||||
/* The attempt to implement this with tiles: (it wasn't fun) */
|
||||
#if 0
|
||||
for (xx= x * dest_rgn->bpp;
|
||||
xx < bar;
|
||||
xx+= dest_rgn->bpp) {
|
||||
#if 0
|
||||
for (bp=0; bp < dest_rgn->bpp; bp++) {
|
||||
dest_rgn->data[yy+xx+bp]=clr[bp];
|
||||
} /* next bp */
|
||||
#else
|
||||
memcpy (&dest_rgn->data[yy+xx], clr, dest_rgn->bpp);
|
||||
#endif
|
||||
|
||||
/* 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++;
|
||||
} /* next xx */
|
||||
maz_row+=mw;
|
||||
} /* next yy */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The Incredible Recursive Maze Generation Routine */
|
||||
/* Ripped from rec.programmers.games maze-faq */
|
||||
/* Modified and commented by me, Kevin Turner. */
|
||||
gint mazegen(pos, maz, x, y, rnd)
|
||||
gint pos, x, y, rnd;
|
||||
gchar *maz;
|
||||
{
|
||||
gchar d, i;
|
||||
gint c=0, j=1;
|
||||
|
||||
/* Punch a hole here... */
|
||||
maz[pos] = 1;
|
||||
|
||||
/* 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) { /* This is simple enough. */
|
||||
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 1; /* Hey neat, broken mazes! */
|
||||
break; /* (Umm... Wow... Yeah, neat.) */
|
||||
default:
|
||||
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);
|
||||
|
||||
} /* End while(d=...) Loop */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ABSMOD(A,B) ( ((A) < 0) ? (((B) + (A)) % (B)) : ((A) % (B)) )
|
||||
|
||||
/* Tileable mazes are my creation, based on the routine above. */
|
||||
static gint mazegen_tileable(gint pos, gchar *maz, gint x, gint y, gint rnd)
|
||||
{
|
||||
gchar d, i;
|
||||
gint c=0, j=1, npos=2;
|
||||
|
||||
/* Punch a hole here... */
|
||||
maz[pos] = 1;
|
||||
|
||||
/* If there is a wall two rows above us, bit 1 is 1. */
|
||||
while((d= (pos < (x*2) ? (maz[x*(y-2)+pos] ? 0 : 1) : (maz[pos - x - x ] ? 0 : 1))
|
||||
/* If there is a wall two rows below us, bit 2 is 1. */
|
||||
| (pos >= x * (y-2) ? (maz[pos - x*(y-2)] ? 0 : 2) : (maz[pos +x+x] ? 0 : 2))
|
||||
/* If there is a wall two columns to the right, bit 3 is 1. */
|
||||
| (pos % x >= x - 2 ? (maz[pos + 2 - x] ? 0 : 4) : (maz[pos + 2] ? 0 : 4))
|
||||
/* If there is a wall two colums to the left, bit 4 is 1. */
|
||||
| ((pos % x <= 1 ) ? (maz[pos + x - 2] ? 0 : 8) : (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) { /* This is simple enough. */
|
||||
case 0: /* Go in the direction we just figured . . . */
|
||||
j = pos < x ? x*(y-1)+pos : pos - x;
|
||||
npos = pos < (x*2) ? x*(y-2)+pos : pos - x - x;
|
||||
break;
|
||||
case 1:
|
||||
j = pos >= x*(y-1) ? pos - x * (y-1) : pos + x;
|
||||
npos = pos >= x*(y-2) ? pos - x*(y-2) : pos + x + x;
|
||||
break;
|
||||
case 2:
|
||||
j = (pos % x) == (x - 1) ? pos + 1 - x : pos + 1;
|
||||
npos = (pos % x) >= (x - 2) ? pos + 2 - x : pos + 2;
|
||||
break;
|
||||
case 3:
|
||||
j= (pos % x) == 0 ? pos + x - 1 : pos - 1;
|
||||
npos = (pos % x) <= 1 ? pos + x - 2 : pos - 2;
|
||||
break;
|
||||
case 99:
|
||||
return 1; /* Hey neat, broken mazes! */
|
||||
break; /* (Umm... Wow... Yeah, neat.) */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* And punch a hole there. */
|
||||
maz[j] = 1;
|
||||
|
||||
/* Now, start again just past where we punched the hole... */
|
||||
mazegen_tileable(npos, maz, x, y, rnd);
|
||||
|
||||
} /* End while(d=...) Loop */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
get_colors (GDrawable *drawable, guint8 *fg, guint8 *bg)
|
||||
{
|
||||
GParam *return_vals;
|
||||
gint nreturn_vals;
|
||||
|
||||
switch ( gimp_drawable_type (drawable->id) )
|
||||
{
|
||||
case RGBA_IMAGE: /* ASSUMPTION: Assuming the user wants entire */
|
||||
fg[3] = 255; /* area to be fully opaque. */
|
||||
bg[3] = 255;
|
||||
case RGB_IMAGE:
|
||||
|
||||
return_vals = gimp_run_procedure ("gimp_palette_get_foreground",
|
||||
&nreturn_vals,
|
||||
PARAM_END);
|
||||
|
||||
if (return_vals[0].data.d_status == STATUS_SUCCESS)
|
||||
{
|
||||
fg[0] = return_vals[1].data.d_color.red;
|
||||
fg[1] = return_vals[1].data.d_color.green;
|
||||
fg[2] = return_vals[1].data.d_color.blue;
|
||||
}
|
||||
else
|
||||
{
|
||||
fg[0] = 255;
|
||||
fg[1] = 255;
|
||||
fg[2] = 255;
|
||||
}
|
||||
return_vals = gimp_run_procedure ("gimp_palette_get_background",
|
||||
&nreturn_vals,
|
||||
PARAM_END);
|
||||
|
||||
if (return_vals[0].data.d_status == STATUS_SUCCESS)
|
||||
{
|
||||
bg[0] = return_vals[1].data.d_color.red;
|
||||
bg[1] = return_vals[1].data.d_color.green;
|
||||
bg[2] = return_vals[1].data.d_color.blue;
|
||||
}
|
||||
else
|
||||
{
|
||||
bg[0] = 0;
|
||||
bg[1] = 0;
|
||||
bg[2] = 0;
|
||||
}
|
||||
break;
|
||||
case GRAYA_IMAGE: /* and again */
|
||||
fg[1] = 255;
|
||||
bg[1] = 255;
|
||||
case GRAY_IMAGE:
|
||||
|
||||
return_vals = gimp_run_procedure ("gimp_palette_get_foreground",
|
||||
&nreturn_vals,
|
||||
PARAM_END);
|
||||
|
||||
if (return_vals[0].data.d_status == STATUS_SUCCESS)
|
||||
{
|
||||
fg[0] = 0.30 * return_vals[1].data.d_color.red +
|
||||
0.59 * return_vals[1].data.d_color.green +
|
||||
0.11 * return_vals[1].data.d_color.blue;
|
||||
}
|
||||
else
|
||||
{
|
||||
fg[0] = 255;
|
||||
}
|
||||
|
||||
return_vals = gimp_run_procedure ("gimp_palette_get_background",
|
||||
&nreturn_vals,
|
||||
PARAM_END);
|
||||
|
||||
if (return_vals[0].data.d_status == STATUS_SUCCESS)
|
||||
{
|
||||
bg[0] = 0.30 * return_vals[1].data.d_color.red +
|
||||
0.59 * return_vals[1].data.d_color.green +
|
||||
0.11 * return_vals[1].data.d_color.blue;
|
||||
}
|
||||
else
|
||||
{
|
||||
bg[0] = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case INDEXEDA_IMAGE:
|
||||
case 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;
|
||||
}
|
||||
}
|
||||
} /* next pr sel_rgn tile thing */
|
||||
/* #ifdef MAZE_DEBUG
|
||||
maze_dump(maz,mw,mh);
|
||||
#endif */
|
||||
} /* mask_maze */
|
||||
#endif /* 0 */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#define MAZE_TITLE "Maze 0.6.2"
|
||||
#define MAZE_URL "http://www.poboxes.com/kevint/gimp/maze.html"
|
||||
#define MAZE_TITLE "Maze 1.2.1"
|
||||
#define MAZE_URL "http://www.poboxes.com/kevint/gimp/maze-help.html"
|
||||
|
||||
#define HELP_OPENS_NEW_WINDOW FALSE
|
||||
|
||||
|
@ -8,15 +8,36 @@
|
|||
latter leaves much to be desired. */
|
||||
#define DIVBOX_LOOKS_LIKE_SPINBUTTON FALSE
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
/* 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
|
||||
|
||||
#include "glib.h"
|
||||
|
||||
typedef enum {
|
||||
DEPTH_FIRST,
|
||||
PRIMS_ALGORITHM
|
||||
} MazeAlgoType;
|
||||
|
||||
typedef struct {
|
||||
gint width;
|
||||
gint height;
|
||||
gint seed;
|
||||
guint seed;
|
||||
gboolean tile;
|
||||
gint multiple;
|
||||
gint offset;
|
||||
MazeAlgoType algorithm;
|
||||
/* Interface options. */
|
||||
gboolean timeseed;
|
||||
} MazeValues;
|
||||
|
||||
enum CellTypes {
|
||||
OUT,
|
||||
IN,
|
||||
FRONTIER,
|
||||
MASKED
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* maze_face.c, version 0.6.2, March 7, 1998.
|
||||
/* maze_face.c
|
||||
* User interface for plug-in-maze.
|
||||
*
|
||||
* Implemented as a GIMP 0.99 Plugin by
|
||||
|
@ -19,7 +19,7 @@
|
|||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -29,7 +29,6 @@
|
|||
#endif /* MAZE_DEBUG */
|
||||
|
||||
#include "maze.h"
|
||||
|
||||
#include "libgimp/gimp.h"
|
||||
#include "libgimp/gimpui.h"
|
||||
|
||||
|
@ -113,6 +112,8 @@ 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 void toggle_callback (GtkWidget *widget, gboolean *data);
|
||||
static void alg_radio_callback (GtkWidget *widget, gpointer data);
|
||||
|
||||
static GtkWidget* divbox_new (guint *max,
|
||||
GtkWidget *friend,
|
||||
GtkWidget **div_entry);
|
||||
|
@ -125,7 +126,7 @@ static void div_buttonr_callback (GtkObject *object);
|
|||
/* entscale stuff begin */
|
||||
static GtkWidget* entscale_int_new ( GtkWidget *table, gint x, gint y,
|
||||
gchar *caption, gint *intvar,
|
||||
gint min, gint max, gint constraint,
|
||||
gint min, gint max, gboolean constraint,
|
||||
EntscaleIntCallbackFunc callback,
|
||||
gpointer data );
|
||||
|
||||
|
@ -168,6 +169,8 @@ gint maze_dialog()
|
|||
GtkWidget *div_x_hbox, *div_y_hbox;
|
||||
GtkWidget *div_x_label, *div_y_label, *div_x_entry, *div_y_entry;
|
||||
|
||||
GtkWidget *alg_box, *alg_button;
|
||||
|
||||
gchar **argv;
|
||||
gint argc;
|
||||
gchar buffer[32];
|
||||
|
@ -239,7 +242,6 @@ gint maze_dialog()
|
|||
GTK_FRAME(msg_frame)->label) + 7);
|
||||
#endif
|
||||
|
||||
|
||||
/* Set up Options page */
|
||||
frame = gtk_frame_new ("Maze Options");
|
||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||||
|
@ -321,8 +323,6 @@ gint maze_dialog()
|
|||
|
||||
gtk_widget_show (div_y_hbox);
|
||||
|
||||
|
||||
|
||||
/* Add Options page to notebook */
|
||||
gtk_widget_show (frame);
|
||||
gtk_widget_show (table);
|
||||
|
@ -331,11 +331,10 @@ gint maze_dialog()
|
|||
gtk_label_new ("Options"));
|
||||
|
||||
/* Set up other page */
|
||||
frame = gtk_frame_new ("Don't change these");
|
||||
frame = gtk_frame_new ("At Your Own Risk");
|
||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||||
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
||||
/* gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); */
|
||||
table = gtk_table_new (3, 2, FALSE);
|
||||
table = gtk_table_new (4, 2, FALSE);
|
||||
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
||||
gtk_container_add (GTK_CONTAINER (frame), table);
|
||||
|
||||
|
@ -399,6 +398,35 @@ gint maze_dialog()
|
|||
gtk_widget_show (time_button);
|
||||
gtk_widget_show (seed_hbox);
|
||||
|
||||
label = gtk_label_new("Algorithm");
|
||||
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
|
||||
gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
|
||||
GTK_FILL, 0, 5, 5);
|
||||
gtk_widget_show (label);
|
||||
|
||||
alg_box=gtk_vbox_new(FALSE, 5);
|
||||
gtk_table_attach (GTK_TABLE (table), alg_box, 1, 2, 3, 4,
|
||||
GTK_FILL, 0, 5, 5);
|
||||
gtk_widget_show (alg_box);
|
||||
|
||||
alg_button=gtk_radio_button_new_with_label (NULL,"Depth First");
|
||||
gtk_signal_connect(GTK_OBJECT(alg_button),"toggled",
|
||||
GTK_SIGNAL_FUNC(alg_radio_callback), (gpointer)DEPTH_FIRST);
|
||||
if(mvals.algorithm==DEPTH_FIRST)
|
||||
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(alg_button), TRUE);
|
||||
gtk_container_add(GTK_CONTAINER(alg_box),alg_button);
|
||||
gtk_widget_show(alg_button);
|
||||
|
||||
alg_button=gtk_radio_button_new_with_label (gtk_radio_button_group(
|
||||
GTK_RADIO_BUTTON(alg_button)), "Prim's Algorithm");
|
||||
gtk_signal_connect(GTK_OBJECT(alg_button),"toggled",
|
||||
GTK_SIGNAL_FUNC(alg_radio_callback), (gpointer)PRIMS_ALGORITHM);
|
||||
if(mvals.algorithm==PRIMS_ALGORITHM)
|
||||
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(alg_button), TRUE);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(alg_box),alg_button);
|
||||
gtk_widget_show(alg_button);
|
||||
|
||||
/* Add Advanced page to notebook */
|
||||
gtk_widget_show (frame);
|
||||
gtk_widget_show (table);
|
||||
|
@ -633,12 +661,16 @@ maze_close_callback (GtkWidget *widget,
|
|||
static void
|
||||
maze_help (GtkWidget *widget, gpointer foo)
|
||||
{
|
||||
void *bar=(void *)NULL;
|
||||
char *proc_blurb, *proc_help, *proc_author, *proc_copyright, *proc_date;
|
||||
int proc_type, nparams, nreturn_vals;
|
||||
GParamDef *params, *return_vals;
|
||||
gint baz;
|
||||
|
||||
if (gimp_query_procedure("extension_web_browser",
|
||||
bar, bar, bar, bar, bar,
|
||||
bar, bar, bar, bar, bar)) {
|
||||
&proc_blurb, &proc_help,
|
||||
&proc_author, &proc_copyright, &proc_date,
|
||||
&proc_type, &nparams, &nreturn_vals,
|
||||
¶ms, &return_vals)) {
|
||||
maze_msg("Opening " MAZE_URL);
|
||||
gimp_run_procedure("extension_web_browser", &baz,
|
||||
PARAM_INT32, RUN_NONINTERACTIVE,
|
||||
|
@ -681,6 +713,12 @@ toggle_callback (GtkWidget *widget, gboolean *data)
|
|||
*data = GTK_TOGGLE_BUTTON (widget)->active;
|
||||
}
|
||||
|
||||
static void
|
||||
alg_radio_callback (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
mvals.algorithm=(MazeAlgoType)data;
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
/* As found in pixelize.c,
|
||||
* hacked to return a pointer to the entry widget. */
|
||||
|
@ -710,7 +748,7 @@ toggle_callback (GtkWidget *widget, gboolean *data)
|
|||
static GtkWidget*
|
||||
entscale_int_new ( GtkWidget *table, gint x, gint y,
|
||||
gchar *caption, gint *intvar,
|
||||
gint min, gint max, gint constraint,
|
||||
gint min, gint max, gboolean constraint,
|
||||
EntscaleIntCallbackFunc callback,
|
||||
gpointer call_data)
|
||||
{
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
(gimp-palette-set-foreground '(14 14 14))))
|
||||
|
||||
(gimp-selection-load image active-selection)
|
||||
(plug-in-maze 1 image active-layer 5 5 TRUE seed 57 1)
|
||||
(plug-in-maze 1 image active-layer 5 5 TRUE 0 seed 57 1)
|
||||
(plug-in-oilify 1 image active-layer mask-size 0)
|
||||
(plug-in-edge 1 image active-layer 2 1)
|
||||
(gimp-desaturate image active-layer)
|
||||
|
|
Loading…
Reference in New Issue