
/* 
A* -------------------------------------------------------------------
B* This file contains source code for the PyMOL computer program
C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. 
D* -------------------------------------------------------------------
E* It is unlawful to modify or remove this copyright notice.
F* -------------------------------------------------------------------
G* Please see the accompanying LICENSE file for further information. 
H* -------------------------------------------------------------------
I* Additional authors of this source file include:
-* 
-* 
-*
Z* -------------------------------------------------------------------
*/
#include"os_python.h"

#include"os_predef.h"
#include"os_std.h"
#include"os_gl.h"

#include"OOMac.h"
#include"ObjectGadgetRamp.h"
#include"GadgetSet.h"
#include"Base.h"
#include"MemoryDebug.h"
#include"CGO.h"
#include"Scene.h"
#include"Setting.h"
#include"PConv.h"
#include"main.h"
#include"Color.h"
#include"VFont.h"
#include"ObjectMolecule.h"
#include"Executive.h"
#include"Util.h"

#include"P.h"

void ObjectGadgetRampFree(ObjectGadgetRamp * I)
{
  ColorForgetExt(I->Gadget.Obj.G, I->Gadget.Obj.Name);
  VLAFreeP(I->Level);
  VLAFreeP(I->Color);
  VLAFreeP(I->Special);
  VLAFreeP(I->Extreme);
  ObjectGadgetPurge(&I->Gadget);
  OOFreeP(I);
}

#define ShapeVertex(cgo,a,b) CGOVertex(cgo,(float)a,(float)b,0.0F)
#define ShapeFVertex(cgo,a,b) CGOFontVertex(cgo,(float)a,(float)b,0.0F)
#define ABS 0.0F
#define REL 1.0F
#define OFF 2.0F

#define ShapeNormal(cgo,a,b) CGONormal(cgo,(float)a,(float)b,0.0F)
#define ShapeColor(cgo,a,b) CGONormal(cgo,(float)a,(float)b,0.0F)
#define LKP 2.0F

#ifdef _PYMOL_INLINE
__inline__
#endif
static void ObjectGadgetRampCalculate(ObjectGadgetRamp * I, float v, float *result)
{
  int i;
  const float _1 = 1.0F;
  const float _0 = 0.0F;
  /* from Filipe Maia */

  /* All of this functions are taken right of the gnuplot manual  */
  if(v > _1)
    v = _1;
  else if(v < _0)
    v = _0;

  switch (I->CalcMode) {
  case cRAMP_TRADITIONAL:
    result[0] = (float) sqrt(v);
    result[1] = v * v * v;
    result[2] = (float) sin(v * 2 * cPI);
    break;
  case cRAMP_SLUDGE:
    result[0] = v;
    result[1] = (float) fabs(v - 0.5F);
    result[2] = v * v * v * v;
    break;
  case cRAMP_OCEAN:
    result[0] = 3 * v - 2;
    result[1] = (float) fabs((3 * v - 1) / 2);
    result[2] = v;
    break;
  case cRAMP_HOT:
    result[0] = 3 * v;
    result[1] = 3 * v - 1;
    result[2] = 3 * v - 2;
    break;
  case cRAMP_GRAYABLE:
    result[0] = v / 0.32F - 0.78125F;
    result[1] = 2 * v - 0.84F;
    result[2] = v / 0.08F - 11.5F;      /* I'm not so sure about this one */
    break;
  case cRAMP_RAINBOW:
    result[0] = (float) fabs(2 * v - 0.5F);
    result[1] = (float) sin(v * cPI);
    result[2] = (float) cos(v * cPI / 2.0F);
    break;
  case cRAMP_AFMHOT:
    result[0] = 2 * v;
    result[1] = 2 * v - 0.5F;
    result[2] = 2 * v - 1.0F;
    break;
  case cRAMP_GRAYSCALE:
    result[0] = v;
    result[1] = v;
    result[2] = v;
    break;
  default:                     /* default is simply white */
    result[0] = 1.0F;
    result[1] = 1.0F;
    result[2] = 1.0F;
    break;
  }
  for(i = 0; i < 3; i++) {
    if(result[i] > 1.0F) {
      result[i] = 1.0F;
    } else if(result[i] < 0.0F) {
      result[i] = 0.0F;
    }
  }
}

#ifdef _PYMOL_INLINE
__inline__
#endif
static int _ObjectGadgetRampInterpolate(ObjectGadgetRamp * I, float level, float *color,
                                        float *table, float *extreme)
{
  register float *i_level = I->Level;
  register int n_level = I->NLevel;
  const float _0 = 0.0F;
  const float _1 = 1.0F;
  int ok = true;
  if(i_level && table) {
    register int level_is_ge = -1;
    register int level_is_le = n_level;
    register int i = 0;
    i = n_level - 1;
    while(i >= 0) {
      register float f = i_level[i];
      if(level >= f) {
        level_is_ge = i;
        break;
      }
      i--;
    }
    i = 0;
    while(i < n_level) {
      register float f = i_level[i];
      if(level <= f) {
        level_is_le = i;
        break;
      } else
        i++;
    }
#if 0
    printf("%9.2f %2i %9.2f %2i %9.2f\n", level, level_is_ge, ((level_is_ge >= 0)
                                                               && (level_is_ge <
                                                                   n_level)) ?
           i_level[level_is_ge] : 1000.0F, level_is_le, ((level_is_le >= 0)
                                                         && (level_is_le <
                                                             n_level)) ?
           i_level[level_is_le] : -1000.0F);
#endif

    if(level_is_ge != level_is_le) {
      if(level_is_le == 0) {    /* lower extreme */
        register float *v;
        if(extreme) {
          v = extreme;
        } else {
          v = table;
        }
        copy3f(v, color);
      } else if(level_is_ge == (n_level - 1)) { /* upper extreme */
        register float *v;
        if(extreme) {
          v = extreme + 3;
        } else {
          v = table + 3 * (n_level - 1);
        }
        copy3f(v, color);
      } else {
        register float d, x0, x1;

        d = i_level[level_is_ge] - i_level[level_is_le];
        if(fabs(d) > R_SMALL8) {
          x0 = (level - i_level[level_is_le]) / d;
          x1 = 1.0F - x0;
          for(i = 0; i < 3; i++) {
            color[i] = x0 * table[3 * level_is_ge + i] + x1 * table[3 * level_is_le + i];
          }
          clamp3f(color);
        } else {
          register float *v = table + 3 * level_is_ge;
          copy3f(v, color);
        }
      }
    } else {                    /* dead on the specified level */
      copy3f(table + 3 * level_is_ge, color);
      clamp3f(color);
    }
  } else {
    float base, range;
    if(n_level && i_level) {
      base = i_level[0];
      range = i_level[n_level - 1] - base;
      if(fabs(range) < R_SMALL8)
        range = _1;
    } else {
      base = _0;
      range = _1;
    }
    level = (level - base) / range;
    ObjectGadgetRampCalculate(I, level, color);
  }
  return (ok);
}

#ifdef _PYMOL_INLINE
__inline__
#endif
static int _ObjectGadgetRampBlend(ObjectGadgetRamp * I, float *color,
                                  float *table, float *extreme, int mode)
{
  /* this capability needs to be re-thought */

  register float *i_level = I->Level;
  register int n_level = I->NLevel;
  const float _1 = 1.0F;
  float avg[3];
  int ok = true;
  zero3f(avg);

  {
    int cnt = 0;
    switch (mode) {
    case 1:
    case 2:
      break;
    default:
      if(i_level && table) {
        int i;
        for(i = 0; i < n_level; i++) {
          add3f(table + 3 * i, avg, avg);
          cnt++;
        }
        if(extreme) {
          add3f(extreme, avg, avg);
          add3f(extreme + 3, avg, avg);
          cnt += 2;
        }
        if(cnt) {
          float fact = _1 / cnt;
          scale3f(avg, fact, avg);
        }
        clamp3f(avg);
      }
      copy3f(avg, color);
    }
  }

  switch (mode) {
  case 1:                      /* min components */
  case 3:
    ones3f(color);
    if(i_level && table) {
      int i, j;
      for(i = 0; i < n_level; i++) {
        for(j = 0; j < 3; j++) {
          color[j] = (color[j] < table[3 * i + j]) ? color[j] : table[3 * i + j];
        }
      }
      if(extreme) {
        for(j = 0; j < 3; j++) {
          color[j] = (color[j] < extreme[j]) ? color[j] : extreme[j];
          color[j] = (color[j] < extreme[j + 3]) ? color[j] : extreme[j + 3];
        }
      }
      clamp3f(color);
    }
    if(mode == 3) {             /* average serves as a minimum */
      int j;
      for(j = 0; j < 3; j++) {
        color[j] = (color[j] > avg[j]) ? color[j] : avg[j];
      }
    }
    break;
  case 2:                      /* max components */
    zero3f(color);
    if(i_level && table) {
      int i, j;
      for(i = 0; i < n_level; i++) {
        for(j = 0; j < 3; j++) {
          color[j] = (color[j] > table[3 * i + j]) ? color[j] : table[3 * i + j];
        }
      }
      if(extreme) {
        for(j = 0; j < 3; j++) {
          color[j] = (color[j] > extreme[j]) ? color[j] : extreme[j];
          color[j] = (color[j] > extreme[j + 3]) ? color[j] : extreme[j + 3];
        }
      }
      clamp3f(color);
    }
    break;
  default:                     /* simple average of all colors */
    copy3f(avg, color);
    break;
  }
  return (ok);
}

#define MAX_COLORS 64

#ifdef _PYMOL_INLINE
__inline__
#endif
static int ObjectGadgetRampInterpolateWithSpecial(ObjectGadgetRamp * I,
                                                  float level,
                                                  float *color,
                                                  float *atomic,
                                                  float *object,
                                                  float *vertex, int state, int blend_all)
{
  /* now thread-safe...via stack copy of colors */

  float stack_color[MAX_COLORS * 3];
  float stack_extreme[6];
  register float *i_level = I->Level;
  register float *i_color = I->Color;
  register int *i_special = I->Special;
  if(i_level && i_color && i_special) {
    register int i = 0;
    register int n_level = I->NLevel;
    register float *i_extreme = I->Extreme;
    register float *extreme = i_extreme;
    /* mix special coloring into the table */
    register float *src = i_color, *dst = stack_color;
    if((n_level + 2) > MAX_COLORS)
      n_level = MAX_COLORS - 2;
    while(i < n_level) {
      register int index = i_special[i];
      switch (index) {
      case 0:
        *(dst++) = *(src++);
        *(dst++) = *(src++);
        *(dst++) = *(src++);
        break;
      case cColorDefault:
      case cColorAtomic:
        copy3f(atomic, dst);
        src += 3;
        dst += 3;
        break;
      case cColorObject:
        copy3f(object, dst);
        dst += 3;
        src += 3;
        break;
      default:                 /* allow nested ramps */
        if(ColorCheckRamped(I->Gadget.Obj.G, index)) {
          ColorGetRamped(I->Gadget.Obj.G, index, vertex, dst, state);
        } else {
          copy3f(src, dst);
        }
        dst += 3;
        src += 3;
        break;
      }
      i++;
    }
    /* mix extreme colors in (if present) */
    if(i_extreme) {
      i = 0;
      extreme = stack_extreme;
      src = i_extreme;
      dst = stack_extreme;
      while(i < 2) {
        register int index = i_special[i + n_level];
        switch (index) {
        case 0:
          *(dst++) = *(src++);
          *(dst++) = *(src++);
          *(dst++) = *(src++);
          break;
        case cColorDefault:
        case cColorAtomic:
          copy3f(atomic, dst);
          dst += 3;
          src += 3;
          break;
        case cColorObject:
          copy3f(object, dst);
          dst += 3;
          src += 3;
          break;
        default:               /* allow nested ramps */
          if(ColorCheckRamped(I->Gadget.Obj.G, index)) {
            ColorGetRamped(I->Gadget.Obj.G, index, vertex, dst, state);
          } else {
            copy3f(src, dst);
          }
          dst += 3;
          src += 3;
          break;
        }
        i++;
      }
    }
    /* interpolate using stack tables */
    if(blend_all)
      return _ObjectGadgetRampBlend(I, color, stack_color, extreme, I->SrcState);
    else
      return _ObjectGadgetRampInterpolate(I, level, color, stack_color, extreme);
  } else {
    /* interpolate using static tables */
    if(blend_all)
      return _ObjectGadgetRampBlend(I, color, i_color, I->Extreme, I->SrcState);
    else
      return _ObjectGadgetRampInterpolate(I, level, color, i_color, I->Extreme);
  }
}

int ObjectGadgetRampInterpolate(ObjectGadgetRamp * I, float level, float *color)
{
  int result = _ObjectGadgetRampInterpolate(I, level, color, I->Color, I->Extreme);
  return result;
}

PyObject *ObjectGadgetRampAsPyList(ObjectGadgetRamp * I)
{
#ifdef _PYMOL_NOPY
  return NULL;
#else

  PyObject *result = NULL;

  result = PyList_New(11);

  PyList_SetItem(result, 0, ObjectGadgetPlainAsPyList(&I->Gadget));
  PyList_SetItem(result, 1, PyInt_FromLong(I->RampType));
  PyList_SetItem(result, 2, PyInt_FromLong(I->NLevel));
  if(I->Level && I->NLevel) {
    PyList_SetItem(result, 3, PConvFloatVLAToPyList(I->Level));
  } else {
    PyList_SetItem(result, 3, PConvAutoNone(NULL));
  }
  if(I->Color && I->NLevel) {
    PyList_SetItem(result, 4, PConvFloatVLAToPyList(I->Color));
  } else {
    PyList_SetItem(result, 4, PConvAutoNone(NULL));
  }
  PyList_SetItem(result, 5, PyInt_FromLong(I->var_index));
  PyList_SetItem(result, 6, PyString_FromString(I->SrcName));
  PyList_SetItem(result, 7, PyInt_FromLong(I->SrcState));
  PyList_SetItem(result, 8, PyInt_FromLong(I->CalcMode));
  if(I->Special && I->NLevel) {
    PyList_SetItem(result, 9, PConvIntVLAToPyList(I->Special));
  } else {
    PyList_SetItem(result, 9, PConvAutoNone(NULL));
  }
  if(I->Extreme && I->NLevel) {
    PyList_SetItem(result, 10, PConvFloatVLAToPyList(I->Extreme));
  } else {
    PyList_SetItem(result, 10, PConvAutoNone(NULL));
  }
  return (PConvAutoNone(result));
#endif
}

int ObjectGadgetRampNewFromPyList(PyMOLGlobals * G, PyObject * list,
                                  ObjectGadgetRamp ** result, int version)
{
#ifdef _PYMOL_NOPY
  return 0;
#else

  ObjectGadgetRamp *I = NULL;
  int ok = true;
  int ll = 0;

  if(ok)
    I = ObjectGadgetRampNew(G);
  if(ok)
    ok = (I != NULL);
  if(ok)
    ok = (list != NULL);
  if(ok)
    ok = PyList_Check(list);
  if(ok)
    ll = PyList_Size(list);
  /* TO SUPPORT BACKWARDS COMPATIBILITY...
     Always check ll when adding new PyList_GetItem's */

  if(ok)
    ok = ObjectGadgetInitFromPyList(G, PyList_GetItem(list, 0), &I->Gadget, version);
  if(ok)
    ok = PConvPyIntToInt(PyList_GetItem(list, 1), &I->RampType);
  if(ok)
    ok = PConvPyIntToInt(PyList_GetItem(list, 2), &I->NLevel);
  if(ok && I->NLevel)
    ok = PConvPyListToFloatVLA(PyList_GetItem(list, 3), &I->Level);
  if(ok && I->NLevel) {
    PyObject *item = PyList_GetItem(list, 4);
    if(item != Py_None) {
      ok = PConvPyListToFloatVLA(item, &I->Color);
    }
  }
  if(ok)
    ok = PConvPyIntToInt(PyList_GetItem(list, 5), &I->var_index);
  if(ok)
    ok = PConvPyStrToStr(PyList_GetItem(list, 6), I->SrcName, WordLength);
  if(ok)
    ok = PConvPyIntToInt(PyList_GetItem(list, 7), &I->SrcState);
  if(ok && (ll > 8))
    ok = PConvPyIntToInt(PyList_GetItem(list, 8), &I->CalcMode);
  if(ok && (ll > 9)) {
    PyObject *item = PyList_GetItem(list, 9);
    if(item != Py_None) {
      ok = PConvPyListToIntVLA(item, &I->Special);
    }
  } else {
    I->Special = NULL;
  }
  if(ok && I->NLevel && (ll > 10)) {
    PyObject *item = PyList_GetItem(list, 10);
    if(item != Py_None) {
      ok = PConvPyListToFloatVLA(item, &I->Extreme);
    }
  } else {
    I->Extreme = NULL;
  }
  /*  if(ok) ObjectGadgetRampBuild(I);
     if(ok) ObjectGadgetRampUpdate(I); */
  if(ok)
    ObjectGadgetUpdateStates(&I->Gadget);
  if(ok)
    ObjectGadgetUpdateExtents(&I->Gadget);
  if(ok)
    (*result) = I;
  return (ok);
#endif
}

int ObjectGadgetRampInterVertex(ObjectGadgetRamp * I, float *pos, float *color, int state)
{
  float level;
  int ok = true;
  switch (I->RampType) {
  case cRampMap:
    if(!I->Map)
      I->Map = ExecutiveFindObjectMapByName(I->Gadget.Obj.G, I->SrcName);
    if(!ExecutiveValidateObjectPtr(I->Gadget.Obj.G, (CObject *) I->Map, cObjectMap))
      ok = false;
    else {
      int src_state;
      if(I->SrcState >= 0)
        src_state = I->SrcState;
      else
        src_state = state;
      if(src_state < 0)
        src_state = SceneGetState(I->Gadget.Obj.G);
      if(ok)
        ok = (I->Map != NULL);
      if(ok)
        ok = ObjectMapInterpolate(I->Map, src_state, pos, &level, NULL, 1);
      if(ok)
        ok = ObjectGadgetRampInterpolate(I, level, color);
    }
    break;
  case cRampMol:
    if(!I->Mol)
      I->Mol = ExecutiveFindObjectMoleculeByName(I->Gadget.Obj.G, I->SrcName);
    if(!ExecutiveValidateObjectPtr(I->Gadget.Obj.G, (CObject *) I->Mol, cObjectMolecule))
      ok = false;
    else {
      float cutoff = 1.0F;
      float dist;
      int sub_vdw = false;
      if(state < 0)
        state = SceneGetState(I->Gadget.Obj.G);
      if(I->Level && I->NLevel) {
        cutoff = I->Level[I->NLevel - 1];
        if(I->Level[0] < 0.0F) {
          sub_vdw = true;
          cutoff += MAX_VDW;
        }
      }
      if(ok)
        ok = (I->Mol != NULL);
      if(ok) {
        if(SettingGet_b
           (I->Gadget.Obj.G, I->Gadget.Obj.Setting, NULL,
            cSetting_ramp_blend_nearby_colors)) {
          float atomic[3];
          int index =
            ObjectMoleculeGetNearestBlendedColor(I->Mol, pos, cutoff, state, &dist,
                                                 atomic, sub_vdw);
          if(index >= 0) {
            float *object = ColorGetRaw(I->Gadget.Obj.G, I->Mol->Obj.Color);

            if(!ObjectGadgetRampInterpolateWithSpecial(I, dist, color, atomic,
                                                       object, pos, state, false)) {
              copy3f(I->Color, color);
            }
          } else {
            float white[3] = { 1.0F, 1.0F, 1.0F };
            if(!ObjectGadgetRampInterpolateWithSpecial(I, cutoff + 1.0F, color, white,
                                                       white, pos, state, false)) {
              copy3f(I->Color, color);
            }
          }
        } else {
          int index =
            ObjectMoleculeGetNearestAtomIndex(I->Mol, pos, cutoff, state, &dist);
          if(index >= 0) {
            float *atomic = ColorGetRaw(I->Gadget.Obj.G, I->Mol->AtomInfo[index].color);
            float *object = ColorGetRaw(I->Gadget.Obj.G, I->Mol->Obj.Color);

            if(sub_vdw) {
              dist -= I->Mol->AtomInfo[index].vdw;
              if(dist < 0.0F)
                dist = 0.0F;
            }

            if(!ObjectGadgetRampInterpolateWithSpecial(I, dist, color, atomic,
                                                       object, pos, state, false)) {
              copy3f(I->Color, color);
            }
          } else {
            float white[3] = { 1.0F, 1.0F, 1.0F };
            if(!ObjectGadgetRampInterpolateWithSpecial(I, cutoff + 1.0F, color, white,
                                                       white, pos, state, false)) {
              copy3f(I->Color, color);
            }
          }
        }
      }
    }
    break;
  case cRampNone:
    {
      float white[3] = { 1.0F, 1.0F, 1.0F };
      if(!ObjectGadgetRampInterpolateWithSpecial(I, 0.0F, color, white, white, pos, state, true)) {     /* simple blend */
        copy3f(I->Color, color);
      }
    }
    break;
  default:
    ok = false;
    break;
  }
  return (ok);
}

static void ObjectGadgetRampUpdateCGO(ObjectGadgetRamp * I, GadgetSet * gs)
{
  CGO *cgo;
  int n_extra;
  int a, c = 0;
  float *p;
  char buffer[255];
  float white[3] = { 1.0F, 1.0F, 1.0F };

  cgo = CGONewSized(I->Gadget.Obj.G, 100);

  /* behind text */

#ifdef _PYMOL_CGO_DRAWARRAYS
  CGOColor(cgo, 0.05F, 0.05F, 0.05F);
  ShapeNormal(cgo, LKP, 2);
  {
    int nverts = 4, pl = 0;
    float *vertexVals;
    const float vVals[] = {
      REL, 9.f, 0.f,
      REL, 10.f, 0.f,
      REL, 7.f, 0.f,
      REL, 8.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY, nverts);      
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGOColor(cgo, 0.05F, 0.05F, 0.05F);
  ShapeNormal(cgo, LKP, 2);
  ShapeVertex(cgo, REL, 9);
  ShapeVertex(cgo, REL, 10);
  ShapeVertex(cgo, REL, 7);
  ShapeVertex(cgo, REL, 8);
  CGOEnd(cgo);
#endif

  CGOColor(cgo, 1.0F, 1.0F, 1.0F);

  //#ifndef _PYMOL_NOPY
  CGOFontScale(cgo, I->text_scale_h, I->text_scale_v);
  if(I->Level && I->NLevel) {
    /*
       for(a=0;a<I->NLevel;a++) {
       sprintf(buffer,"%0.3f",I->Level[a]);
       ShapeFVertex(cgo,REL,11+a);
       CGOWriteIndent(cgo,buffer, -(I->NLevel-a)/(float)a);
       }
     */
    sprintf(buffer, "%0.3f", I->Level[0]);
    ShapeFVertex(cgo, REL, 11);
    CGOWrite(cgo, buffer);
    sprintf(buffer, "%0.3f", I->Level[I->NLevel - 1]);
    ShapeFVertex(cgo, REL, 12);
    CGOWriteLeft(cgo, buffer);
  }
  //#endif

  /* center */
#ifdef _PYMOL_CGO_DRAWARRAYS
  ShapeNormal(cgo, LKP, 2);
  if(I->Color) {
    n_extra = 3 * I->NLevel;
    if(n_extra < 6)
      n_extra = 6;
    {
      VLACheck(gs->Coord, float, (I->var_index + n_extra) * 3);
      c = I->var_index;
      p = gs->Coord + 3 * c;
      if(I->NLevel > 1) {
	int nverts = I->NLevel*2, pl = 0, plc = 0;
	float *vertexVals, *colorVals, *tmp_ptr;
	vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY | CGO_COLOR_ARRAY, nverts);	      
	colorVals = vertexVals + (3*nverts);
        for(a = 0; a < I->NLevel; a++) {
          if(I->Special && (I->Special[a] < 0)) {
	    colorVals[plc++] = white[0]; colorVals[plc++] = white[1]; colorVals[plc++] = white[2]; colorVals[plc++] = cgo->alpha;
          } else {
            float tmp[3], *src = I->Color + 3 * a;
            copy3f(src, tmp);
            ColorLookupColor(I->Gadget.Obj.G, tmp);
	    colorVals[plc++] = tmp[0]; colorVals[plc++] = tmp[1]; colorVals[plc++] = tmp[2]; colorVals[plc++] = cgo->alpha;
          }
          *(p++) = I->border + (I->width * a) / (I->NLevel - 1);
          *(p++) = -I->border;
          *(p++) = I->border;
	  vertexVals[pl++] = REL; vertexVals[pl++] = c; vertexVals[pl++] = 0.f;
          c++;
          *(p++) = I->border + (I->width * a) / (I->NLevel - 1);
          *(p++) = -(I->border + I->bar_height);
          *(p++) = I->border;
	  tmp_ptr = &colorVals[plc-4];
	  colorVals[plc++] = tmp_ptr[0]; colorVals[plc++] = tmp_ptr[1]; colorVals[plc++] = tmp_ptr[2]; colorVals[plc++] = cgo->alpha;
	  vertexVals[pl++] = REL; vertexVals[pl++] = c; vertexVals[pl++] = 0.f;
          c++;
          *(p++) = I->border + (I->width * a) / (I->NLevel - 1);
          *(p++) = -(I->border + I->height + I->height);
          *(p++) = I->border;
          c++;
        }
      } else {
	int nverts = 4, pl = 0, plc = 0;
	float *vertexVals, *colorVals, *tmp_ptr;
	vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY | CGO_COLOR_ARRAY, nverts);	      
	colorVals = vertexVals + (3*nverts);

        for(a = 0; a < 2; a++) {
          if(I->Special && (I->Special[0] < 0)) {
	    colorVals[plc++] = white[0]; colorVals[plc++] = white[1]; colorVals[plc++] = white[2]; colorVals[plc++] = cgo->alpha;
          } else {
            float tmp[3], *src = I->Color;
            copy3f(src, tmp);
            ColorLookupColor(I->Gadget.Obj.G, tmp);
	    colorVals[plc++] = tmp[0]; colorVals[plc++] = tmp[1]; colorVals[plc++] = tmp[2]; colorVals[plc++] = cgo->alpha;
          }

          *(p++) = I->border + (I->width * a);
          *(p++) = -I->border;
          *(p++) = I->border;
	  vertexVals[pl++] = REL; vertexVals[pl++] = c; vertexVals[pl++] = 0.f;
          c++;

          *(p++) = I->border + (I->width * a);
          *(p++) = -(I->border + I->bar_height);
          *(p++) = I->border;
	  tmp_ptr = &colorVals[plc-4];
	  colorVals[plc++] = tmp_ptr[0]; colorVals[plc++] = tmp_ptr[1]; colorVals[plc++] = tmp_ptr[2]; colorVals[plc++] = cgo->alpha;
	  vertexVals[pl++] = REL; vertexVals[pl++] = c; vertexVals[pl++] = 0.f;
          c++;

          *(p++) = I->border + (I->width * a);
          *(p++) = -(I->border + I->height + I->height);
          *(p++) = I->border;
          c++;
        }
      }
    }
  } else {
    int samples = 20;
    float fxn;
    float color[3];
    int nverts = samples*2, pl = 0, plc = 0;
    float *vertexVals, *colorVals, *tmp_ptr;
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY | CGO_COLOR_ARRAY, nverts);	      
    colorVals = vertexVals + (3*nverts);
    n_extra = 3 * samples;
    VLACheck(gs->Coord, float, (I->var_index + n_extra) * 3);
    c = I->var_index;
    p = gs->Coord + 3 * c;

    for(a = 0; a < samples; a++) {
      fxn = a / (samples - 1.0F);

      ObjectGadgetRampCalculate(I, fxn, color);
      colorVals[plc++] = color[0]; colorVals[plc++] = color[1]; colorVals[plc++] = color[2]; colorVals[plc++] = cgo->alpha;

      *(p++) = I->border + (I->width * fxn);
      *(p++) = -I->border;
      *(p++) = I->border;
      vertexVals[pl++] = REL; vertexVals[pl++] = c; vertexVals[pl++] = 0.f;
      c++;

      *(p++) = I->border + (I->width * fxn);
      *(p++) = -(I->border + I->bar_height);
      *(p++) = I->border;
      tmp_ptr = &colorVals[plc-4];
      colorVals[plc++] = tmp_ptr[0]; colorVals[plc++] = tmp_ptr[1]; colorVals[plc++] = tmp_ptr[2]; colorVals[plc++] = cgo->alpha;
      vertexVals[pl++] = REL; vertexVals[pl++] = c; vertexVals[pl++] = 0.f;
      c++;

      *(p++) = I->border + (I->width * fxn);
      *(p++) = -(I->border + I->height + I->height);
      *(p++) = I->border;
      c++;
    }
  }
  gs->NCoord = c;
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeNormal(cgo, LKP, 2);
  if(I->Color) {
    n_extra = 3 * I->NLevel;
    if(n_extra < 6)
      n_extra = 6;
    {
      VLACheck(gs->Coord, float, (I->var_index + n_extra) * 3);
      c = I->var_index;
      p = gs->Coord + 3 * c;
      if(I->NLevel > 1) {
        for(a = 0; a < I->NLevel; a++) {
          if(I->Special && (I->Special[a] < 0)) {
            CGOColorv(cgo, white);
          } else {
            float tmp[3], *src = I->Color + 3 * a;
            copy3f(src, tmp);
            ColorLookupColor(I->Gadget.Obj.G, tmp);
            CGOColorv(cgo, tmp);
          }
          *(p++) = I->border + (I->width * a) / (I->NLevel - 1);
          *(p++) = -I->border;
          *(p++) = I->border;
          ShapeVertex(cgo, REL, c);
          c++;
          *(p++) = I->border + (I->width * a) / (I->NLevel - 1);
          *(p++) = -(I->border + I->bar_height);
          *(p++) = I->border;
          ShapeVertex(cgo, REL, c);
          c++;
          *(p++) = I->border + (I->width * a) / (I->NLevel - 1);
          *(p++) = -(I->border + I->height + I->height);
          *(p++) = I->border;
          c++;
        }
      } else {
        for(a = 0; a < 2; a++) {
          if(I->Special && (I->Special[0] < 0)) {
            CGOColorv(cgo, white);
          } else {
            float tmp[3], *src = I->Color;
            copy3f(src, tmp);
            ColorLookupColor(I->Gadget.Obj.G, tmp);
            CGOColorv(cgo, tmp);
          }

          *(p++) = I->border + (I->width * a);
          *(p++) = -I->border;
          *(p++) = I->border;
          ShapeVertex(cgo, REL, c);
          c++;

          *(p++) = I->border + (I->width * a);
          *(p++) = -(I->border + I->bar_height);
          *(p++) = I->border;
          ShapeVertex(cgo, REL, c);
          c++;

          *(p++) = I->border + (I->width * a);
          *(p++) = -(I->border + I->height + I->height);
          *(p++) = I->border;
          c++;
        }

      }
    }
  } else {
    int samples = 20;
    float fxn;
    float color[3];
    n_extra = 3 * samples;
    VLACheck(gs->Coord, float, (I->var_index + n_extra) * 3);
    c = I->var_index;
    p = gs->Coord + 3 * c;

    for(a = 0; a < samples; a++) {
      fxn = a / (samples - 1.0F);

      ObjectGadgetRampCalculate(I, fxn, color);
      CGOColorv(cgo, color);

      *(p++) = I->border + (I->width * fxn);
      *(p++) = -I->border;
      *(p++) = I->border;
      ShapeVertex(cgo, REL, c);
      c++;

      *(p++) = I->border + (I->width * fxn);
      *(p++) = -(I->border + I->bar_height);
      *(p++) = I->border;
      ShapeVertex(cgo, REL, c);
      c++;

      *(p++) = I->border + (I->width * fxn);
      *(p++) = -(I->border + I->height + I->height);
      *(p++) = I->border;
      c++;

    }
  }
  gs->NCoord = c;
  /* center */
  CGOEnd(cgo);
#endif

  CGOColor(cgo, 0.5F, 0.5F, 0.5F);
  /* top */
#ifdef _PYMOL_CGO_DRAWARRAYS
  {
    int nverts = 4, pl = 0;
    float *vertexVals, *normalVals;
    const float nVals[] = {
      LKP, 2.f, 0.f,
      LKP, 2.f, 0.f,
      LKP, 1.f, 0.f,
      LKP, 1.f, 0.f
    };
    const float vVals[] = {
      REL, 5.f, 0.f,
      REL, 6.f, 0.f,
      REL, 1.f, 0.f,
      REL, 2.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY, nverts);
    normalVals = vertexVals + (3*nverts);
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
      normalVals[pl] = nVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeNormal(cgo, LKP, 2);
  ShapeVertex(cgo, REL, 5);
  ShapeVertex(cgo, REL, 6);
  ShapeNormal(cgo, LKP, 1);
  ShapeVertex(cgo, REL, 1);
  ShapeVertex(cgo, REL, 2);
  CGOEnd(cgo);
#endif

  /* bottom */
#ifdef _PYMOL_CGO_DRAWARRAYS
  {
    int nverts = 4, pl = 0;
    float *vertexVals, *normalVals;
    const float nVals[] = {
      LKP, 4.f, 0.f,
      LKP, 4.f, 0.f,
      LKP, 2.f, 0.f,
      LKP, 2.f, 0.f
    };
    const float vVals[] = {
      REL, 3.f, 0.f,
      REL, 4.f, 0.f,
      REL, 7.f, 0.f,
      REL, 8.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY, nverts);      
    normalVals = vertexVals + (3*nverts);
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
      normalVals[pl] = nVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeNormal(cgo, LKP, 4);
  ShapeVertex(cgo, REL, 3);
  ShapeVertex(cgo, REL, 4);
  ShapeNormal(cgo, LKP, 2);
  ShapeVertex(cgo, REL, 7);
  ShapeVertex(cgo, REL, 8);
  CGOEnd(cgo);
#endif

  /* left */
#ifdef _PYMOL_CGO_DRAWARRAYS
  {
    int nverts = 4, pl = 0;
    float *vertexVals, *normalVals;
    const float nVals[] = {
      LKP, 3.f, 0.f,
      LKP, 3.f, 0.f,
      LKP, 2.f, 0.f,
      LKP, 2.f, 0.f
    };
    const float vVals[] = {
      REL, 1.f, 0.f,
      REL, 3.f, 0.f,
      REL, 5.f, 0.f,
      REL, 7.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY, nverts);      
    normalVals = vertexVals + (3*nverts);
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
      normalVals[pl] = nVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeNormal(cgo, LKP, 3);
  ShapeVertex(cgo, REL, 1);
  ShapeVertex(cgo, REL, 3);
  ShapeNormal(cgo, LKP, 2);
  ShapeVertex(cgo, REL, 5);
  ShapeVertex(cgo, REL, 7);
  CGOEnd(cgo);
#endif

  /* right */
#ifdef _PYMOL_CGO_DRAWARRAYS
  {
    int nverts = 4, pl = 0;
    float *vertexVals, *normalVals;
    const float nVals[] = {
      LKP, 2.f, 0.f,
      LKP, 2.f, 0.f,
      LKP, 0.f, 0.f,
      LKP, 0.f, 0.f
    };
    const float vVals[] = {
      REL, 6.f, 0.f,
      REL, 8.f, 0.f,
      REL, 2.f, 0.f,
      REL, 4.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY | CGO_NORMAL_ARRAY, nverts);      
    normalVals = vertexVals + (3*nverts);
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
      normalVals[pl] = nVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeNormal(cgo, LKP, 2);
  ShapeVertex(cgo, REL, 6);
  ShapeVertex(cgo, REL, 8);
  ShapeNormal(cgo, LKP, 0);
  ShapeVertex(cgo, REL, 2);
  ShapeVertex(cgo, REL, 4);
  CGOEnd(cgo);
#endif
  CGOStop(cgo);

  CGOFree(gs->ShapeCGO);
  gs->ShapeCGO = cgo;

  //#ifndef _PYMOL_NOPY
  CGOPreloadFonts(gs->ShapeCGO);
  //#endif

  cgo = CGONewSized(I->Gadget.Obj.G, 100);
  CGODotwidth(cgo, 5);
  CGOPickColor(cgo, 0, cPickableGadget);

  /* top */
#ifdef _PYMOL_CGO_DRAWARRAYS
  {
    int nverts = 4, pl = 0;
    float *vertexVals;
    const float vVals[] = {
      REL, 1.f, 0.f,
      REL, 2.f, 0.f,
      REL, 5.f, 0.f,
      REL, 6.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY, nverts);      
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeVertex(cgo, REL, 1);
  ShapeVertex(cgo, REL, 2);
  ShapeVertex(cgo, REL, 5);
  ShapeVertex(cgo, REL, 6);
  CGOEnd(cgo);
#endif

  /* bottom */
#ifdef _PYMOL_CGO_DRAWARRAYS
  {
    int nverts = 4, pl = 0;
    float *vertexVals;
    const float vVals[] = {
      REL, 3.f, 0.f,
      REL, 4.f, 0.f,
      REL, 7.f, 0.f,
      REL, 8.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY, nverts);      
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeVertex(cgo, REL, 3);
  ShapeVertex(cgo, REL, 4);
  ShapeVertex(cgo, REL, 7);
  ShapeVertex(cgo, REL, 8);
  CGOEnd(cgo);
#endif

  /* left */
#ifdef _PYMOL_CGO_DRAWARRAYS
  {
    int nverts = 4, pl = 0;
    float *vertexVals;
    const float vVals[] = {
      REL, 1.f, 0.f,
      REL, 3.f, 0.f,
      REL, 5.f, 0.f,
      REL, 7.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY, nverts);      
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeVertex(cgo, REL, 1);
  ShapeVertex(cgo, REL, 3);
  ShapeVertex(cgo, REL, 5);
  ShapeVertex(cgo, REL, 7);
  CGOEnd(cgo);
#endif

  /* right */
#ifdef _PYMOL_CGO_DRAWARRAYS
  {
    int nverts = 4, pl = 0;
    float *vertexVals;
    const float vVals[] = {
      REL, 6.f, 0.f,
      REL, 8.f, 0.f,
      REL, 2.f, 0.f,
      REL, 4.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY, nverts);      
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeVertex(cgo, REL, 6);
  ShapeVertex(cgo, REL, 8);
  ShapeVertex(cgo, REL, 2);
  ShapeVertex(cgo, REL, 4);
  CGOEnd(cgo);
#endif

  /* band */
  CGOPickColor(cgo, 13, cPickableGadget);
#ifdef _PYMOL_CGO_DRAWARRAYS
  {
    int nverts = 4, pl = 0;
    float *vertexVals;
    const float vVals[] = {
      REL, 5.f, 0.f,
      REL, 6.f, 0.f,
      REL, 7.f, 0.f,
      REL, 8.f, 0.f
    };
    vertexVals = CGODrawArrays(cgo, GL_TRIANGLE_STRIP, CGO_VERTEX_ARRAY, nverts);      
    for (pl=0; pl<3*nverts; pl++){
      vertexVals[pl] = vVals[pl];
    }
  }
#else
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  ShapeVertex(cgo, REL, 5);
  ShapeVertex(cgo, REL, 6);
  ShapeVertex(cgo, REL, 7);
  ShapeVertex(cgo, REL, 8);
  CGOEnd(cgo);
#endif

  CGOStop(cgo);

  CGOFree(gs->PickShapeCGO);
  gs->PickShapeCGO = cgo;
}

static void ObjectGadgetRampBuild(ObjectGadgetRamp * I)
{
  GadgetSet *gs = NULL;
  ObjectGadget *og;
  int a;

  float coord[100];
  int ix = 0;

  float normal[] = {
    1.0, 0.0, 0.1,
    0.0, 1.0, 0.1,
    0.0, 0.0, 1.0,
    -1.0, 0.0, 0.1,
    0.0, -1.0, 0.1,
  };
#define VV(a,b,c) {coord[ix++]=a;coord[ix++]=b;coord[ix++]=c;};

  VV(I->x, I->y, 0.3F);

  /* outer points */

  VV(0.0, 0.0, 0.0);
  VV(I->width + I->border * 2, 0.0, 0.0);
  VV(0.0, -(I->height + I->border * 2), 0.0);
  VV(I->width + I->border * 2, -(I->height + I->border * 2), 0.0);

  VV(I->border, -I->border, I->border);
  VV(I->width + I->border, -I->border, I->border);
  VV(I->border, -(I->height + I->border), I->border);
  VV(I->width + I->border, -(I->height + I->border), I->border);

  VV(I->border, -(I->border + I->bar_height), I->border);
  VV(I->width + I->border, -(I->border + I->bar_height), I->border);

  VV(I->border + I->text_border, I->text_border - (I->border + I->height),
     I->border + I->text_raise);
  VV(I->width + I->border, I->text_border - (I->border + I->height),
     I->border + I->text_raise);

  VV(0.0, 0.0, 0.0);
#undef VV

  OrthoBusyPrime(I->Gadget.Obj.G);

  og = &I->Gadget;
  gs = GadgetSetNew(I->Gadget.Obj.G);

  gs->NCoord = 14;
  I->var_index = gs->NCoord;
  gs->Coord = VLAlloc(float, gs->NCoord * 3);
  for(a = 0; a < gs->NCoord * 3; a++) {
    gs->Coord[a] = coord[a];
  }

  gs->NNormal = 5;
  gs->Normal = VLAlloc(float, gs->NNormal * 3);
  for(a = 0; a < gs->NNormal; a++) {
    copy3f(normal + 3 * a, gs->Normal + 3 * a);
    normalize3f(gs->Normal + 3 * a);
  }

  og->GSet[0] = gs;
  og->NGSet = 1;
  og->Obj.Context = 1;          /* unit window */
  gs->Obj = (ObjectGadget *) I;
  gs->State = 0;

  ObjectGadgetRampUpdateCGO(I, gs);
  gs->fUpdate(gs);

}


/*========================================================================*/
void ObjectGadgetRampUpdate(ObjectGadgetRamp * I)
{
  float scale;

  if(I->Gadget.Changed) {
    scale = (1.0F + 5 * I->Gadget.GSet[0]->Coord[13 * 3]);

    I->Gadget.GSet[0]->Coord[13 * 3] = 0.0;
    switch (I->RampType) {
    case cRampMol:
      {
        int a;
        for(a = 0; a < I->NLevel; a++) {
          I->Level[a] = I->Level[a] * scale;
        }
        ExecutiveInvalidateRep(I->Gadget.Obj.G, cKeywordAll, cRepAll, cRepInvColor);
      }
      break;
    default:
      if(I->NLevel == 2) {
        {
          float mean = (I->Level[0] + I->Level[1]) / 2.0F;
          I->Level[0] = (I->Level[0] - mean) * scale + mean;
          I->Level[2] = (I->Level[1] - mean) * scale + mean;
          ExecutiveInvalidateRep(I->Gadget.Obj.G, cKeywordAll, cRepAll, cRepInvColor);
        }
        break;
      } else if(I->NLevel == 3) {
        I->Level[0] = (I->Level[0] - I->Level[1]) * scale + I->Level[1];
        I->Level[2] = (I->Level[2] - I->Level[1]) * scale + I->Level[1];
        ExecutiveInvalidateRep(I->Gadget.Obj.G, cKeywordAll, cRepAll, cRepInvColor);
      }
    }
    if(I->Gadget.NGSet)
      if(I->Gadget.GSet[0]) {
        ObjectGadgetRampUpdateCGO(I, I->Gadget.GSet[0]);
        ObjectGadgetUpdateStates(&I->Gadget);
      }
    ObjectGadgetUpdateExtents(&I->Gadget);
    I->Gadget.Changed = false;
    SceneChanged(I->Gadget.Obj.G);
  }
}

static int ObjectGadgetRampHandleInputColors(ObjectGadgetRamp * I)
{
  int ok = true;
  if(I->NLevel < 1) {
    I->Level = VLASetSize(I->Level, 1);
    I->NLevel = 1;
    I->Level[0] = 0.0F;
  }
  if(ok && I->Color) {          /* handle cases where number of colors doesn't make expectation */
    int n_color = VLAGetSize(I->Color) / 3;

    if(!n_color) {
      I->Color = VLASetSize(I->Color, 3);
      I->Color[0] = I->Color[1] = I->Color[2] = 1.0F;
      n_color++;
    }

    if(n_color < I->NLevel) {
      I->Color = VLASetSize(I->Color, 3 * I->NLevel);
      while(n_color < I->NLevel) {
        float *v0 = I->Color + 3 * (n_color - 1);
        float *v1 = v0 + 3;
        copy3f(v0, v1);
        n_color++;
      }
    } else if(n_color > I->NLevel) {
      I->Extreme = VLAlloc(float, 6);   /* above/below extreme values */
      if(I->Extreme) {
        if(n_color == I->NLevel + 1) {  /* upper extreme only */
          copy3f(I->Color, I->Extreme);
          copy3f(I->Color + 3 * I->NLevel, I->Extreme + 3);
        } else {                /* both extremes */
          copy3f(I->Color + (3 * I->NLevel), I->Extreme);
          copy3f(I->Color + (3 * I->NLevel) + 3, I->Extreme + 3);
        }
      }
      I->Color = VLASetSize(I->Color, 3 * I->NLevel);
    }
  }
  if(ok && I->Color && I->NLevel) {     /* detect default coloring as negative R */
    I->Special = VLACalloc(int, I->NLevel + 2);
    if(I->Special && I->Color) {
      int a;
      for(a = 0; a < I->NLevel; a++) {
        if(I->Color[a * 3] < 0.0F) {
          I->Special[a] = (int) I->Color[a * 3];
        }
      }
      if(I->Extreme) {          /* Extremes */
        for(a = 0; a < 2; a++) {
          if(I->Extreme[a * 3] < 0.0F) {        /* is red negative? then color is special */
            I->Special[I->NLevel + a] = (int) I->Extreme[a * 3];
          }
        }
      }
    }
  }

  return ok;
}


/*========================================================================*/
ObjectGadgetRamp *ObjectGadgetRampMapNewAsDefined(PyMOLGlobals * G,
                                                  ObjectMap * map,
                                                  float *level_vla,
                                                  float *color_vla, int map_state,
                                                  float *vert_vla, float beyond,
                                                  float within, float sigma, int zero,
                                                  int calc_mode)
{

  ObjectGadgetRamp *I;
  int ok = true;
  I = ObjectGadgetRampNew(G);
  I->RampType = cRampMap;

  if(ok)
    I->Color = color_vla;
  if(ok)
    I->CalcMode = calc_mode;

  if(ok) {
    ObjectMapState *ms;
    float tmp_level[3];
    if(map_state < 0)
      map_state = 0;
    if(vert_vla && (ms = ObjectMapGetState(map, map_state))) {
      if(ObjectMapStateGetExcludedStats(G, ms, vert_vla, beyond, within, tmp_level)) {
        tmp_level[0] = tmp_level[1] + (tmp_level[0] - tmp_level[1]) * sigma;
        tmp_level[2] = tmp_level[1] + (tmp_level[2] - tmp_level[1]) * sigma;
        if(zero) {
          if(tmp_level[1] < 0.0F) {
            tmp_level[1] = 0.0F;
            tmp_level[2] = -tmp_level[0];
          } else if(tmp_level[1] > 0.0F) {
            tmp_level[1] = 0.0F;
            tmp_level[0] = -tmp_level[2];
          }
        }
      }
      I->Level = VLAlloc(float, 3);
      copy3f(tmp_level, I->Level);
      VLAFreeP(level_vla);
    } else {
      I->Level = level_vla;
    }
  }
  if(ok)
    I->NLevel = VLAGetSize(I->Level);
  if(ok)
    ok = ObjectGadgetRampHandleInputColors(I);
  ObjectGadgetRampBuild(I);
  UtilNCopy(I->SrcName, map->Obj.Name, WordLength);
  I->SrcState = map_state;

  /* test interpolate 
     { 
     float test[3];

     ObjectGadgetRampInterpolate(I,-2.0,test);
     dump3f(test,"test color");
     ObjectGadgetRampInterpolate(I,-1.0,test);
     dump3f(test,"test color");
     ObjectGadgetRampInterpolate(I,-0.9,test);
     dump3f(test,"test color");
     ObjectGadgetRampInterpolate(I,-0.5,test);
     dump3f(test,"test color");
     ObjectGadgetRampInterpolate(I,0.0,test);
     dump3f(test,"test color");
     ObjectGadgetRampInterpolate(I,0.5,test);
     dump3f(test,"test color");
     ObjectGadgetRampInterpolate(I,1.0,test);
     dump3f(test,"test color");
     ObjectGadgetRampInterpolate(I,2.0,test);
     dump3f(test,"test color");
     }
   */
  return (I);

}


/*========================================================================*/
ObjectGadgetRamp *ObjectGadgetRampMolNewAsDefined(PyMOLGlobals * G, ObjectMolecule * mol,
                                                  float *level_vla,
                                                  float *color_vla,
                                                  int mol_state, int calc_mode)
{

  ObjectGadgetRamp *I;
  int ok = true;

  I = ObjectGadgetRampNew(G);
  if(mol)
    I->RampType = cRampMol;
  else
    I->RampType = cRampNone;
  if(ok)
    I->Color = color_vla;
  if(ok)
    I->CalcMode = calc_mode;
  if(ok)
    I->Level = level_vla;
  if(ok)
    I->NLevel = VLAGetSize(I->Level);
  if(ok)
    ok = ObjectGadgetRampHandleInputColors(I);

  if(ok && I->Level && I->NLevel) {
    int a;
    float last = I->Level[0];
    for(a = 1; a < I->NLevel; a++) {
      if(I->Level[a] < last)
        I->Level[a] = last;
      last = I->Level[a];
    }
  }

  ObjectGadgetRampBuild(I);
  if(mol) {
    UtilNCopy(I->SrcName, mol->Obj.Name, WordLength);
  } else {
    UtilNCopy(I->SrcName, "none", WordLength);
  }
  I->SrcState = mol_state;
  return (I);
}

static void ObjectGadgetRampInvalidate(ObjectGadgetRamp * I, int rep, int level,
                                       int state)
{
  I->Gadget.Changed = true;
}


/*========================================================================*/
ObjectGadgetRamp *ObjectGadgetRampNew(PyMOLGlobals * G)
{
  OOAlloc(G, ObjectGadgetRamp);

  ObjectGadgetInit(G, &I->Gadget);
  I->Gadget.GadgetType = cGadgetRamp;
  I->RampType = 0;
  I->NLevel = 0;
  I->Level = NULL;
  I->Color = NULL;
  I->Special = NULL;
  I->Extreme = NULL;
  I->SrcName[0] = 0;

  I->Gadget.Obj.fUpdate = (void (*)(CObject *)) ObjectGadgetRampUpdate;
  I->Gadget.Obj.fFree = (void (*)(CObject *)) ObjectGadgetRampFree;
  I->Gadget.Obj.fInvalidate =
    (void (*)(CObject *, int, int, int)) ObjectGadgetRampInvalidate;

  I->Mol = NULL;
  I->Map = NULL;
  I->width = 0.9F;
  I->height = 0.06F;
  I->bar_height = 0.03F;
  I->text_raise = 0.003F;
  I->text_border = 0.004F;
  I->text_scale_h = 0.04F;
  I->text_scale_v = 0.02F;
  I->border = 0.018F;
  I->var_index = 0;
  I->x = (1.0F - (I->width + 2 * I->border)) / 2.0F;
  I->y = 0.12F;
  I->Map = NULL;
  I->CalcMode = 0;
  return (I);
}
