/**************************************************************************/
/* svgafft --  Spectrum Analyzer                                          */
/*                                                                        */ 
/* svga 3d display class implementation                                   */
/**************************************************************************/

/**************************************************************************

    Copyright (C) 1995 Andrew Veliath

    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.


***************************************************************************/

// $Id: s_3d_display.cc,v 0.4 1995/05/01 22:04:52 drewvel Exp $             

/*******************************************************************/
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <vga.h>
#include <vgagl.h>
#include "s_3d_display.h"
#include "analyzer.h"
#include "gconfig.h"
#include "signal.h"
#include "barconfig.h"
#include "colors.h"
#include "keymap.h"


// SVGA_Display_2D derived class (from SVGA_Display)
// this will eventually be a two-dimensional display for the 
// coefficient data
// THIS IS INCOMPLETE
/*******************************************************************/
SVGA_Display_2D::SVGA_Display_2D(int n, Signal& S, 
		     aScreen &screen, boolean st)
: SVGA_Display(n, S, screen, st)
{
  numsteps=gConfig.FadeSteps.val;
  numcols=4;
}

/*******************************************************************/
void SVGA_Display_2D::Show()
{
  InitScreen();
  InitData();
  Reset();
}


/*******************************************************************/
void SVGA_Display_2D::InitData()
{
  FadeCols=new int[numsteps];
  Color *Colors=new Color[numcols];

  Colors[0].Set(0, 0, 0);
  Colors[1].Set(0, 128, 255);
  Colors[2].Set(255, 128, 0);
  Colors[3].Set(255, 255, 120);

  FadeColorRange(Colors, numcols, FadeCols, numsteps);
}

  
/*******************************************************************/
void SVGA_Display_2D::UpdateCoefficients()
#define LinearBarBase(barnum) ((int) ((float) (barnum)*interval)+lownum)    
{
  static int w=Width();
  static int h=Height();
  static int cur=0;
  static int sz=s->GetParms().sampbufsize;
  static int rms_val;

  sz-=16;
  register int i;
  for (i=0; i<h && i<sz; i++) {
    rms_val=(int) (rrms(&coefficients[i], 16)/(float) controls.Attenuation.val);
    if (rms_val>numsteps) rms_val=numsteps-1;
    else if (rms_val<0) rms_val=0;
    gl_setpixel(theScreen.x1+cur, theScreen.y1+i, 
		FadeCols[rms_val]);
  }

  cur=(cur+1) % w;
  sz+=16;
}
#undef LinearBarBase
/*******************************************************************/


// Derived 3D display class with misc. 3D functions (not comprehensive though ;-)
/*******************************************************************/
SVGA_Display_3D::SVGA_Display_3D(int n, Signal& S, 
		     aScreen &screen, boolean st)
: SVGA_Display(n, S, screen, st)
{

  // user distance
  user_dist=gConfig.Distance.val;
  if (!gConfig.NoPerspective.val) 
    perspective=true;
  else 
    perspective=false;

  SetTransformConfig(gConfig);

}


/*******************************************************************/
void SVGA_Display_3D::SetTransformConfig(globalConfig& config)
{
  RotX=config.RotX.val;
  RotY=config.RotY.val;
  RotZ=config.RotZ.val;
  TransX=config.TransX.val;
  TransY=config.TransY.val;
  TransZ=config.TransZ.val;
}


/*******************************************************************/
void SVGA_Display_3D::GetTransformConfig(globalConfig& config)
{
  config.RotX.val=RotX;
  config.RotY.val=RotY;
  config.RotZ.val=RotZ;
  config.TransX.val=TransX;
  config.TransY.val=TransY;
  config.TransZ.val=TransZ;
}


/*******************************************************************/
void SVGA_Display_3D::ProcessControl(char control)
{
  extern char *gSVGADisplay3DHelpText;
  const float sizeInc=SIZEINC;

  // inherit parent controls
  SVGA_Display::ProcessControl(control);

  switch(control) {
  case K_HELP:
    ClrScr();
    cerr << gProgInfo << endl;
    cerr << gSVGADisplay3DHelpText << endl;
    KeyCont();
    break;

  case K_SD3D_ROTX_DEC:
    RotX-=sizeInc;
    ClearGraph();
    break;

  case K_SD3D_ROTX_INC:
    RotX+=sizeInc;
    ClearGraph();
    break;
    
  case K_SD3D_ROTX_RESET:
    RotX=gConfig.RotX.val;
    ClearGraph();
    break;

  case K_SD3D_ROTY_DEC:
    RotY-=sizeInc;
    ClearGraph();
    break;

  case K_SD3D_ROTY_INC:
    RotY+=sizeInc;
    ClearGraph();
    break;
    
  case K_SD3D_ROTY_RESET:
    RotY=gConfig.RotY.val;
    ClearGraph();
    break;

  case K_SD3D_ROTZ_DEC:
    RotZ-=sizeInc;
    ClearGraph();
    break;

  case K_SD3D_ROTZ_INC:
    RotZ+=sizeInc;
    ClearGraph();
    break;

  case K_SD3D_ROTZ_RESET:
    RotZ=gConfig.RotZ.val;
    ClearGraph();
    break;

  case K_SD3D_TOGGLEPERSPECTIVE:
    perspective=!perspective;
    ClearGraph();
    break;

  case K_REVERTDEFAULTS:
    SetTransformConfig(gConfig);
    ClearGraph();
    break; 

  case K_SAVEOPTIONS:
    GetTransformConfig(gPassConfig);
    break;
  }
}


/*******************************************************************/
void SVGA_Display_3D::InitRange(int SizeScale)
{
  Display::InitRange(SizeScale);
  UpdateFreqIndex();
}


/*******************************************************************/
inline void SVGA_Display_3D::RotatePoint3D_X(Point3D& p, float theta)
{
  float Y, Z;
  
  Y=p.y*cos(theta)+p.z*sin(theta);
  Z=-p.y*sin(theta)+p.z*cos(theta);

  p.y=Y; p.z=Z;
  
}


/*******************************************************************/
inline void SVGA_Display_3D::RotatePoint3D_Y(Point3D& p, float theta)
{
  float X, Z;

  X=p.x*cos(theta)-p.z*sin(theta);
  Z=p.x*sin(theta)+p.z*cos(theta);

  p.x=X; p.z=Z;
}


/*******************************************************************/
inline void SVGA_Display_3D::RotatePoint3D_Z(Point3D& p, float theta)
{
  float X, Y;
  
  X=p.x*cos(theta)+p.y*sin(theta);
  Y=-p.x*sin(theta)+p.y*cos(theta);

  p.x=X; p.y=Y;
}


/*******************************************************************/
inline void SVGA_Display_3D::ScalePoint3D(Point3D& p,
					  int& xn, int& yn)
{
  if (perspective) {
    // perspective scaling
    xn=(int) (p.x/(p.z+user_dist)*user_dist);
    yn=(int) (p.y/(p.z+user_dist)*user_dist);
  }
  else {
    xn=(int) p.x; 
    yn=(int) p.y;
  }
}
  

/*******************************************************************/
inline void SVGA_Display_3D::DrawPoint3D(Point3D& p, int color)
{
  int x, y;
  ScalePoint3D(p, x, y);
  gl_setpixel(x, y, color);
}


/*******************************************************************/
inline void SVGA_Display_3D::DrawLine3D(Point3D& p1, Point3D& p2, 
					int color)
{
  int x1, y1, x2, y2;
  ScalePoint3D(p1, x1, y1);
  ScalePoint3D(p2, x2, y2);
  gl_line(x2, y2, x1, y1, color);
}


/*******************************************************************/
inline void SVGA_Display_3D::DrawLine3D(Point3D& p,
					int color)
{
  static int xo=-1, yo=-1, xn, yn;

  ScalePoint3D(p, xn, yn);

  if (xo>0)
    gl_line(xo, yo, xn, yn, color);

  xo=xn; yo=yn;
}



// SVGA_Waveform_Display_3D Derived Class (From SVGA_Display_3D)
// this class is one of which will display the 
// fourier coefficients in a three-dimensional format
// needs lots of horsepower
/*******************************************************************/
SVGA_Waveform_Display_3D::SVGA_Waveform_Display_3D(int n, Signal& S, 
						   aScreen &screen, 
						   boolean st)
: SVGA_Display_3D(n, S, screen, st)
{
  size=s->GetParms().sampbufsize/sizescale;
  
  savex=new short[size];
  savey=new short[size];
  savecol=new unsigned char[size];

  xinterval=gConfig.XInterval.val;
  yinterval=gConfig.YInterval.val;
  iinterval=yinterval/2;
  if (iinterval<1) iinterval=1;

  cur=0;

  FadeCols=NULL;
}


/*******************************************************************/
SVGA_Waveform_Display_3D::~SVGA_Waveform_Display_3D() 
{
  if (savex) delete [] savex;
  if (savey) delete [] savey;
  if (savecol) delete [] savecol;
  if (FadeCols) delete [] FadeCols;
}


/*******************************************************************/
void SVGA_Waveform_Display_3D::ProcessControl(char control)
{
  extern char *gSVGAWaveformDisplay3DHelpText;

  // inherit parent controls
  SVGA_Display_3D::ProcessControl(control);    

  switch(control) {
    
  case K_HELP:
    ClrScr();
    cerr << gProgInfo << endl;
    cerr << gSVGAWaveformDisplay3DHelpText << endl;
    KeyCont();
    break;

  case K_SWD3D_XINT_INC:
    xinterval++;
    break;
    
  case K_SWD3D_XINT_DEC:
    xinterval--;
    break;

  case K_SWD3D_YINT_INC:
    yinterval++;
    break;

  case K_SWD3D_YINT_DEC:
    yinterval--;
    break;

  case K_REVERTDEFAULTS:
    xinterval=gConfig.XInterval.val;
    yinterval=gConfig.YInterval.val;
    break;

  case K_SAVEOPTIONS:
    gPassConfig.XInterval.val=xinterval;
    gPassConfig.YInterval.val=yinterval;
    break;
  }
}


/*******************************************************************/
void SVGA_Waveform_Display_3D::Show()
{
  InitScreen();
  InitData();
  Reset();
  DrawInit();
}


/*******************************************************************/
void SVGA_Waveform_Display_3D::DrawInit()
{
  DisableClipping();

  ClearGraph();

  if (!gConfig.FullScreen.val) {
    int x1, y1, x2, y2;

    x1=theScreen.x1-20; y1=theScreen.y1+2;
    x2=x1+11; y2=theScreen.y2-theScreen.y1+3;

    FadeBox(x1, y1,
	    x2, y2-18,
	    Color(0,0,0), Color(255,255,250), 
	    (x2-x1)/2,
	    true, 0);

    ShowColorsV(FadeCols, numsteps, 
		x1+1, y1+1, 
		x2-x1-1, y2-y1-4, 
		true);
  }
  
  EnableClipping();  
}


/*******************************************************************/
void SVGA_Waveform_Display_3D::InitData()
{
  numsteps=128;
  numcols=8;
  
  if (!FadeCols) 
    FadeCols=new unsigned char[numsteps];

  DOUT("mapping " << numsteps << " colors for grid");

  Color *Colors=new Color[numcols];
  
  Colors[0].Set(64, 64, 80);
  Colors[1].Set(64, 128, 255);
  Colors[2].Set(128, 160, 255);
  Colors[3].Set(0, 255, 255);
  Colors[4].Set(0, 255, 0);
  Colors[5].Set(255, 255, 0);
  Colors[6].Set(255, 128, 0);
  Colors[7].Set(255, 0, 0);
  
  FadeColorRange(Colors, 3, FadeCols, 64);
  FadeColorRange(Colors+3, numcols-3, FadeCols+64, numsteps-64);

  delete [] Colors;

  DOUT("color mapping done");

}


/*******************************************************************/
void SVGA_Waveform_Display_3D::Draw3DAxes()
{
  Point3D origin(0,0,0), p[3];
  Point3D translate(TransX, TransY, TransZ);
  int i, ox, oy, x1, y1;

  p[0].set(theScreen.x1-Width()/2-10, Height()/2, theScreen.y2+yinterval*15); 
  p[1].set(theScreen.x1+Width()/2, Height()/2, theScreen.y2+size*yinterval+50);  
  p[2].set(theScreen.x1-Width()/2-10, -Height()/2, theScreen.y2+size*yinterval);  
  
  origin.set(theScreen.x1-Width()/2, Height()/2, 
	     theScreen.y2+size*yinterval);  
  RotatePoint3D_X(origin, RotX);
  RotatePoint3D_Y(origin, RotY);
  RotatePoint3D_Z(origin, RotZ);
  origin=origin+translate;
  ScalePoint3D(origin, ox, oy);
  ox+=Width()/2; oy+=Height()/2;

  for (i=0; i<3; i++) {
    RotatePoint3D_X(p[i], RotX);
    RotatePoint3D_Y(p[i], RotY);
    RotatePoint3D_Z(p[i], RotZ);
    p[i]=p[i]+translate;
    
    ScalePoint3D(p[i], x1, y1);
    x1+=Width()/2; y1+=Height()/2;
    
    gl_line(ox, oy, x1, y1, gConfig.AxesColor.val);    
  }    
}


/*******************************************************************/
void SVGA_Waveform_Display_3D::ClearGraph()
{
  SVGA_Display::ClearGraph();
  Draw3DAxes();
  cur=0;
}


/*******************************************************************/
void SVGA_Waveform_Display_3D::UpdateCoefficients()
#define LinearBarBase(barnum) ((int) ((float) (barnum)*interval)+lownum)    
{
  static int w2=Width()/2;
  static int h2=Height()/2;
  static int h=Height();
  static int rms_val;
  static int x1, y1, x2, y2;
  static Point3D p, q;
  
  if (cur>w2) {
    ClearGraph();
  }

  register int i;
  static unsigned short l;

  i=size;
  while (i>yinterval) {

    l=size-i;

    if (controls.LogYScale.isOn) 
      rms_val=(int) 
	(log10(rrms(&coefficients[index[l]], i_length[l])+1)*
	 (float) logatten/(float) controls.Attenuation.val);
    else      
      rms_val=(int) rrms(&coefficients[index[l]], i_length[l])/controls.Attenuation.val;

    rms_val+=controls.VPosition.val;

    if (rms_val>h) rms_val=h;
    if (rms_val<0) rms_val=0;
    
    p.set(theScreen.x1+cur*2-w2, 
	  h-rms_val-h2, 
	  theScreen.y2+i*yinterval);
    
    RotatePoint3D_X(p, RotX);
    RotatePoint3D_Y(p, RotY);
    RotatePoint3D_Z(p, RotZ);

    static Point3D translate(TransX, TransY, TransZ);
    p=p+translate;

    ScalePoint3D(p, x1, y1);
    ScalePoint3D(q, x2, y2);

    x1+=w2; y1+=h2; 
    x2+=w2; y2+=h2; 

    if (i<size) {
      static int c=0, d;
      d=rms_val/2;
      c=FadeCols[d<numsteps?d:numsteps-1];

      if (cur!=0) 
	gl_line(savex[i], savey[i], x1, y1, ((short) c+(short) savecol[i])/2);
      
      savex[i]=x1;
      savey[i]=y1;      	  
      savecol[i]=c;
      
      gl_line(x2, y2, x1, y1, c);
    }    

    // save last point
    q=p;

    i-=iinterval;
  }
  cur+=xinterval;
}
#undef LinearBarBase



/*******************************************************************/
SVGA_Continuous_Waveform_Display_3D::
SVGA_Continuous_Waveform_Display_3D(int n, Signal& S, 
				    aScreen &screen, boolean st)
:SVGA_Waveform_Display_3D(n, S, screen, st) 
{
  clipping_enabled=false;
  perspective=false;
  gConfig.NoText.val=true;
}


/*******************************************************************/
SVGA_Continuous_Waveform_Display_3D::~SVGA_Continuous_Waveform_Display_3D()
{
}


/*******************************************************************/
void SVGA_Continuous_Waveform_Display_3D::Show()
{
  InitScreen();
  InitData();
  Reset();
  gl_clearscreen(gConfig.GraphBG.val);
}


/*******************************************************************/
void SVGA_Continuous_Waveform_Display_3D::ProcessControl(char control)
{
  // inherit parent controls
  SVGA_Waveform_Display_3D::ProcessControl(control);    

  switch(control) {
  }
}


/*******************************************************************/
void SVGA_Continuous_Waveform_Display_3D::
UpdateCoefficients()
{
  static int w2=Width()/2;
  static int w=Width();
  static int h2=Height()/2;
  static int h=Height();
  static int rms_val;
  static int x1, y1, x2, y2;
  static Point3D p, q;
  static int offset=16;
  static int filloffset=16;


  if (cur>w+xinterval) {
    cur=0;
  }

  gl_fillbox((cur+filloffset)%theScreen.w, 0, xinterval, h, 0);

  register int i;

  i=size;
  while (i>yinterval) {

#ifdef UNDEF
    if (gConfig.LogScale.val)
      rms_val=(int) 
	(log10(rrms(&coefficients[size-i], yinterval)+1)*
	 (float) logatten/(float) atten);
    else      
      rms_val=(int) rrms(&coefficients[size-i], yinterval)/atten;
#else
    if (controls.LogYScale.isOn)
      rms_val=(int) 
	(log10(rrms(&coefficients[index[size-i]], yinterval)+1)*
	 (float) logatten/(float) controls.Attenuation.val);
    else      
      rms_val=(int) rrms(&coefficients[index[size-i]], yinterval)/controls.Attenuation.val;
#endif

    rms_val+=controls.VPosition.val;

    if (rms_val>h) rms_val=h;
    if (rms_val<0) rms_val=0;
    
    p.set(theScreen.x1+cur-w2, 
	  h-rms_val-h2, 
	  theScreen.y2+i*yinterval);
    
    RotatePoint3D_X(p, RotX);
    RotatePoint3D_Y(p, RotY);

    static Point3D translate(TransX, TransY, TransZ);
    p=p+translate;

    ScalePoint3D(p, x1, y1);
    ScalePoint3D(q, x2, y2);

    x1+=w2; y1+=h2; 
    x2+=w2; y2+=h2; 

    if (i<size) {
      static int c=0, d;
      d=rms_val/2;
      c=FadeCols[d<numsteps?d:numsteps-1];

      if (cur!=0) 
	gl_line(savex[i], savey[i], x1, y1, ((short) c+(short) savecol[i])/2);
      
      savex[i]=x1;
      savey[i]=y1;      	  
      savecol[i]=c;
      
      gl_line(x2, y2, x1, y1, c);
      gl_setdisplaystart((cur+offset)%theScreen.w, 1);
    }    

    // save last point
    q=p;

    i-=iinterval;
  }
  cur+=xinterval;
}
#undef LinearBarBase


/*******************************************************************/
