/*
 * $Id: transferBox.cxx,v 4.0 2003/04/28 14:40:07 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 * */

/*------------------------------------------------------------------------
 *
 * A subclass of Box for drawing transfer functions
 *
 * Hugues Talbot	 3 May 1998
 *      
 *-----------------------------------------------------------------------*/

#include <FL/math.h>

#include "imview.hxx"
#include "imageIO.hxx"
#include "imageViewer.hxx"
#include "menubar.hxx"
#include "transferFunction.hxx"
#include "transferRGBFunction.hxx"
#include "transferBox.hxx"

extern imageIO *IOBlackBox;
extern imageViewer *mainViewer;
extern imViewMenuBar *mainMenuBar;
extern transfer  *transferEditorPanel;
extern transferRGB *transferRGBEditorPanel;

myTransferBox::~myTransferBox()
{
    dbgprintf("Transfer Box destroyed");
    return;
}

void myTransferBox::draw()
{
    draw_box();
    draw_label();

    // the superclass actually does no further drawing

    return;
}


void myTransferBox::computeEndPoints(double &x0, double &y0, double &x1, double &y1)
{
    double dx, dy;

     // OK, draw the transfer function now!
    if (currContrast >= -0.5 && currContrast <= 0.5) {
	dy = tan(currContrast*M_PI/2);
	if (dy != 0) {
	    x0 = currBrightness - 0.5/dy;
	    x1 = 0.5/dy + currBrightness;
	} else {
	    x0 = 0;
	    x1 = 1;
	}
	y0 = 0.5 - dy*currBrightness;
	y1 = 0.5 + dy*(1-currBrightness);
    } else {
	dx = 1/tan(currContrast*M_PI/2);
	dbgprintf("Contrast=%g, dx= %g\n", currContrast, dx);
	if (fabs(dx) >= 1e-7) {
	    y0 = 0.5 - currBrightness/dx;
	    y1 = (1-currBrightness)/dx +0.5;
	} else {
	    if (dx >= 0) {
		y0 = -2;
		y1 = 2;
	    } else {
		y0 = 200;
		y1 = -200;
	    }
	}
	x0 = currBrightness - 0.5*dx;
	x1 = currBrightness + 0.5*dx;
    }
}

void myTransferBox::findExtremes(double x0, double y0, double x1, double y1,
				 double *startX, double *startY,
				 double *endX, double *endY)
{
    // the values that will end up in the input widgets
    double    swapd;

    // see if the contrast is positive or negative:
    if (x0 > x1 || y0 > y1) { // negative
	swapd = x0;
	x0 = x1;
	x1 = swapd;
	if (y0 > 1) {
	    *startX = x0;
	    *startY = 1.0;
	} else {
	    *startX = 0.0;
	    *startY = y0;
	}
	if (y1 > 0) {
	    *endX = 1.0;
	    *endY = y1;
	} else {
	    *endX = x1;
	    *endY = 0.0;
	}
    } else { // positive
    	if (y0 < 0)  {
	    *startX = x0;
	    *startY = 0.0;
	} else {
	    *startX = 0.0;
	    *startY = y0;
	}
	if (y1 <= 1) {
	    *endX = 1.0;
	    *endY = y1;
	} else {
	    *endX = x1;
	    *endY = 1.0;
	}
    }

    return;	   
}

void myTransferBox::buildFunction(double x0, double y0, double x1, double y1)
{
    // the values that will end up in the input widgets
    int       startX, startY, endX, endY;
    double    swapd;
    const int mininval = 0, maxinval = 255;
    const int minoutval = 0, maxoutval = 255;
    int       i;

    // see if the contrast is positive or negative:
    if (x0 > x1 || y0 > y1) { // negative
	swapd = x0;
	x0 = x1;
	x1 = swapd;
	if (y0 > 1) {
	    startX = (int)(x0*maxinval);
	    startY = maxoutval;
	} else {
	    startX = mininval;
	    startY = (int)(y0*maxoutval);
	}
	if (y1 > 0) {
	    endX = maxinval;
	    endY = (int)(y1*maxoutval);
	} else {
	    endX = (int)(x1*maxinval);
	    endY = 0;
	}
	for (i = mininval ; i < startX; i++)
	    map[i] = maxoutval;

	int h0 = endY - startY;
	int w0 = endX - startX;
	for (i = startX ; i < endX ; i++) {
	    map[i] = (unsigned char)(startY + h0*pow(((double)i-startX)/w0,1/currGamma));
	}

	for (i = endX ; i <= maxinval ; i++) {
	    map[i] = minoutval;
	}
    } else { // positive
    	if (y0 < 0)  {
	    startX = (int)(x0*maxinval);
	    startY = 0;
	} else {
	    startX = mininval;
	    startY = (int)(y0*maxoutval);
	}
	if (y1 <= 1) {
	    endX = maxinval;
	    endY = (int)(y1*maxoutval);
	} else {
	    endX = (int)(x1*maxinval);
	    endY = maxoutval;
	}
	for (i = mininval ; i < startX; i++)
	    map[i] = minoutval;

	int h0 = endY - startY;
	int w0 = endX - startX;
	for (i = startX ; i < endX ; i++) {
	    map[i] = (unsigned char)(startY + h0*pow(((double)i-startX)/w0,1/currGamma));
	}

	for (i = endX ; i <= maxinval ; i++) {
	    map[i] = maxoutval;
	}
    }

    
    if (hasReleaseOccured) {
	causeMainWindowRedraw();
	hasReleaseOccured = false;
    }

    if (transferEditorPanel) {
	transferEditorPanel->setTopValue(endX);
	transferEditorPanel->setBottomValue(startX);
	transferEditorPanel->setLeftValue(startY);
	transferEditorPanel->setRightValue(endY);
    }

    return;	     
}

// this is a very slow call, so we don't want to do it
// all the time...
void myTransferBox::causeMainWindowRedraw()
{
    // this might be very slow...
    dbgprintf("Really redrawing the image\n");
    
    // a million parameters there...
    IOBlackBox->saveGammaParameters(leftX, leftY, rightX, rightY, gammas,
				    map, 0, 0);
    
     // get the current image
    IMAGEPARM *p  = (IMAGEPARM *)mainMenuBar->getSelectedItemArgument(IMAGE_LIST_ID);

    if ((p != 0) && (transferEditorPanel != 0)) {
	// save the gamma, etc
	dbgprintf("Attempting to save the transfer functions parameters\n");
	transferEditorPanel->getParameters(p->gamma, p->contrast, p->brightness);
	dbgprintf("%s got: gamma=%g, contrast=%g, brightness=%g\n",
		  p->itempath,
		  p->gamma, p->contrast, p->brightness);
    }
    // this re-reads all the parameters, etc.
    IOBlackBox->applyImageParameters(p, 0, 0, true);
    mainViewer->displayCurrentImage(); // this can be quite slow as well...
    trueRedrawHook();

    // and don't forget myself...
    redraw();
}

void myTransferBox::setParms(float c, float b, float g)
{
    double x0, y0, x1, y1;
    double startX, startY, endX, endY;

    
    currContrast = (c < -1.0) ? -1.0:c;
    currBrightness = b;
    currGamma = g;
    
    computeEndPoints(x0, y0, x1, y1);

    findExtremes(x0, y0, x1, y1,
		 &startX, &startY,
		 &endX, &endY);
    
    // This is temporary until we find the time of implementing
    // separate transfer function controls for R, G and B
    gammas[0] = gammas[1] = gammas[2] = g;
    leftX[0]  = leftX[1]  = leftX[2]  = startX;
    leftY[0]  = leftY[1]  = leftY[2]  = startY;
    rightX[0] = rightX[1] = rightX[2] = endX;
    rightY[0] = rightY[1] = rightY[2] = endY;

    // this will update the image if need be,
    // and will put the right numbers in the input widget fields
    buildFunction(x0, y0, x1, y1);
    
    redraw();

    return;
}


