/**
    \file DistanceTransform.cpp
    Implementation for DistanceTransform abstract base class which, given and
    input binary image, calculates the corresponding distance transform via
    various methods.

    \author George J. Grevera, Ph.D., ggrevera@sju.edu

    Copyright (C) 2002, George J. Grevera

    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.

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
    USA or from http://www.gnu.org/licenses/gpl.txt.

    This General Public License does not permit incorporating this
    code into proprietary programs.  (So a hypothetical company such
    as GH (Generally Hectic) should NOT incorporate this code into
    their proprietary programs.)
 */

#include <assert.h>
#include <float.h>
#include <iostream>
#include <set>
#include <math.h>
#include <stdlib.h>

// no OUT OF BOUNDS checks!

#include "DistanceTransform.h"

// since we'll be adding pairs of values, one or both of which may be
// "infinity," we need to define "infinity" to be half of the max
// value allowed for a particular numeric type

const int    DistanceTransform::IntInfinity   = INT_MAX / 2;
const double DistanceTransform::FloatInfinity = FLT_MAX / 2.0;

using namespace std;
//----------------------------------------------------------------------
DistanceTransform::DistanceTransform ( const int xSize,  const int ySize,
                                       const bool unload ) {
    this->iD = NULL;
    this->dD = NULL;
    //this->xSize = this->ySize = -1;
    //this->rowOffsets = NULL;

    assert( xSize>0 && ySize>0 );
    this->xSize = xSize;
    this->ySize = ySize;
    this->rowOffsets = (int*)malloc( ySize * sizeof(int) );
    assert( this->rowOffsets!=NULL );
    for (int y=0; y<ySize; y++)    this->rowOffsets[y] = y*xSize;

    this->unloadFlag = unload;
    if (unload)
        cout << "ints WILL be converted to doubles." << endl;
    else
        cout << "ints will NOT be converted to doubles." << endl;
}
//----------------------------------------------------------------------
DistanceTransform::~DistanceTransform ( ) {
    cleanUp();
    if (this->rowOffsets!=NULL) { free(this->rowOffsets);  this->rowOffsets=NULL; }
}
//----------------------------------------------------------------------
void DistanceTransform::cleanUp ( ) {
    if (this->iD != NULL)  {  free(this->iD);  this->iD=NULL;  }
    if (this->dD != NULL)  {  free(this->dD);  this->dD=NULL;  }
}
//----------------------------------------------------------------------
void DistanceTransform::borderCheck ( const unsigned char* const I ) {
    for (int x=0; x<xSize; x++) {
        //check the first row
        if (I[sub(x,0)]!=0)          cout << "borderCheck: failed." << endl;
        //check the last row
        if (I[sub(x,ySize-1)]!=0)    cout << "borderCheck: failed." << endl;
    }

    for (int y=0; y<ySize; y++) {
        //check the first col
        if (I[sub(0,y)]!=0)          cout << "borderCheck: failed." << endl;
        //check the last col
        if (I[sub(xSize-1,y)]!=0)    cout << "borderCheck: failed." << endl;
    }
}
//----------------------------------------------------------------------
/** called to finish up after distance transform employing doubles */
void DistanceTransform::finish ( const unsigned char* const I, double* d ) {

    //indicate inside & outside
    for (int y=0; y<ySize; y++) {
        for (int x=0; x<xSize; x++) {
            const int i=sub(x,y);
            if (I[i] == 0)    d[i] = -d[i];
        }
    }

    this->dD = d;
}
//----------------------------------------------------------------------
/** called to finish up after distance transform employing ints */
void DistanceTransform::finish ( const unsigned char* const I,
                                 int* d, const int dx, const int dy )
{
    // "unload" == "convert ints to doubles"
    if (this->unloadFlag) {
        assert( dx==dy );
        double*  dblResult = (double*)malloc( ySize*xSize*sizeof(double) );
        assert( dblResult!=NULL );
        //indicate inside & outside
        for (int y=0; y<ySize; y++) {
            for (int x=0; x<xSize; x++) {
                const int i=sub(x,y);
                if (I[i] == 0)    d[i] = -d[i];
                //if (d[i] != IntInfinity)  dblResult[i] = d[i]/((double)dx);
                //else                      dblResult[i] = FloatInfinity;
                dblResult[i] = d[i];
                if (d[i] > -IntInfinity && d[i] < IntInfinity)
                    dblResult[i] /= dx;
            }
        }
        this->dD = dblResult;
        free(d);  d=NULL;  this->iD=NULL;

        return;
    }

    //otherwise, don't "unload" ints (i.e., don't convert them to doubles)

    //indicate inside & outside
    for (int y=0; y<ySize; y++) {
        for (int x=0; x<xSize; x++) {
            const int i=sub(x,y);
            if (I[i] == 0)    d[i] = -d[i];
        }
    }
    this->iD = d;
}
//----------------------------------------------------------------------
