// nUtil - An utility Library for gnurbs
// Copyright (C) 2008-2026 Eric Bechet
//
// See the LICENSE file for contributions and license information.
// Please report all bugs and problems to <bechet@cadxfem.org>.
//

#include <cstring>
#include <limits>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include "GL/glu.h"
#ifdef WIN32
#include "windows.h"
#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE  0x809D
#endif
#endif //WIN32

#include "nglu.h"
#include "fltkdisplay.h"
#include "nfltkwindow.h"
#include "ndisplaytofile.h"

#include <FL/Fl.H>
#include <FL/Fl_Menu_Item.H>


GLenum glCheckError_(const char *file, int line)
{
    GLenum errorCode;
    while ((errorCode = glGetError()) != GL_NO_ERROR)
    {
        std::string error;
        switch (errorCode)
        {
            case GL_INVALID_ENUM:                  error = "INVALID_ENUM"; break;
            case GL_INVALID_VALUE:                 error = "INVALID_VALUE"; break;
            case GL_INVALID_OPERATION:             error = "INVALID_OPERATION"; break;
            case GL_STACK_OVERFLOW:                error = "STACK_OVERFLOW"; break;
            case GL_STACK_UNDERFLOW:               error = "STACK_UNDERFLOW"; break;
            case GL_OUT_OF_MEMORY:                 error = "OUT_OF_MEMORY"; break;
            case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break;
        }
        std::cout << error << " | " << file << " (" << line << ")" << std::endl;
    }
    return errorCode;
}
#define glCheckError() glCheckError_(__FILE__, __LINE__) 

fltkdisplay::fltkdisplay(color _c,const char *_name) : drawlines(true),drawpoints(true),drawsurfaces(true),drawtexts(true),AA(true),
                                                       up_to_date(false),data(0x0),data2(0x0),
                                                       llf(std::numeric_limits< double >::max(),
                                                           std::numeric_limits< double >::max(),
                                                           std::numeric_limits< double >::max()),
                                                       urb(std::numeric_limits< double >::min(),
                                                           std::numeric_limits< double >::min(),
                                                           std::numeric_limits< double >::min()),
                                                       focal(2.8),fov( atan(1./focal) * 2. * 180 / 3.14159265),
                                                         nearclip(0.1),farclip(40.),win(0)//,current_pick(pickinfo()), picking(false)
{
#ifdef USE_DLISTS
    Dlist=0;
#endif
    
  vp[0]=1.0;vp[1]=2.2;vp[2]=2.0; look[0]=0.;look[1]=0.;look[2]=0.;up[0]=0.; up[1]=1.;up[2]=0.;
  if (_name)
  {
    name=new char[strlen(_name)+1];
    strcpy(name,_name);
  }
  else
    name=NULL;
  c=_c;
  setpick(false);
  picking=false;
  glpicking=false;
  tagid=false;
  setcallbacks(NULL);
  int scrW=Fl::w();
  int scrH=Fl::h();
  int W=(800*scrW)/1920;
  int H=(600*scrW)/1920;
  win =new nFltkWindow( W, H,name,this);
}

fltkdisplay::~fltkdisplay()
{
  if (name) delete[] name;
  if (data2) delete data2;
  if (win) delete win;
}

void fltkdisplay::display()
{
  if (ncb)
  {
    const std::vector<data_container> *datas=ncb->get_data();
    if (datas)
    {
      ncb->draw();
      init_data(*datas);
    }
    else
    {
      ncb->draw();
    }
  }
  win->show();
  reset_view();
  Fl::run();
}


void fltkdisplay::setpick(bool p)
{
  ndisplay_base::setpick(p);
  if (win)
  {
    if (win->m_pick)
    {
      if (p) win->m_pick->set();
      else win->m_pick->clear();
    }
  }
}


bool fltkdisplay::individual_picks(bool p)
{
  if (win)
  {
    if (win->m_indiv_pick)
    {
      if (p) win->m_indiv_pick->set();
      else win->m_indiv_pick->clear();
    }
  }  
  bool pp=tagid; tagid=p;return pp;
}

bool fltkdisplay::gl_picks(bool p)
{
  if (win)
  {
    if (win->m_gl_pick)
    {
      if (p) win->m_gl_pick->set();
      else win->m_gl_pick->clear();
    }
  }
  bool pp=glpicking;
  glpicking=p;
  return pp;
}

void  fltkdisplay::draw ( void )  // called from fltk
{
  if ( !win->gl->valid() )
  {
    setup_gl();
    get_matrices();
  }
  #ifndef MESA
//  glDrawBuffer(GL_FRONT_AND_BACK);
  #endif // !MESA
  glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  draw_model();
  #ifndef MESA
//  glDrawBuffer(GL_BACK);
  #endif // !MESA
}

void fltkdisplay::draw_model_pick() // called to pick
{
  glDepthMask(GL_TRUE);
  glInitNames();
  if (data) for (int i=0;i<data->size();++i)
    _draw_model(false,DRAW_PICK,i);// display only solid elements
}

void fltkdisplay::draw_model(int mode) // called automatically at every refresh
{
  if (data)
  {
    for (int i=0;i<data->size();++i)
    {

#ifdef USE_DLISTS
      if (!up_to_date||(*data)[i].changed(false))
      {
        glDeleteLists(Dlists[i],1);
        Dlists[i]=glGenLists(1);
        glNewList(Dlists[i], GL_COMPILE);
#endif
        glDepthMask(GL_TRUE);
        _draw_model(false,mode,i);// display only solid elements
        glDepthMask(GL_FALSE);
        _draw_model(true,mode,i); // display only transparent elements
        glDepthMask(GL_TRUE);

#ifdef USE_DLISTS 
        glEndList();
      }
      glCallList(Dlists[i]);
#endif
    }
    up_to_date=true;
  }
}

int fltkdisplay::handle(int event) //called from fltk
{
 switch ( event )
  {
    case FL_MOVE:
    {
      npoint3 p;
      npoint3 v;
      GLdouble xpp,ypp,zz;
      xp=Fl::event_x();
      yp=win->gl->h()-Fl::event_y();
      
      
      
      get_coords ( xp,yp, p,v,z);
      pickedpoint=p;
      std::stringstream s;
      s << "(" << xp << "," << yp << ") " << "("<< p[0] << "," << p[1] << "," << p[2] << ") ";
      if ((getpick()||Fl::event_button3())&&!Fl::event_state(FL_SHIFT))
      {
        int r;
        std::vector<pickinfo> picks;
        int nbpicks;
        if (glpicking) nbpicks=pick(xp,yp,picks);
        else nbpicks=0;
        if (nbpicks>0)
        {
          int whichpick=0;
          if (whichpick>=0)
          {
            picking=true;
            if (current_pick!=picks[whichpick]);
            {
              current_pick=picks[whichpick];
//              up_to_date=false;
              win->gl->redraw();
 
              switch(current_pick.type)
              {
                case 1: s << "Vertex";break;
                case 2: s << "Line";break;
                case 3: s << "Triangle";break;
                case 4: s << "Quad";break;
                default : break;
              }
              if (current_pick.id!=-1) s << " #" << current_pick.id;
              s << " in seg #" << current_pick.seg;
              if (current_pick.nb_uid)
              {
                s << " uids:";
                for (int i=0;i<current_pick.nb_uid;++i) s << " " << current_pick.uids[i];
              }
              if ((current_pick.id!=-1)&&(current_pick.type==1))
              {
                point p=(*data)[current_pick.seg].get_point(current_pick.id);
                s << " x=" << p.pts[0] << " y=" << p.pts[1] << " z=" << p.pts[2];
              }
              if (ncb)
              {
                std::string ss=ncb->get_info(current_pick);
                if (!ss.empty()) s << " : " << ss;
              }
            }
            get_coords_with_z( xp,yp,z, p,v);
            pickedpoint=p;
          }
        }
        else
        {
          if (picking){up_to_date=false; win->gl->redraw();};
          picking=false;
        }
      }
      win->info->value(s.str().c_str());
      return 1;
    }
    case FL_PUSH:
//    ... mouse down event ...
//    ... position in Fl::event_x() and Fl::event_y()
    {
//      std::cout << Fl::event_button() << std::endl;
      npoint3 p;
      npoint3 v;
      GLdouble xpp,ypp,zz;
      xp=Fl::event_x();
      yp=win->gl->h()-Fl::event_y();
      get_coords ( xp,yp, p,v,z);
      pickedpoint=p;
      if (Fl::event_key(FL_Shift_L))
      {
 //       picking=true;
      } 
      else
      if (getpick()||Fl::event_button3())
      {
        int r;
        std::vector<pickinfo> picks;
        int nbpicks;
        if (glpicking) nbpicks=pick(xp,yp,picks);
        else nbpicks=0;
        int whichpick=0;
        if (ncb)
        {
          whichpick=ncb->pick_callback(nbpicks,&picks[0],p);
        }
        if ((nbpicks>0)||(!glpicking))
        {
          if ((whichpick>=0)||(!glpicking))
          {
            picking=true;
            if (glpicking)
            {
              current_pick=picks[whichpick];
              up_to_date=false;
            }
            // correct z value given by get_coords which does not work for transparent objects)
//            z=((float)current_pick.z)/std::numeric_limits< unsigned int >::max();
            get_coords_with_z( xp,yp,z, p,v);
            pickedpoint=p;
          }
          else if (glpicking) picking=false;
        }
      }
      dinit=0.;
      for (int i=0;i<3;++i) {dinit+=(look[i]-vp[i])*(look[i]-vp[i]);}
      dinit=sqrt(dinit);
      orig=p;win->gl->redraw();
      return 1;
    }

    case FL_DRAG:  //    ... mouse moved while down event ...
    {
      int xx= Fl::event_x();
      int yy=win->gl->h()-Fl::event_y();
      npoint3 p;
      get_coords(xx,yy,z, p);
      std::stringstream s;
      s << "("<< p[0] << "," << p[1] << "," << p[2] << ") ";
      if (Fl::event_button1()&&(!Fl::event_key(FL_Control_L)))
      {

        npoint3 dir;
        if (Fl::event_key(FL_Shift_L))
        {
          std::vector<pickinfo> picks;
          int szx=abs(xx-xp);
          if (szx==0) szx=1;
          int szy=abs(yy-yp);
          if (szy==0) szy=1;
          int nbpicks=pick((xp+xx)/2,(yp+yy)/2,picks,szx,szy);
          current_picks=picks;
          std::sort(current_picks.begin(),current_picks.end(),less_pick());
//          up_to_date=false;
          win->gl->redraw(); 
        }
        else
        {
          if (picking&&(getpick()||Fl::event_button3()))
          {
            if (ncb)
            {
              int res=ncb->drag_callback(pickedpoint,p,current_pick);
              if (res) {up_to_date=false; win->gl->redraw();};
            }
          }
          else
          {
            double d=0.0;
            for (int i=0;i<3;++i) {d+=(look[i]-vp[i]+(p[i]-orig[i])*5)*(look[i]-vp[i]+(p[i]-orig[i])*5);}
            d=sqrt(d);
            for (int i=0;i<3;++i) { vp[i]-=(p[i]-orig[i])*5;orig[i]=p[i];}
            for (int i=0;i<3;++i) { vp[i]=(vp[i]-look[i])*dinit/d + look[i];}
            d=0.0;
            for (int i=0;i<3;++i) { dir[i]=vp[i]-look[i] ; d+=dir[i]*dir[i];}
            d=sqrt(d);
            for (int i=0;i<3;++i) { dir[i]/=d;}
            double pscal=0;
            for (int i=0;i<3;++i) { pscal+=dir[i]*up[i];}
            d=0.0;
            for (int i=0;i<3;++i) { up[i]-=dir[i]*pscal;d+=up[i]*up[i];}
            d=sqrt(d);
            for (int i=0;i<3;++i) { up[i]/=d;}
            setup_gl();
            win->gl->redraw();
          }
        }
      }
      else if (Fl::event_button2()||(Fl::event_button1()&&Fl::event_key(FL_Control_L)))
      {
        npoint3 p;
        int xx= Fl::event_x();
        int yy=win->gl->h()-Fl::event_y();
        get_coords (xx,yy,z, p);
        if (picking)
        {
          
        }
        else
        {
          for (int i=0;i<3;++i) { look[i]-=p[i]-orig[i];vp[i]-=p[i]-orig[i];orig[i]=p[i];}
          setup_gl();
          win->gl->redraw();
        }
      }
      win->info->value(s.str().c_str());
      return 1;
    }

    case FL_RELEASE: //    ... mouse up event ...
    { 
      npoint3 p;
      npoint3 dir;
      int xx= Fl::event_x();
      int yy=win->gl->h()-Fl::event_y();
      get_coords(xx,yy,z, p);
      if (getpick()||Fl::event_button3())
      {
        if (ncb)
        {
          int res=ncb->release_callback(pickedpoint,p,current_pick);
          if ((res)||(!glpicking)||Fl::event_button3())
          { /*up_to_date=false;*/ win->gl->redraw();}
        }
      }
      get_matrices();
      picking=false;
      return 1;
    }
    case FL_ENTER :
    {
//       win->gl->take_focus(); // keyboard focus
       return 1;
    }
    case FL_LEAVE :
    if (picking)
    {
      picking=false;
      //up_to_date=false;
      win->gl->redraw();
    }
    case FL_FOCUS :
    case FL_UNFOCUS :
      return 1;


    case FL_MOUSEWHEEL:
    {
      if (Fl::event_shift())
      {
        npoint3 p;
        npoint3 v;
        xp=Fl::event_x();
        yp=win->gl->h()-Fl::event_y();
        get_coords ( xp,yp, p,v,z);
        orig=p;
        double d=0.0;
        for (int i=0;i<3;++i) {d+=(look[i]-vp[i])*(look[i]-vp[i]);}
        d=sqrt(d);
        d/=10.;
        for (int i=0;i<3;++i) {look[i]-=v[i]*Fl::event_dy()*d;vp[i]-=v[i]*Fl::event_dy()*d;}
      }
        else
      {
        focal*=(1.0-Fl::event_dy()/10.0);
        fov=(( atan(1/focal)) * 2. * 180. / 3.14159265);
      }
    
      setup_gl();
      get_matrices();
      win->gl->redraw();
      return 1;
    }
    case FL_KEYBOARD:
    {
      int key=Fl::event_key();
      const char*txt=Fl::event_text();
      switch ( key )
      {

        case 'c' :
        {
          if (Fl::event_state()&FL_CTRL)
          {
            Fl::copy(win->info->value(), win->info->size(),1);
            return 1;
          }
          break;
        }
        case 'h' :
        {
          if (Fl::event_state()&FL_CTRL)
          {
            if (picking)
            {
              if (current_pick.nb_uid)
                hide(current_pick.nb_uid,current_pick.uids);
              else
                hide(current_pick.seg,current_pick.type,current_pick.id);
              up_to_date=false;
              win->gl->redraw();
            }
            else
            {
              if (current_picks.size())
              {
                for (int i=0;i<current_picks.size();++i)
                {
                  if (current_picks[i].nb_uid)
                    hide(current_picks[i].nb_uid,current_picks[i].uids);
                  else
                    hide(current_picks[i].seg,current_picks[i].type,current_picks[i].id);
                }
                up_to_date=false;
                win->gl->redraw();
              }
            }
            return 1;
          }
          break;
        }
        default : {  
          int ret=0;
          if ((ncb)&&(!(Fl::event_state()&FL_CTRL))&&(!(Fl::event_state()&FL_ALT)))
          {
             ret= ncb->key_callback(key,txt);
          }
          if (!ret)
          {
            return 0;
          }
          else win->gl->redraw();
          return 1;
        }
      }

//    ... keypress, key is in Fl::event_key(), ascii in Fl::event_text()
//    ... Return 1 if you understand/use the keyboard event, 0 otherwise...
      return 0;
    }

    case FL_SHORTCUT:
//    ... shortcut, key is in Fl::event_key(), ascii in Fl::event_text()
//    ... Return 1 if you understand/use the shortcut event, 0 otherwise...
      return 0;

    default:
      return 0;
  }
}


void fltkdisplay::hide(int seg,int type, int num)
{
  switch(type)
  {
    case 1:
      if (data)
        (*data)[seg].get_point(num).drawable=false;
      break;
    case 2:
      if (data)
        (*data)[seg].get_line(num).drawable=false;
      break;
    case 3:
      if (data)
        (*data)[seg].get_triangle(num).drawable=false;
      break;
    case 4:
      if (data)
        (*data)[seg].get_quad(num).drawable=false;
      break;
    default :
      break;
  }
}

void fltkdisplay::hide(int nb_uid, const int uids[])
{
  for (int n=0;n<data->size();n++)
  {
    for (int blk=0;blk<(*data)[n].getnumpropquads();++blk)
    {
      const properties &p=(*data)[n].getpropquads(blk);
      if (p.nb_uid==nb_uid)
      {
        bool equal=true;
        for (int u=0;u<nb_uid;++u)
        {
          if (p.uids[u]!=uids[u])
          {
            equal=false;
            break;
          }
        }
        if (equal)
        {
          int first=(*data)[n].getindexquads(blk);
          int last=(blk+1<(*data)[n].getnumpropquads()) ? (*data)[n].getindexquads(blk+1) : (*data)[n].nb_quads();
          for (int i=first;i!=last;++i)
            (*data)[n].get_quad(i).drawable=false;
        }
      }
    }
    for (int blk=0;blk<(*data)[n].getnumproptriangles();++blk)
    {
      const properties &p=(*data)[n].getproptriangles(blk);
      if (p.nb_uid==nb_uid)
      {
        bool equal=true;
        for (int u=0;u<nb_uid;++u)
        {
          if (p.uids[u]!=uids[u])
          {
            equal=false;
            break;
          }
        }
        if (equal)
        {
          int first=(*data)[n].getindextriangles(blk);
          int last=(blk+1<(*data)[n].getnumproptriangles()) ? (*data)[n].getindextriangles(blk+1) : (*data)[n].nb_triangles();
          for (int i=first;i!=last;++i)
            (*data)[n].get_triangle(i).drawable=false;
        }
      }
    }
    for (int blk=0;blk<(*data)[n].getnumproplines();++blk)
    {
      const properties &p=(*data)[n].getproplines(blk);
      if (p.nb_uid==nb_uid)
      {
        bool equal=true;
        for (int u=0;u<nb_uid;++u)
        {
          if (p.uids[u]!=uids[u])
          {
            equal=false;
            break;
          }
        }
        if (equal)
        {
          int first=(*data)[n].getindexlines(blk);
          int last=(blk+1<(*data)[n].getnumproplines()) ? (*data)[n].getindexlines(blk+1) : (*data)[n].nb_lines();
          for (int i=first;i!=last;++i)
            (*data)[n].get_line(i).drawable=false;
        }
      }
    }
    for (int blk=0;blk<(*data)[n].getnumproppoints();++blk)
    {
      const properties &p=(*data)[n].getproppoints(blk);
      if (p.nb_uid==nb_uid)
      {
        bool equal=true;
        for (int u=0;u<nb_uid;++u)
        {
          if (p.uids[u]!=uids[u])
          {
            equal=false;
            break;
          }
        }
        if (equal)
        {
          int first=(*data)[n].getindexpoints(blk);
          int last=(blk+1<(*data)[n].getnumproppoints()) ? (*data)[n].getindexpoints(blk+1) : (*data)[n].nb_points();
          for (int i=first;i!=last;++i)
            (*data)[n].get_point(i).drawable=false;
        }
      }
    }
  }
}


void fltkdisplay::unhide(void)
{
  for (int n=0;n<data->size();++n)
    (*data)[n].unhide();
}

void fltkdisplay::reset_bb(void)
{
  llf=npoint3(std::numeric_limits< double >::max(),std::numeric_limits< double >::max(),std::numeric_limits< double >::max());
  urb=npoint3(-std::numeric_limits< double >::max(),-std::numeric_limits< double >::max(),-std::numeric_limits< double >::max());
}

void chk_bb(npoint3 &llf,npoint3 &urb,const npoint3 &t)
{
  for (int i=0;i<3 ; ++i)
  {
    if (llf[i]>t[i]) llf[i]=t[i];
    if (urb[i]<t[i]) urb[i]=t[i];
  }
}

void fltkdisplay::compute_bb(void)
{
  reset_bb();
  if (data)
  {
    for (int n=0;n<data->size();++n)
    {
      for (int i=0;i!=(*data)[n].nb_quads();++i)
      {
        const quad &q=(*data)[n].get_quad(i);
        for (int j=0;j<4;++j)
        {
          chk_bb(llf,urb,q.pts[j]);
        }
      }
      for (int i=0;i!=(*data)[n].nb_triangles();++i)
      {
        const triangle &t=(*data)[n].get_triangle(i);
        for (int j=0;j<3;++j)
        {
          chk_bb(llf,urb,t.pts[j]);
        }
      }
      for (int i=0;i!=(*data)[n].nb_lines();++i)
      {
        const line &l=(*data)[n].get_line(i);
        for (int j=0;j<2;++j)
        {
          chk_bb(llf,urb,l.pts[j]);
        }
      }
      for (int i=0;i!=(*data)[n].nb_points();++i)
      {
        const point &p=(*data)[n].get_point(i);
        chk_bb(llf,urb,p.pts);
      }
      for (int dim=0;dim<3;dim++)
      {
        int nb=(*data)[n].nb_texts(dim);
        for (int i=0;i<nb;i++)
        {
          const point &pt=(*data)[n].get_text(dim,i);
          chk_bb(llf,urb,pt.pts);
        }
      }
    }
  }
  double sz[3];
  double mean[3];
  int max=-1;
  double ratio=0.001;
  double valmax=-1;
  for (int i=0;i<3;++i)
  {
    sz[i]=fabs(llf[i]-urb[i]);
    mean[i]=(llf[i]+urb[i])/2.0;
    if (valmax<sz[i])
    {
      valmax=sz[i];
      max=i;
    }
  }
  if (valmax==0.0) { valmax=1.0; max=-1;}
  for (int i=0;i<3;++i)
  {
    if (i!=max)
      if (sz[i]<ratio*valmax)
      {
        llf[i]=mean[i]-valmax*ratio/2.0;
        urb[i]=mean[i]+valmax*ratio/2.0;
        sz[i]=fabs(llf[i]-urb[i]);
        mean[i]=(llf[i]+urb[i])/2.0;
      }
  }
}

int fltkdisplay::getnumpick(void)
{
  int nb=0;
  if (data)
  {
    for (int n=0;n<data->size();++n)
    {
      nb+=(*data)[n].nb_points()+(*data)[n].nb_lines()+(*data)[n].nb_triangles()+(*data)[n].nb_quads();
    }
  }
  return nb;
} // return max number of pickable elements

void fltkdisplay::_draw_model(bool transparent,int mode,int iddata) // draw transparent or solid elements, or all if mode==DRAW_PICK
{
  if (data)
  {
    //if (mode==DRAW_PICK)
    //  glInitNames();
    //    for (int n=0;n<data->size();++n)
    int n=iddata;
    {
      if (drawsurfaces)
      {
        // draw quads
        for (int blk=0;blk<(*data)[n].getnumpropquads();++blk)
        {
          glEnable(GL_LIGHTING);
          const properties &p=(*data)[n].getpropquads(blk);
          if (mode==DRAW_PICK)
          {
            for (int i=0;i<p.nb_uid;++i) glPushName(p.uids[i]);
            glPushName(n);
            glPushName(4);
            glPushName(-1);// will be overwritten or not
          }
          int last=(blk+1<(*data)[n].getnumpropquads()) ? (*data)[n].getindexquads(blk+1) : (*data)[n].nb_quads();
          if (((p.c.A!=std::numeric_limits<unsigned char>::max()) != (!transparent))||(mode==DRAW_PICK))
          {
            
            glColor4ub(p.c.R,p.c.G,p.c.B,p.c.A);

            for (int i=(*data)[n].getindexquads(blk);i!=last;++i)
            {
              const quad &q=(*data)[n].get_quad(i);
              if (q.drawable)
              {
//                pickinfo pick(4,i,0,0x0);
                pickinfo pick(n,4,i,p.nb_uid,p.uids);
                bool pck=false;
                if (get_currentpick()==pick)
                {
                  glColor4ub(p.c.R+127,p.c.G+127,p.c.B+127,p.c.A);
                  pck=true;
                }
          //      else 
                {
                  std::vector<pickinfo>&ppp=get_currentpicks();
                  if (ppp.size())
                    if (std::binary_search(ppp.begin(),ppp.end(),pick,less_pick())) 
                    {
                      glColor4ub(255,0,0,p.c.A);
                      pck=true;
                    }
                }
                npoint3 A,B,normal;
                A=q.pts[1]-q.pts[0];
                B=q.pts[3]-q.pts[0];
                normal.crossprod(A,B);
                normal.normalize();
                if ((mode==DRAW_PICK)&&tagid)
                  glLoadName(i);
                glBegin(GL_QUADS);
                glNormal3d(normal.x(),normal.y(),normal.z());
                for (int j=0;j<4;++j)
                  glVertex3d(q.pts[j].x(),q.pts[j].y(),q.pts[j].z());
                glEnd();
                if (pck)
                  glColor4ub(p.c.R,p.c.G,p.c.B,p.c.A);
              }
            }
          }
          if (p.edgeon)
          {
            if (((p.edgecolor.A!=std::numeric_limits<unsigned char>::max()) != (!transparent)) || (mode==DRAW_PICK))
            {
              glDisable(GL_LIGHTING);
              glColor4ub(p.edgecolor.R,p.edgecolor.G,p.edgecolor.B,p.edgecolor.A);
              glLineWidth(p.edgethickness);

              for (int i=(*data)[n].getindexquads(blk);i!=last;++i)
              {
                const quad &q=(*data)[n].get_quad(i);
                if (q.drawable)
                {
                if ((mode==DRAW_PICK)&&tagid)
                    glLoadName(i);
                  glBegin(GL_LINES);
                  
                  for (int j=0;j<4;++j)
                  {
                    glVertex3d(q.pts[j].x(),q.pts[j].y(),q.pts[j].z());
                    glVertex3d(q.pts[(j+1)%4].x(),q.pts[(j+1)%4].y(),q.pts[(j+1)%4].z());
                  }
                  glEnd();
                }
              }
            }
          }
          if (mode==DRAW_PICK)
          {
            glPopName();
            glPopName();
            glPopName();
            for (int i=0;i<p.nb_uid;++i) glPopName();
          }
        }
      }

      // draw triangles
      if (drawsurfaces)
      {
        for (int blk=0;blk<(*data)[n].getnumproptriangles();++blk)
        {
          glEnable(GL_LIGHTING);
          const properties &p=(*data)[n].getproptriangles(blk);
          if (mode==DRAW_PICK)
          {
            for (int i=0;i<p.nb_uid;++i) glPushName(p.uids[i]);
            glPushName(n);
            glPushName(3);
            glPushName(-1);// will be overwritten
          }
          int last=(blk+1<(*data)[n].getnumproptriangles()) ? (*data)[n].getindextriangles(blk+1) : (*data)[n].nb_triangles();
          if (((p.c.A!=std::numeric_limits<unsigned char>::max()) != (!transparent))||(mode==DRAW_PICK))
          {
            glColor4ub(p.c.R,p.c.G,p.c.B,p.c.A);
            for (int i=(*data)[n].getindextriangles(blk);i!=last;++i)
            {
              const triangle &t=(*data)[n].get_triangle(i);
              if (t.drawable)
              {
                pickinfo pick(n,3,i,p.nb_uid,p.uids);
                bool pck=false;
                if (get_currentpick()==pick)
                {
                  glColor4ub(p.c.R+127,p.c.G+127,p.c.B+127,p.c.A);

                  pck=true;
                }
          //      else 
                {
                  std::vector<pickinfo>&ppp=get_currentpicks();
                  if (ppp.size())
                    if (std::binary_search(ppp.begin(),ppp.end(),pick,less_pick())) 
                    {
                      glColor4ub(255,0,0,p.c.A);
                      pck=true;
                    }
                }
                
                npoint3 A,B,normal;
                A=t.pts[1]-t.pts[0];
                B=t.pts[2]-t.pts[0];
                normal.crossprod(A,B);
                normal.normalize();
                if ((mode==DRAW_PICK)&&tagid)
                  glLoadName(i);
                glBegin(GL_TRIANGLES);
                glNormal3d(normal.x(),normal.y(),normal.z());
                for (int j=0;j<3;++j)
                  glVertex3d(t.pts[j].x(),t.pts[j].y(),t.pts[j].z());
                glEnd();
                if (pck)
                  glColor4ub(p.c.R,p.c.G,p.c.B,p.c.A);
              }
            }
          }
          if (p.edgeon&&drawlines)
          {
            glDisable(GL_LIGHTING);
            if (((p.edgecolor.A!=std::numeric_limits<unsigned char>::max()) != (!transparent))||(mode==DRAW_PICK))
            {
              glColor4ub(p.edgecolor.R,p.edgecolor.G,p.edgecolor.B,p.edgecolor.A);
              glLineWidth(p.edgethickness);

              for (int i=(*data)[n].getindextriangles(blk);i!=last;++i)
              {
                const triangle &t=(*data)[n].get_triangle(i);
                if (t.drawable)
                {
                if ((mode==DRAW_PICK)&&tagid)
                    glLoadName(i);
                  glBegin(GL_LINES);
                
                  for (int j=0;j<3;++j)
                  {
                    glVertex3d(t.pts[j].x(),t.pts[j].y(),t.pts[j].z());
                    glVertex3d(t.pts[(j+1)%3].x(),t.pts[(j+1)%3].y(),t.pts[(j+1)%3].z());
                  }
                  glEnd();
                }
              }
            }
          }
          if (mode==DRAW_PICK)
          {
            glPopName();
            glPopName();
            glPopName();
            for (int i=0;i<p.nb_uid;++i) glPopName();
          }
        }
      }
      
      // draw lines
      glDisable(GL_LIGHTING);
      if (drawlines)
      {
        for (int blk=0;blk<(*data)[n].getnumproplines();++blk)
        {
          const properties &p=(*data)[n].getproplines(blk);
          if (mode==DRAW_PICK)
          {
            for (int i=0;i<p.nb_uid;++i) glPushName(p.uids[i]);
            glPushName(n);
            glPushName(2);
            glPushName(-1);// will be overwritten
          }
          int last=(blk+1<(*data)[n].getnumproplines()) ? (*data)[n].getindexlines(blk+1) : (*data)[n].nb_lines();
          if (((p.c.A!=std::numeric_limits<unsigned char>::max()) != (!transparent))||(mode==DRAW_PICK))
          {
            glColor4ub(p.c.R,p.c.G,p.c.B,p.c.A);
            glLineWidth(p.thickness);
            for (int i=(*data)[n].getindexlines(blk);i!=last;++i)
            {
              const line &l=(*data)[n].get_line(i);
              if (l.drawable)
              {
                pickinfo pick(n,2,i,p.nb_uid,p.uids);
                bool pck;
                if (get_currentpick()==pick)
                {
                  glLineWidth(p.thickness+2);
                  glColor4ub(p.c.R+127,p.c.G+127,p.c.B+127,p.c.A);
                  pck=true;
                }
            //    else 
                {
                  std::vector<pickinfo>&ppp=get_currentpicks();
                  if (ppp.size())
                  if (std::binary_search(ppp.begin(),ppp.end(),pick,less_pick())) 
                  {
                    glColor4ub(255,0,0,p.c.A);
                    glLineWidth(p.thickness);
                    pck=true;
                  }
                }
                if ((mode==DRAW_PICK)&&tagid)
                  glLoadName(i);
                glBegin(GL_LINES);
  //              glNormal3f(0.,0.,0.);
                for (int j=0;j<2;++j)
                  glVertex3d(l.pts[j].x(),l.pts[j].y(),l.pts[j].z());
                glEnd();
                if (pck)
                {
                  glLineWidth(p.thickness);
                  glColor4ub(p.c.R,p.c.G,p.c.B,p.c.A);
                }
              }
            }
          }
          if (mode==DRAW_PICK)
          {
            glPopName();
            glPopName();
            glPopName();
            for (int i=0;i<p.nb_uid;++i) glPopName();
          }
        }
      }
      
      // draw points
      glDisable(GL_LIGHTING);
      if (drawpoints)
      {
        for (int blk=0;blk<(*data)[n].getnumproppoints();++blk)
        {
          const properties &p=(*data)[n].getproppoints(blk);
          if (mode==DRAW_PICK)
          {
            for (int i=0;i<p.nb_uid;++i) glPushName(p.uids[i]);
            glPushName(n);
            glPushName(1);
            glPushName(-1);// will be overwritten
          }
          int last=(blk+1<(*data)[n].getnumproppoints()) ? (*data)[n].getindexpoints(blk+1) : (*data)[n].nb_points();
          if (((p.c.A!=std::numeric_limits<unsigned char>::max()) != (!transparent))||(mode==DRAW_PICK))
          {
            glColor4ub(p.c.R,p.c.G,p.c.B,p.c.A);
            glPointSize(p.pointsize);
            for (int i=(*data)[n].getindexpoints(blk);i!=last;++i)
            {
              const point &pt=(*data)[n].get_point(i);
              if (pt.drawable)
              {
                pickinfo pick(n,1,i,p.nb_uid,p.uids);
                if (get_currentpick()==pick)
                {
                  glColor4ub(p.c.R+127,p.c.G+127,p.c.B+127,p.c.A);
                  glPointSize(p.pointsize+4);
                }
              //  else 
                {
                  std::vector<pickinfo>&ppp=get_currentpicks();
                  if (ppp.size())
                  if (std::binary_search(ppp.begin(),ppp.end(),pick,less_pick())) 
                  {
                    glColor4ub(255,0,0,p.c.A);
                    glPointSize(p.pointsize+4);
                  }
                }
                if ((mode==DRAW_PICK)&&tagid)
                  glLoadName(i);
                glBegin(GL_POINTS);
  //              glNormal3f(0.,0.,0.);

                glVertex3d(pt.pts.x(),pt.pts.y(),pt.pts.z());
                glEnd();
                if (get_currentpick()==pick)
                {
                  glColor4ub(p.c.R,p.c.G,p.c.B,p.c.A);
                  glPointSize(p.pointsize);
                }
              }
            }
          }
          if (mode==DRAW_PICK)
          {
            glPopName();
            glPopName();
            glPopName();
            for (int i=0;i<p.nb_uid;++i) glPopName();
          }
        }
      }
      
      //draw texts
      if ((!transparent)||(mode==DRAW_PICK))
      {
        glDisable(GL_LIGHTING);
        
        if (drawtexts)
        {
          gl_font(0, (win->fontsize*12)/14);
          glColor3f(1.0, 1.0, 1.0);
          for (int dim=0;dim<3;dim++)
          {
            int nb=(*data)[n].nb_texts(dim);
            for (int i=0;i<nb;i++)
            {
              const point &pt=(*data)[n].get_text(dim,i);
              const npoint3 &pos=pt.pts;        // 3D geometric position
              const char *txt=pt.info.c_str();  // actual text string
              glRasterPos3f(pos[0],pos[1],pos[2]);
              gl_draw(txt, strlen(txt));
            }
          }
        }
      }
    }
    glEnable(GL_LIGHTING);
  }
}

void fltkdisplay::update_view()
{
  if (AA)
    win->gl->mode(win->gl->mode()|FL_MULTISAMPLE);
  else
    win->gl->mode(win->gl->mode()&~FL_MULTISAMPLE);
  resetup_gl();
 // win->gl->flush();
}

void fltkdisplay::update_view_right(double d)
{
  if (d==0.)
  { 
    for (int i=0;i<3;++i) d+=(look[i]-vp[i])*(look[i]-vp[i]);
    d=sqrt(d);
  }
  const double nup[3]={0.,0.,1.};
  const double nvp[3]={look[0]+d,look[1],look[2]};
  set_viewpoint(focal,fov,nearclip,farclip,nvp,look,nup);
  setup_gl();
  get_matrices();
  win->gl->flush();
}

void fltkdisplay::update_view_left(double d)
{
  if (d==0.)
  { 
    for (int i=0;i<3;++i) d+=(look[i]-vp[i])*(look[i]-vp[i]);
    d=sqrt(d);
  }
  const double nup[3]={0.,0.,1.};
  const double nvp[3]={look[0]-d,look[1],look[2]};
  set_viewpoint(focal,fov,nearclip,farclip,nvp,look,nup);
  setup_gl();
  get_matrices();
  win->gl->flush();
}

void fltkdisplay::update_view_front(double d)
{
  if (d==0.)
  { 
    for (int i=0;i<3;++i) d+=(look[i]-vp[i])*(look[i]-vp[i]);
    d=sqrt(d);
  }
  const double nup[3]={0.,0.,1.};
  const double nvp[3]={look[0],look[1]-d,look[2]};
  set_viewpoint(focal,fov,nearclip,farclip,nvp,look,nup);
  setup_gl();
  get_matrices();
  win->gl->flush();
}

void fltkdisplay::update_view_back(double d)
{
  if (d==0.)
  { 
    for (int i=0;i<3;++i) d+=(look[i]-vp[i])*(look[i]-vp[i]);
    d=sqrt(d);
  }
  const double nup[3]={0.,0.,1.};
  const double nvp[3]={look[0],look[1]+d,look[2]};
  set_viewpoint(focal,fov,nearclip,farclip,nvp,look,nup);
  setup_gl();
  get_matrices();
  win->gl->flush();
}

void fltkdisplay::update_view_top(double d)
{
  if (d==0.)
  { 
    for (int i=0;i<3;++i) d+=(look[i]-vp[i])*(look[i]-vp[i]);
    d=sqrt(d);
  }
  const double nup[3]={0.,1.,0.};
  const double nvp[3]={look[0],look[1],look[2]+d};
  set_viewpoint(focal,fov,nearclip,farclip,nvp,look,nup);
  setup_gl();
  get_matrices();
  win->gl->flush();
}

void fltkdisplay::update_view_bottom(double d)
{
  if (d==0.)
  { 
    for (int i=0;i<3;++i) d+=(look[i]-vp[i])*(look[i]-vp[i]);
    d=sqrt(d);
  }
  const double nup[3]={0.,-1.,0.};
  const double nvp[3]={look[0],look[1],look[2]-d};
  set_viewpoint(focal,fov,nearclip,farclip,nvp,look,nup);
  setup_gl();
  get_matrices();
  win->gl->flush();
}

void fltkdisplay::update_view_X(bool swap)
{
  double d=0; for (int i=0;i<3;++i) d+=(look[i]-vp[i])*(look[i]-vp[i]);
  d=sqrt(d);
  if ((vp[0]==look[0]+d)&& (up[2]==1.0)&& swap)
    update_view_left(d);
  else
    update_view_right(d);
}

void fltkdisplay::update_view_Y(bool swap)
{
  double d=0; for (int i=0;i<3;++i) d+=(look[i]-vp[i])*(look[i]-vp[i]);
  d=sqrt(d);
  const double nup[3]={0.,0.,1.};
  if ((vp[1]==look[1]-d)&& (up[2]==1.0)&& swap)
    update_view_back(d);
  else
    update_view_front(d);
}

void fltkdisplay::update_view_Z(bool swap)
{
  double d=0; for (int i=0;i<3;++i) d+=(look[i]-vp[i])*(look[i]-vp[i]);
  d=sqrt(d);
  if ((vp[2]==look[2]+d)&& (up[1]==1.0)&& swap)
  update_view_bottom(d);
  else
    update_view_top(d);
}


void fltkdisplay::redraw()
{
  get_matrices();
  win->gl->flush();
}

void fltkdisplay::reset_view()
{
  compute_bb();
  double focal=2.8;
  double fov =  (atan(1/focal) ) * 2 * 180 / 3.14159265;
  npoint3 center=(llf+urb)/2.0;
  npoint3 size=urb-llf;
  double sz=size.norm();
  double nearclip=sz/2;
  double farclip=sz*2;
//  double size2=sqrt(size[0]*size[0]+size[1]*size[1]);
  const double look[3]={center[0],center[1],center[2]};
  const double vp[3]={center[0],center[1],center[2]+sz*1.5};
  const double up[3]={0.,1.,0.};
  set_viewpoint(focal,fov,nearclip,farclip,vp,look,up);
  setup_gl();
  get_matrices();
  win->gl->flush();
}


void fltkdisplay::update_bb()
{
  compute_bb();
  setup_gl();
  get_matrices();
  win->gl->flush();
}

void fltkdisplay::init_data(const data_container &data_)
{
  std::vector<data_container>*data__=new std::vector<data_container>;
  data__->push_back(data_);
  #ifdef USE_DLISTS
    Dlists.clear();
    Dlists.push_back(0);
    Dlist=0;
  #endif
  data=data__;
  data2=data__;
  reset_bb();
  compute_bb();
  up_to_date=false;
  draw_model();

//  std::cout << " error def list " <<  glGetError() << std::endl;
//  if (glIsList(Dlist)) std::cout << "is gllist "; else std::cout << "not gllist " ;std::cout << std::endl;
}

void fltkdisplay::init_data()
{
  std::vector<data_container>*data__=new std::vector<data_container>;
  data__->push_back(data_container());
  #ifdef USE_DLISTS
    Dlists.clear();
    Dlists.push_back(0);
    Dlist=0;
  #endif
  data=data__;
  data2=data__;
  reset_bb();
  compute_bb();
  up_to_date=false;
  draw_model();
}

void fltkdisplay::init_data(const std::vector< data_container >& datas_)
{
  data=&datas_;
  data2=NULL;
  #ifdef USE_DLISTS
    Dlists.resize(datas_.size());
    std::fill(Dlists.begin(), Dlists.end(), 0);
    Dlist=0;
  #endif
  reset_bb();
  compute_bb();
  up_to_date=false;
  draw_model();
}

void fltkdisplay::save(const char *fname)
{
  std::ofstream file(fname);
  ndisplaytofile df(get_backgroundcolor(),get_name(),file);
  df.init_data(*get_data_all());
  color c=get_backgroundcolor();
  file << "background " << (int)c.R << " " << (int)c.G << " " << (int)c.B << " " << (int)c.A << std::endl;
  df.display();
  double focal,fov,nearclip,farclip,vp[3],look[3],up[3];
  get_viewpoint(focal,fov,nearclip,farclip,vp,look,up);
  file << "view " << 2.0 << " " << focal << " " <<fov << " " << nearclip << " " << farclip << std::endl;
  for (int i=0;i<3;++i) file << vp[i] << " " ; file << std::endl;
  for (int i=0;i<3;++i) file << look[i] << " " ; file << std::endl;
  for (int i=0;i<3;++i) file << up[i] << " " ; file << std::endl;
  file.close();
}

void fltkdisplay::load(const char *fname)
{
  std::ifstream file(fname);
  std::string buf;
  int ndata;
  int R,G,B,A;
  file >> buf;
  file >> R >> G >> B >> A ;
  file >> buf;
  file >> ndata;
  std::vector<data_container> *data_;
  data_=new std::vector<data_container>(ndata);
  bool err=false;
  for(int i=0;i<ndata;++i)
  {
    if ((*data_)[i].read(file))
    {
      err=true;
      break;
    }
  }
  if (err)
  {
    delete data;
  }
  else
  {
    c=color(R,G,B,A);
    if (data2) delete data2;
    data=data_;
    data2=data_; // own it
    setcallbacks(0x0);// cannot use callbacks in general because links with original objects are broken
    set_color(c);
    double focal,fov,nearclip,farclip,vp[3],look[3],up[3];
    bool err=false;
    if (file >> buf >> buf >> focal >> fov >> nearclip >> farclip)
    {
      for (int i=0;i<3;++i) if (!(file >> vp[i]))  {err=true;break;}
      for (int i=0;i<3;++i) if (!(file >> look[i])){err=true;break;}
      for (int i=0;i<3;++i) if (!(file >> up[i]))  {err=true;break;}
    }
    else err=true;
    compute_bb();
    reset_view();
    up_to_date=false;
    
    if (!err)
      set_viewpoint(focal,fov,nearclip,farclip,vp,look,up);
    setup_gl();
    #ifdef USE_DLISTS
      Dlists.resize(data->size());
      std::fill(Dlists.begin(), Dlists.end(), 0);
      Dlist=0;
    #endif
    win->gl->redraw();
  }
  file.close();
}


int fltkdisplay::pick(int x, int y, std::vector<pickinfo>& picks, int szx, int szy)
{
  int nb_obj=1000;//getnumpick();
  int size=nb_obj*8+10;
  GLuint *sel = new GLuint[size];
  glSelectBuffer(size, sel);
  glRenderMode(GL_SELECT);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  setup_gl(DRAW_PICK,x,y,szx,szy);
  draw_model_pick();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  GLint numhits = glRenderMode(GL_RENDER);

// Here, GL names have three to six levels:
// the 0 to 3 first are user ids, then the three following are local ids 
// first is the storage segment number
// second is the type of the entity (1 for point, 2 for edge, 3 for triangle 4 for quad
// and the last is the entity number in the data storage.
  
  unsigned int hi = 0;
  GLuint *bufp = sel;
  GLuint name, numnames, zmin, zmax;
  if (numhits<0) // overflow : too many picks
  {
    delete[] sel;
    return -1;
  }
  for(unsigned int j = 0; j < numhits; j++)
  {
    numnames = *bufp++;
    zmin = *bufp++;
    zmax = *bufp++;
    GLuint z=zmax;
    if (numnames>=3&&numnames<=6)
    {
      pickinfo p;
      p.nb_uid=numnames-3;
      p.z=z;
      for (int i=0;i<p.nb_uid;++i)
        p.uids[i]=*bufp++;
      p.seg=*bufp++;
      p.type=*bufp++;
      p.id=*bufp++;
      picks.push_back(p);
    }
    else
    {
      while(numnames--)  // skip go to next record
      {
        bufp++;
      }
    }
  }
  delete[] sel;
  std::sort(picks.begin(),picks.end()); // closest to eye in front.
  return picks.size();
}


bool fltkdisplay::get_coords ( GLfloat i,GLfloat j, npoint3 &p,npoint3 &v,GLfloat &z)
{
  npoint3 p2;
  bool object=true;
  z = 0.0f;
  glReadBuffer(GL_BACK);

  glReadPixels(i, j, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z);
/*  GLint zzz =0;
  GLint bits;
  glReadPixels(i, j, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, &zzz);
  glGetIntegerv(GL_DEPTH_BITS, &bits);
  GLdouble zNorm = z ;// 2 * z - 1;
  GLdouble zView = 2 * nearclip * farclip / ((farclip - nearclip) * zNorm - nearclip - farclip);
  
  
  std::cout << bits << "bits zview="<< zView << " z=" << z  << " zi=" << zzz << std::endl;*/
  if (z==1.0f) // no object or out of frustrum : we take the point at which we look to as reference distance
  {
    object=false;
    double finalMatrix[16];
    double xpp,ypp,zz;
    _gluMultMatricesd ( ( GLdouble * )ModelViewMatrix, ( GLdouble * )ProjectionMatrix,( GLdouble * ) finalMatrix );
    _gluProjectFast(look[0],look[1],look[2],finalMatrix,( GLint * )Viewport,&xpp,&ypp,&zz);
    z=zz;
  }
  _gluUnProjectFast ( i*1.0,j*1.0,z, ( GLdouble* ) Inv, ( GLint * ) Viewport,&p[0], &p[1], &p[2] );
  _gluUnProjectFast ( i*1.0,j*1.0,0.0, ( GLdouble* ) Inv, ( GLint * ) Viewport,&p2[0], &p2[1], &p2[2]);
  v=p-p2;
  v.normalize();
/*  if (z==1.0f) // no object or out of frustrum
  {
    p=p;
  }*/
  return object; // object found or not
}

bool fltkdisplay::get_coords_with_z ( GLfloat i,GLfloat j, GLfloat z,npoint3 &p,npoint3 &v)
{
  npoint3 p2;
  bool object=true;
  if (z==1.0f) // no object or out of frustrum : we take the point at which we look to as reference distance
  {
    object=false;
    double finalMatrix[16];
    double xpp,ypp,zz;
    _gluMultMatricesd ( ( GLdouble * )ModelViewMatrix, ( GLdouble * )ProjectionMatrix,( GLdouble * ) finalMatrix );
    _gluProjectFast(look[0],look[1],look[2],finalMatrix,( GLint * )Viewport,&xpp,&ypp,&zz);
    z=zz;
  }
  _gluUnProjectFast ( i*1.0,j*1.0,z, ( GLdouble* ) Inv, ( GLint * ) Viewport,&p[0], &p[1], &p[2] );
  _gluUnProjectFast ( i*1.0,j*1.0,0.0, ( GLdouble* ) Inv, ( GLint * ) Viewport,&p2[0], &p2[1], &p2[2]);
  v=p-p2;
  v.normalize();
/*  if (z==1.0f) // no object or out of frustrum
  {
    p=p;
  }*/
  return object; // object found or not
}

bool fltkdisplay::get_coords ( GLfloat di,GLfloat dj,GLfloat z_, npoint3 &p)
{
  npoint3 p2;
  _gluUnProjectFast ( di*1.0,dj*1.0,z_, ( GLdouble* ) Inv, ( GLint * ) Viewport,&p2[0], &p2[1], &p2[2]);
  p=p2;
  return true;
}

void fltkdisplay::get_matrices ( void )
{
  glGetDoublev ( GL_MODELVIEW_MATRIX, ( GLdouble * ) ModelViewMatrix );
  glGetDoublev ( GL_PROJECTION_MATRIX, ( GLdouble * ) ProjectionMatrix );
  glGetIntegerv ( GL_VIEWPORT, ( GLint * ) Viewport );
  _gluMultMatricesd ( ( GLdouble * ) ModelViewMatrix, ( GLdouble * )  ProjectionMatrix, ( GLdouble * ) Inv );
  _gluInvertMatrixd ( ( GLdouble * ) Inv, ( GLdouble * ) Inv );
}

void fltkdisplay::set_viewpoint (double focal_, double fov_,double nearclip_,double farclip_,const double vp_[3],const double look_[3],const double up_[3])
{
  focal=focal_,fov=fov_,nearclip=nearclip_,farclip=farclip_;
  for (int i=0;i<3;++i) {vp[i]=vp_[i];look[i]=look_[i];up[i]=up_[i];}
  update_view();
}

void fltkdisplay::get_viewpoint (double &focal_, double &fov_,double &nearclip_,double &farclip_,double vp_[3],double look_[3],double up_[3])
{
  focal_=focal,fov_=fov,nearclip_=nearclip,farclip_=farclip;
  for (int i=0;i<3;++i) {vp_[i]=vp[i];look_[i]=look[i];up_[i]=up[i];}
}

void fltkdisplay::resetup_gl (void)
{
  
  glEnable ( GL_DEPTH_TEST );
  glEnable( GL_BLEND ); 
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
  glPolygonMode ( GL_FRONT_AND_BACK,GL_FILL);
  if (AA)
  {
    glEnable(GL_POLYGON_SMOOTH);
    glHint(GL_POLYGON_SMOOTH_HINT, GL_LINEAR);
    glEnable(GL_MULTISAMPLE);
    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_LINEAR);
    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_LINEAR);
  }
  
  glEnable(GL_COLOR_MATERIAL);
  glEnable(GL_LIGHTING);
  add_lights();
}

void fltkdisplay::setup_gl (int mode,int i,int j,int di,int dj)
{
//  glDepthFunc ( GL_LESS );
  glEnable ( GL_DEPTH_TEST );
  glEnable( GL_BLEND ); 
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
  glPolygonMode ( GL_FRONT_AND_BACK,GL_FILL);
  glLoadIdentity();
  glViewport ( 0,0,win->gl->w(),win->gl->h() );
  glOrtho ( 0,win->gl->w(),0,win->gl->h(),0,1 );
  // couleur de fond (RGBT)
  glClearColor ( c.R/255., c.G/255., c.B/255.,c.A/255.);
  glClearAccum(0.0,0.0,0.0,0.0);
  // Remplissage
//  glShadeModel ( GL_FLAT);
  glShadeModel ( GL_SMOOTH );

  glMatrixMode ( GL_PROJECTION );
//  glPushMatrix();
  glLoadIdentity();
  
  if (mode==DRAW_PICK)
  {
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT,viewport);
    gluPickMatrix(i, j,
                   di,dj, viewport);
  }
  _gluPerspective ( fov, ( GLdouble ) win->gl->w() / ( GLdouble ) win->gl->h(),
                      nearclip, farclip );
  glMatrixMode ( GL_MODELVIEW );
//  glPushMatrix();
  glLoadIdentity();
  _gluLookAt ( vp,
                  look,    /* lookat point */
                  up );  /* up is in +ive y */


  if (AA)
  {
    glEnable(GL_POLYGON_SMOOTH);
    glHint(GL_POLYGON_SMOOTH_HINT, GL_LINEAR);
    glEnable(GL_MULTISAMPLE);
    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_LINEAR);
    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_LINEAR);
  }
  
  glEnable(GL_COLOR_MATERIAL);
  glEnable(GL_LIGHTING);
  add_lights();
}

void fltkdisplay::add_lights ( void )
{
  glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
  GLfloat light_ambient[] =
  {0.0, 0.0, 0.0, 0.0};
  GLfloat light_diffuse[] =
  {1.0, 1.0, 1.0, 1.0};
  GLfloat light_specular[] =
  {0.0, 0.0, 0.0, 1.0};
  GLfloat light_position[] =
  {(GLfloat)(vp[0]-look[0]),(GLfloat)(vp[1]-look[1]),(GLfloat)(vp[2]-look[2]), 0.0};
  glLightfv ( GL_LIGHT0, GL_AMBIENT, light_ambient );
  glLightfv ( GL_LIGHT0, GL_DIFFUSE, light_diffuse );
  glLightfv ( GL_LIGHT0, GL_SPECULAR, light_specular );
  glLightfv ( GL_LIGHT0, GL_POSITION, light_position );
  glEnable ( GL_LIGHT0);
}

void fltkdisplay::buildMenus(const Fl_Menu_Item *menuitems_custom,void * ptr)
{
  win->buildMenus(menuitems_custom,ptr);
}
