/* random.c: distort a bitmap.

Copyright (C) 1992 Free Software Foundation, Inc.

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, 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 "config.h"

#include "rand.h"

#include "random.h"

/* Distort the bitmap (see below).  (-randomize)  */
real random_max = 0.0;
real random_threshold = 0.2;


static bitmap_type enlarge_bitmap (bitmap_type, int, int);
static real random_number (void);



/* Randomize the bitmap B by moving each black pixel a randomly chosen
   distance between -random_max and random_max (both horizontally and
   vertically).  Return the enlargement of B on all four side in ADJUST.
   */

void
randomize_bitmap (bitmap_type *b, bounding_box_type *adjust)
{
  unsigned this_row, this_col;

  MIN_ROW (*adjust) = MAX_ROW (*adjust) = MIN_COL (*adjust) = MAX_COL (*adjust)
    = 0;

  for (this_row = 0; this_row < BITMAP_HEIGHT (*b); this_row++)
    for (this_col = 0; this_col < BITMAP_WIDTH (*b); this_col++)
      {
        if (BITMAP_PIXEL (*b, this_row, this_col) != WHITE)
          {
            int row_enlarge, col_enlarge;
            /* Generate two random numbers between -random_max and
               random_max to give rise to the new x and y.  */
            real random1 = random_number ();
            int row_delta = random1 + .5 * SIGN (random1);
            int new_row = this_row + row_delta;

            real random2 = random_number ();
            int col_delta = random2 + .5 * SIGN (random2);
            int new_col = this_col + col_delta;

            /* Clear the old location before we (possibly) mess with the
               bitmap dimensions.  */
            BITMAP_PIXEL (*b, this_row, this_col) = WHITE;

            /* Check if the new location is outside the current bitmap.  */
            if (new_row < 0)
              {
                row_enlarge = new_row;
                /* Negative rows are off the top of the bitmap -- it's
                   in C coordinates, not Cartesian.  */
                MAX_ROW (*adjust) -= row_enlarge;
              }
            else if (new_row >= BITMAP_HEIGHT (*b))
              {
                row_enlarge = new_row - BITMAP_HEIGHT (*b) + 1;
                MIN_ROW (*adjust) += row_enlarge;
              }
            else
              row_enlarge = 0;

            if (new_col < 0)
              {
                col_enlarge = new_col;
                MIN_COL (*adjust) -= col_enlarge;
              }
            else if (new_col >= BITMAP_WIDTH (*b))
              {
                col_enlarge = new_col - BITMAP_WIDTH (*b) + 1;
                MAX_COL (*adjust) += col_enlarge;
              }
            else
              col_enlarge = 0;

            if (row_delta != 0 || col_delta != 0)
              { /* Enlarge the bitmap to make the new location valid.  */
                bitmap_type new_b
                  = enlarge_bitmap (*b, row_enlarge, col_enlarge);
                free_bitmap (b);
                *b = new_b;
                
                /* We've made the new row/column at the edge.  */
                if (new_row < 0) new_row = 0;
                if (new_col < 0) new_col = 0;
              }

            BITMAP_PIXEL (*b, new_row, new_col) = BLACK;
          }
      }
}


/* Return the enlargement of the bitmap B by ROW_DELTA and COL_DELTA. 
   Negative deltas enlarge to the bottom/left, positive to the
   top/right.  */

static bitmap_type
enlarge_bitmap (bitmap_type b, int row_delta, int col_delta)
{
  bitmap_type new;
  dimensions_type new_size;
  unsigned row;
  int row_addend = row_delta < 0 ? abs (row_delta) : 0;
  int col_addend = col_delta < 0 ? abs (col_delta) : 0;
  
  DIMENSIONS_HEIGHT (new_size) = BITMAP_HEIGHT (b) + abs (row_delta);
  DIMENSIONS_WIDTH (new_size) = BITMAP_WIDTH (b) + abs (col_delta);
  
  new = new_bitmap (new_size);
  
  /* `new_bitmap' initializes all the pixels to white, so we don't have
     to do anything to the new rows and columns.  */
  for (row = 0; row < BITMAP_HEIGHT (b); row++)
    {
      one_byte *source = BITMAP_ROW (b, row);
      one_byte *target = BITMAP_ROW (new, row + row_addend) + col_addend;
      
      memcpy (target, source, BITMAP_WIDTH (b));
    }
  
  return new;
}



/* Return a random number between -random_max and random_max.  */

static real
random_number ()
{
  static boolean initialized = false;
  real r;
  
  if (!initialized)
    {
      seed_rand (getpid ());
      initialized = true;
    }

  /* If the random number is less than `random_threshold', return zero.
     Otherwise, return a random number in the interval
     [-random_max,random_max].  */
  r = (real) k_rand () / RAND_MAX;
  r = r > random_threshold
      ? (real) k_rand () / RAND_MAX * random_max - random_max
      : 0;

  return r;
}
