mirror of https://github.com/GNOME/gimp.git
599 lines
17 KiB
C
599 lines
17 KiB
C
/* $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 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.
|
|
*
|
|
*/
|
|
|
|
#ifndef SOLO_COMPILE
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include "maze.h"
|
|
#include "libgimp/gimp.h"
|
|
#include "libgimp/gimpui.h"
|
|
#include "libgimp/stdplugins-intl.h"
|
|
|
|
extern MazeValues mvals;
|
|
extern GRand *gr;
|
|
|
|
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(gint pos,
|
|
gchar *maz,
|
|
guint x,
|
|
guint y);
|
|
void prim_tileable(gchar *maz,
|
|
guint x,
|
|
guint y);
|
|
|
|
#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(gint pos, gchar *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, max_progress;
|
|
char d, i;
|
|
guint c=0;
|
|
gint rnd = mvals.seed;
|
|
|
|
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:
|
|
;
|
|
}
|
|
}
|
|
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:
|
|
;
|
|
}
|
|
}
|
|
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:
|
|
;
|
|
}
|
|
}
|
|
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:
|
|
;
|
|
}
|
|
}
|
|
|
|
/* 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);
|
|
|
|
} /* while front_cells */
|
|
|
|
g_slist_free(front_cells);
|
|
} /* prim */
|
|
|
|
void
|
|
prim_tileable(gchar *maz, guint x, guint y)
|
|
{
|
|
GSList *front_cells=NULL;
|
|
guint current, pos;
|
|
guint up, down, left, right;
|
|
guint progress=0, max_progress;
|
|
char d, i;
|
|
guint c=0;
|
|
gint rnd = mvals.seed;
|
|
|
|
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. */
|
|
g_rand_set_seed (gr, rnd);
|
|
|
|
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:
|
|
;
|
|
}
|
|
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:
|
|
;
|
|
}
|
|
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:
|
|
;
|
|
}
|
|
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:
|
|
;
|
|
}
|
|
|
|
/* 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);
|
|
|
|
} /* while front_cells */
|
|
|
|
g_slist_free(front_cells);
|
|
}
|