

/* 
A* -------------------------------------------------------------------
B* This file contains source code for the PyMOL computer program
C* Copyright (c) Schrodinger, LLC. 
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"MemoryDebug.h"
#include"CGO.h"
#include"Err.h"
#include"Base.h"
#include"OOMac.h"
#include"Crystal.h"
#include"Feedback.h"
#include"Util.h"
#include"PConv.h"

CCrystal *CrystalNewFromPyList(PyMOLGlobals * G, PyObject * list)
{
#ifdef _PYMOL_NOPY
  return NULL;
#else

  CCrystal *I = NULL;
  I = CrystalNew(G);
  if(I) {
    if(!CrystalFromPyList(I, list)) {
      CrystalFree(I);
      I = NULL;
    }
  }
  return (I);
#endif

}

PyObject *CrystalAsPyList(CCrystal * I)
{
#ifdef _PYMOL_NOPY
  return NULL;
#else
  PyObject *result = NULL;

  if(I) {
    result = PyList_New(2);
    PyList_SetItem(result, 0, PConvFloatArrayToPyList(I->Dim, 3));
    PyList_SetItem(result, 1, PConvFloatArrayToPyList(I->Angle, 3));
  }
  return (PConvAutoNone(result));
#endif
}

int CrystalFromPyList(CCrystal * I, PyObject * list)
{
#ifdef _PYMOL_NOPY
  return 0;
#else
  int ok = true, rok = true;
  int ll = 0;
  if(ok)
    ok = (I != NULL);
  if(ok)
    ok = PyList_Check(list);
  if(ok)
    ll = PyList_Size(list);
  rok = ok;
  if(ok && (ll > 0))
    ok = PConvPyListToFloatArrayInPlace(PyList_GetItem(list, 0), I->Dim, 3);
  if(ok && (ll > 1))
    ok = PConvPyListToFloatArrayInPlace(PyList_GetItem(list, 1), I->Angle, 3);
  if(ok)
    CrystalUpdate(I);

  /* TO SUPPORT BACKWARDS COMPATIBILITY...
     Always check ll when adding new PyList_GetItem's */

  return (rok);
#endif

}

void CrystalFree(CCrystal * I)
{
  OOFreeP(I);
}

void CrystalInit(PyMOLGlobals * G, CCrystal * I)
{
  int a;
  I->G = G;
  for(a = 0; a < 9; a++) {
    I->RealToFrac[a] = 0.0;
    I->FracToReal[a] = 0.0;
  }
  for(a = 0; a < 3; a++) {
    I->Angle[a] = 90.0;
    I->Dim[a] = 1.0;
    I->RealToFrac[a + a * 3] = 1.0;
    I->FracToReal[a + a * 3] = 1.0;
  }
  I->UnitCellVolume = 1.0;

}

CCrystal *CrystalNew(PyMOLGlobals * G)
{
  OOAlloc(G, CCrystal);
  CrystalInit(G, I);
  return (I);
}

CCrystal *CrystalCopy(CCrystal * other)
{
  OOAlloc(other->G, CCrystal);
  UtilCopyMem(I, other, sizeof(CCrystal));
  return (I);
}

void CrystalUpdate(CCrystal * I)
{
  float cabg[3]; /* Cosine of axis angle */
  float sabg[3]; /* Singe of axis angle */
  float cabgs[3];
  float sabgs1;
  int i;

  /* if we just cleared out the memory, but didn't init
   * then init the crystal and return */
  if(((I->Angle[0] == 0.0F) &&
      (I->Angle[1] == 0.0F) &&
      (I->Angle[2] == 0.0F)) ||
     ((I->Dim[0] == 0.0F) && (I->Dim[1] == 0.0F) && (I->Dim[2] == 0.0F))) {
    CrystalInit(I->G, I);
    return;
  }

  for(i = 0; i < 9; i++) {
    I->RealToFrac[i] = 0.0;
    I->FracToReal[i] = 0.0;
  }

  for(i = 0; i < 3; i++) {
    cabg[i] = (float) cos(I->Angle[i] * PI / 180.0);
    sabg[i] = (float) sin(I->Angle[i] * PI / 180.0);
  }

  cabgs[0] = (cabg[1] * cabg[2] - cabg[0]) / (sabg[1] * sabg[2]);
  cabgs[1] = (cabg[2] * cabg[0] - cabg[1]) / (sabg[2] * sabg[0]);
  cabgs[2] = (cabg[0] * cabg[1] - cabg[2]) / (sabg[0] * sabg[1]);

  I->UnitCellVolume = (float) (I->Dim[0] * I->Dim[1] * I->Dim[2] *
                               sqrt1d(1.0 + (double) 2.0 * cabg[0] * cabg[1] * cabg[2] -
                                      (double) (cabg[0] * cabg[0] +
                                                (double) cabg[1] * cabg[1] +
                                                (double) cabg[2] * cabg[2])));

  I->RecipDim[0] = I->Dim[1] * I->Dim[2] * sabg[0] / I->UnitCellVolume;
  I->RecipDim[1] = I->Dim[0] * I->Dim[2] * sabg[1] / I->UnitCellVolume;
  I->RecipDim[2] = I->Dim[0] * I->Dim[1] * sabg[2] / I->UnitCellVolume;

  sabgs1 = (float) sqrt1d(1.0 - cabgs[0] * cabgs[0]);

  I->RealToFrac[0] = 1.0F / I->Dim[0];
  I->RealToFrac[1] = -cabg[2] / (sabg[2] * I->Dim[0]);
  I->RealToFrac[2] = -(cabg[2] * sabg[1] * cabgs[0] + cabg[1] * sabg[2]) /
    (sabg[1] * sabgs1 * sabg[2] * I->Dim[0]);
  I->RealToFrac[4] = 1.0F / (sabg[2] * I->Dim[1]);
  I->RealToFrac[5] = cabgs[0] / (sabgs1 * sabg[2] * I->Dim[1]);
  I->RealToFrac[8] = 1.0F / (sabg[1] * sabgs1 * I->Dim[2]);

  I->FracToReal[0] = I->Dim[0];
  I->FracToReal[1] = cabg[2] * I->Dim[1];
  I->FracToReal[2] = cabg[1] * I->Dim[2];
  I->FracToReal[4] = sabg[2] * I->Dim[1];
  I->FracToReal[5] = -sabg[1] * cabgs[0] * I->Dim[2];
  I->FracToReal[8] = sabg[1] * sabgs1 * I->Dim[2];

  I->Norm[0] = (float) sqrt1d(I->RealToFrac[0] * I->RealToFrac[0] +
                              I->RealToFrac[1] * I->RealToFrac[1] +
                              I->RealToFrac[2] * I->RealToFrac[2]);
  I->Norm[1] = (float) sqrt1d(I->RealToFrac[3] * I->RealToFrac[3] +
                              I->RealToFrac[4] * I->RealToFrac[4] +
                              I->RealToFrac[5] * I->RealToFrac[5]);
  I->Norm[2] = (float) sqrt1d(I->RealToFrac[6] * I->RealToFrac[6] +
                              I->RealToFrac[7] * I->RealToFrac[7] +
                              I->RealToFrac[8] * I->RealToFrac[8]);

}

void CrystalDump(CCrystal * I)
{
  PyMOLGlobals *G = I->G;
  int i;

  PRINTF
    " Crystal: Unit Cell         %8.3f %8.3f %8.3f\n", I->Dim[0], I->Dim[1], I->Dim[2]
    ENDF(G);
  PRINTF
    " Crystal: Alpha Beta Gamma  %8.3f %8.3f %8.3f\n",
    I->Angle[0], I->Angle[1], I->Angle[2]
    ENDF(G);
  PRINTF " Crystal: RealToFrac Matrix\n" ENDF(G);
  for(i = 0; i < 3; i++) {
    PRINTF " Crystal: %9.4f %9.4f %9.4f\n",
      I->RealToFrac[i * 3], I->RealToFrac[i * 3 + 1], I->RealToFrac[i * 3 + 2]
      ENDF(G);
  }
  PRINTF " Crystal: FracToReal Matrix\n" ENDF(G);
  for(i = 0; i < 3; i++) {
    PRINTF
      " Crystal: %9.4f %9.4f %9.4f\n",
      I->FracToReal[i * 3], I->FracToReal[i * 3 + 1], I->FracToReal[i * 3 + 2]
      ENDF(G);
  }
  PRINTF " Crystal: Unit Cell Volume %8.0f.\n", I->UnitCellVolume ENDF(G);

}

CGO *CrystalGetUnitCellCGO(CCrystal * I)
{
  PyMOLGlobals *G = I->G;
  float v[3];
  CGO *cgo = NULL;
  if(I) {
    cgo = CGONew(G);
    CGODisable(cgo, GL_LIGHTING);

#ifdef _PYMOL_CGO_DRAWARRAYS
    {
      int nverts = 10, pl = 0;
      float *vertexVals;
      vertexVals = CGODrawArrays(cgo, GL_LINE_STRIP, CGO_VERTEX_ARRAY, nverts);	      
      set3f(v, 0, 0, 0);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 1, 0, 0);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 1, 1, 0);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 0, 1, 0);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 0, 0, 0);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 0, 0, 1);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 1, 0, 1);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 1, 1, 1);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 0, 1, 1);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 0, 0, 1);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
    }
    {
      int nverts = 6, pl = 0;
      float *vertexVals;
      vertexVals = CGODrawArrays(cgo, GL_LINES, CGO_VERTEX_ARRAY, nverts);	      
      set3f(v, 0, 1, 0);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 0, 1, 1);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 1, 1, 0);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 1, 1, 1);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 1, 0, 0);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
      set3f(v, 1, 0, 1);
      transform33f3f(I->FracToReal, v, v);
      vertexVals[pl++] = v[0]; vertexVals[pl++] = v[1]; vertexVals[pl++] = v[2];
    }
#else
    CGOBegin(cgo, GL_LINE_STRIP);
    set3f(v, 0, 0, 0);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 1, 0, 0);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 1, 1, 0);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 0, 1, 0);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 0, 0, 0);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 0, 0, 1);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 1, 0, 1);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 1, 1, 1);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 0, 1, 1);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 0, 0, 1);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);
    CGOEnd(cgo);

    CGOBegin(cgo, GL_LINES);

    set3f(v, 0, 1, 0);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 0, 1, 1);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 1, 1, 0);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 1, 1, 1);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 1, 0, 0);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);

    set3f(v, 1, 0, 1);
    transform33f3f(I->FracToReal, v, v);
    CGOVertexv(cgo, v);
    CGOEnd(cgo);
#endif

    CGOEnable(cgo, GL_LIGHTING);
    CGOStop(cgo);
  }
  return (cgo);
}
