#include <Rcpp.h>
// Copyright (c) 1997-2002  Max-Planck-Institute Saarbruecken (Germany).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v5.6.1/Nef_S2/include/CGAL/Nef_S2/Sphere_geometry_OGL.h $
// $Id: Sphere_geometry_OGL.h 521c72d 2021-10-04T13:22:00+02:00 Mael Rouxel-Labbé
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Michael Seel  <seel@mpi-sb.mpg.de>

#ifndef CGAL_SPHERE_GEOMETRY_OGL_H
#define CGAL_SPHERE_GEOMETRY_OGL_H

#include <CGAL/license/Nef_S2.h>


#include <CGAL/Nef_S2/OGL_base_object.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Nef_S2/Sphere_geometry.h>
#include <CGAL/Nef_S2/SM_triangulator.h>
#include <CGAL/IO/Color.h>
#include <qgl.h>
#include <CGAL/glu.h>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <vector>

#undef CGAL_NEF_DEBUG
#define CGAL_NEF_DEBUG 151
#include <CGAL/Nef_2/debug.h>

#define CGAL_NEF_LGREY CGAL::IO::Color(170,170,200)
#define CGAL_NEF_DGREY CGAL::IO::Color(30,30,50)

namespace CGAL {
namespace OGL {

struct Gen_object {
  Gen_object() {}
  virtual ~Gen_object() {}
  virtual void draw() const {}
  virtual Gen_object* clone() const { return 0; }
  virtual void print() const {}
};

typedef CGAL::Simple_cartesian<double>      VKernel;
typedef VKernel::Vector_3                   VVector;
typedef VKernel::Point_3                    VPoint;
typedef VKernel::Aff_transformation_3       VTrafo;
typedef VKernel::Aff_transformation_3       Affine_3;
typedef std::vector<VPoint>                 VSegment;
typedef VKernel::Triangle_3                 DTriangle;
typedef std::vector<DTriangle>              VTriangle;

template <typename R>
VVector convert(const CGAL::Vector_3<R>& v)
{ return VVector(CGAL::to_double(v.x()),
                 CGAL::to_double(v.y()),
                 CGAL::to_double(v.z())); }

template <typename R>
VVector normalize_and_convert(const CGAL::Vector_3<R>& v)
{
  typename R::FT xa = CGAL::abs(v.x());
  typename R::FT ya = CGAL::abs(v.y());
  typename R::FT za = CGAL::abs(v.z());
  typename R::FT m = (std::max)((std::max)(xa,ya),za);
  if (m==0) {
      return VVector(0,0,0);
  } else {
      double xd = CGAL::to_double(v.x()/m);
      double yd = CGAL::to_double(v.y()/m);
      double zd = CGAL::to_double(v.z()/m);
      VVector u(xd,yd,zd);
      return u / CGAL_NTS sqrt(u*u) ; // normalize
  }
}

const double refinement_angle = 0.1;
const double shrink_fac = 0.995;

template <typename R>
class Approximator {

 public:
  static VPoint approximate(const CGAL::Sphere_point<R>& p) {
    VVector v = normalize_and_convert(p-CGAL::ORIGIN);
    return CGAL::ORIGIN+v;
  }

  static VSegment approximate(const CGAL::Sphere_segment<R>& s) {

    /* we construct the rotation matrix that transfers the x-axis
       into |ps_|, the z-axis into h_.orthogonal_vector() and the
       y-axix into the corresponding crossproduct of the two.*/
    if ( s.is_degenerate() ) {
      VSegment S(1);
      S[0] = approximate(s.source());
      return S;
    }

    VVector v0 = convert(s.source()-CGAL::ORIGIN);
    VVector v2 = convert(s.sphere_circle().orthogonal_vector());
    VVector v1(-cross_product(v0,v2));
    VVector v3 = convert(s.target()-CGAL::ORIGIN);
    double v0l = CGAL_NTS sqrt(v0*v0);
    double v1l = CGAL_NTS sqrt(v1*v1);
    double v2l = CGAL_NTS sqrt(v2*v2);
    double v3l = CGAL_NTS sqrt(v3*v3);
    double cosalpha = v0*v3 / v0l / v3l;
    double alpha = std::acos(cosalpha);
    const int units_per_halfcircle = 50;
    int units = int(units_per_halfcircle/CGAL_PI * alpha);
    if (units == 0) ++units;
    bool seg_is_short = s.is_short();
    bool seg_is_halfcircle = s.is_halfcircle();
    if ( seg_is_halfcircle ) units = units_per_halfcircle;
    else if ( !seg_is_short ) {
      units = 2*units_per_halfcircle - (units+1);
    } CGAL_NEF_TRACEV(units); CGAL_NEF_TRACEV(cosalpha); CGAL_NEF_TRACEV(alpha);

    v0 = v0 / v0l;
    v1 = v1 / v1l;
    v2 = v2 / v2l;
    v3 = v3 / v3l;
    VTrafo T(v0.x(),v1.x(),v2.x(),
             v0.y(),v1.y(),v2.y(),
             v0.z(),v1.z(),v2.z());
    VSegment S(units+1);
    for (int i=0; i<units; ++i)
      S[i] = VPoint(std::cos(CGAL_PI*i/double(units_per_halfcircle)),
                    std::sin(CGAL_PI*i/double(units_per_halfcircle)),
                    0.0);
    double sinalpha = 1 - cosalpha*cosalpha;
    if (sinalpha <0) sinalpha = 0;
    else             sinalpha = CGAL_NTS sqrt(sinalpha);
    if ( seg_is_short )
      S[units] = VPoint(cosalpha, sinalpha, 0);
    else
      S[units] = VPoint(cosalpha, -sinalpha, 0);
    VSegment::iterator it;
    for(it = S.begin(); it != S.end(); ++it) { CGAL_NEF_TRACEN(*it<<" "<<T(*it));
    *it = T(*it);
    } CGAL_NEF_TRACEN("");
    return S;
  }

  static VSegment approximate(const CGAL::Sphere_circle<R>& s) {

    /* we construct the rotation matrix that transfers the x-axis
       into |ps_|, the z-axis into h_.orthogonal_vector() and the
       y-axix into the corresponding crossproduct of the two.*/

    VVector v0 = convert(s.base1());
    VVector v1 = convert(s.base2());
    VVector v2 = convert(s.orthogonal_vector());
    double v0l = CGAL_NTS sqrt(v0*v0);
    double v1l = CGAL_NTS sqrt(v1*v1);
    double v2l = CGAL_NTS sqrt(v2*v2);
    const int units = 100;
    v0 = v0 / v0l;
    v1 = v1 / v1l;
    v2 = v2 / v2l;
    VTrafo T(v0.x(),v1.x(),v2.x(),
             v0.y(),v1.y(),v2.y(),
             v0.z(),v1.z(),v2.z());
    VSegment S(units);
    for (int i=0; i<units; ++i) {
      S[i] = VPoint(std::cos(2.0*CGAL_PI*i/double(units)),
                    std::sin(2.0*CGAL_PI*i/double(units)),
                    0.0);
    }
    VSegment::iterator it;
    for(it = S.begin(); it != S.end(); ++it) *it = T(*it);
    return S;
  }


/* the following operation refines a sphere triangle as a list of flat
   triangles in 3d. The refinement only works for triangles that are
   contained in a perfect hemisphere (no long sphere segments are
   allowed as triangle segments). We split triangles along at the
   midpoint of their longest side into two. */

  static void refine(const DTriangle& t, VTriangle& T) {
    double angle[3]; int i(0);
    angle[0] = std::acos((t[0]-CGAL::ORIGIN)*(t[1]-CGAL::ORIGIN));
    angle[1] = std::acos((t[1]-CGAL::ORIGIN)*(t[2]-CGAL::ORIGIN));
    angle[2] = std::acos((t[2]-CGAL::ORIGIN)*(t[0]-CGAL::ORIGIN));
    CGAL_NEF_TRACEN("refine "<<angle[0]<<" "<<angle[1]<<" "<<angle[2]);
    if ( angle[1] > angle[0] ) {
      if ( angle[2] > angle[1] ) i=2;
      else                       i=1;
    } else { // angle[0] >= angle[1]
      if ( angle[2] > angle[0] ) i=2;
      else                       i=0;
    }
    // now i references the side of maximal angle
    if ( angle[i] < refinement_angle ) // refinement threshold
      { T.push_back(t); return; }
    VVector v;
    switch (i) {
    case 0: v = (t[0]-CGAL::ORIGIN)+(t[1]-CGAL::ORIGIN); break;
    case 1: v = (t[1]-CGAL::ORIGIN)+(t[2]-CGAL::ORIGIN); break;
    case 2: v = (t[2]-CGAL::ORIGIN)+(t[0]-CGAL::ORIGIN); break;
    }
    v = v / CGAL_NTS sqrt(v*v) ; // normalize
    VPoint p = CGAL::ORIGIN+v;
    DTriangle t1,t2;
    switch (i) {
    case 0: t1=DTriangle(t[0],p,t[2]); t2=DTriangle(p,t[1],t[2]); break;
    case 1: t1=DTriangle(t[1],p,t[0]); t2=DTriangle(p,t[2],t[0]); break;
    case 2: t1=DTriangle(t[2],p,t[1]); t2=DTriangle(p,t[0],t[1]); break;
    }
    refine(t1,T);
    refine(t2,T);
  }

  static VTriangle approximate(const CGAL::Sphere_triangle<R>& t) {
    // we subdivide the triangle into a list of triangles until
    // we reach a fine resolution on the surface.

    VTriangle T;
    DTriangle td(approximate(t.point(0)),
                 approximate(t.point(1)),
                 approximate(t.point(2)));
    CGAL_NEF_TRACEN("approximate " << td);
    refine(td,T);
    return T;
  }

};


template <class R_>
class Sphere_point : public VPoint, public Gen_object {
  typedef R_ R;
  CGAL::Sphere_point<R> p_;
  CGAL::IO::Color           c_;
  unsigned              w_;
public:
  Sphere_point() {}
  Sphere_point(const CGAL::Sphere_point<R>& p,
    CGAL::IO::Color c = CGAL::black(), unsigned w = 10) :
    VPoint(Approximator<R>::approximate(p)), p_(p), c_(c), w_(w) {}
  Sphere_point(const Sphere_point<R>& p) : VPoint(p), Gen_object()
  { p_ = p.p_; c_ = p.c_; w_ = p.w_; }
  Sphere_point<R>& operator=(const Sphere_point<R>& p)
  { VPoint::operator=(p); p_ = p.p_;  c_ = p.c_; w_ = p.w_;
    return *this; }

  virtual ~Sphere_point() {}

  const CGAL::Sphere_point<R>& original() const
  { return p_; }

  virtual Gen_object* clone() const
  { return new Sphere_point<R>(*this); }

  virtual void draw() const {
    glPointSize(w_);
    glColor3ub(c_.red(),c_.green(),c_.blue());
    glBegin(GL_POINTS);
    glNormal3d(x(),y(),z());
    glVertex3d(x(),y(),z());
    glEnd();
  }

  virtual void print() const
  { Rcpp::Rcerr << "point " << p_; }

};





template <class R_>
class Sphere_segment : public VSegment, public Gen_object {
  typedef R_ R;
  CGAL::Sphere_segment<R> s_;
  CGAL::IO::Color             c_;
  unsigned                w_;
public:
  Sphere_segment() {}
  Sphere_segment(const CGAL::Sphere_segment<R>& s,
    CGAL::IO::Color c = CGAL::black(), unsigned w = 2)
    : VSegment(Approximator<R>::approximate(s)), s_(s), c_(c), w_(w) {}
  Sphere_segment(const Sphere_segment<R>& s) : VSegment(s), Gen_object()
  { s_ = s.s_; c_ = s.c_; w_ = s.w_; }
  Sphere_segment<R>& operator=(const Sphere_segment<R>& s)
  { VSegment::operator=(s); s_ = s.s_; c_ = s.c_; w_ = s.w_;
    return *this; }
  virtual ~Sphere_segment() {}

  const CGAL::Sphere_segment<R>& original() const
  { return s_; }

  virtual Gen_object* clone() const
  { return new Sphere_segment<R>(*this); }

  virtual void draw() const
  { CGAL_NEF_TRACEN("draw "<<s_);
    if ( size() == 1 ) {
      glPointSize(5*w_);
      glColor3ub(c_.red(),c_.green(),c_.blue());
      glBegin(GL_POINTS);
      glNormal3d(begin()->x(),begin()->y(),begin()->z());
      glVertex3d(begin()->x(),begin()->y(),begin()->z());
      glEnd();
    } else {
      glLineWidth(w_);
      glColor3ub(c_.red(),c_.green(),c_.blue());
      glBegin(GL_LINE_STRIP);
      VSegment::const_iterator it;
      for(it = begin(); it != end(); ++it) {
        glNormal3d(it->x(),it->y(),it->z());
        glVertex3d(it->x(),it->y(),it->z());
      }
      glEnd();
    }
  }

  virtual void print() const
  { Rcpp::Rcerr << "segment " << s_; }

};



template <class R_>
class Sphere_circle : public VSegment, public Gen_object {
  typedef R_ R;
  CGAL::Sphere_circle<R> s_;
  CGAL::IO::Color            c_;
  unsigned               w_;
public:
  Sphere_circle() {}
  Sphere_circle(const CGAL::Sphere_circle<R>& s,
    CGAL::IO::Color c = CGAL::black(), unsigned w = 2)
    : VSegment(Approximator<R>::approximate(s)), s_(s), c_(c), w_(w) {}
  Sphere_circle(const Sphere_circle<R>& s) : VSegment(s), Gen_object()
  { s_ = s.s_; c_ = s.c_; w_ = s.w_; }
  Sphere_circle<R>& operator=(const Sphere_circle<R>& s)
  { VSegment::operator=(s); s_ = s.s_; c_ = s.c_; w_ = s.w_;
    return *this; }
  virtual ~Sphere_circle() {}

  const CGAL::Sphere_circle<R>& original() const
  { return s_; }

  virtual Gen_object* clone() const
  { return new Sphere_circle<R>(*this); }

  virtual void draw() const
  { CGAL_NEF_TRACEN("draw "<<s_);
    glLineWidth(w_);
    glColor3ub(c_.red(),c_.green(),c_.blue());
    glBegin(GL_LINE_LOOP);
    VSegment::const_iterator it;
    for(it = begin(); it != end(); ++it) {
      glNormal3d(it->x(),it->y(),it->z());
      glVertex3d(it->x(),it->y(),it->z());
    }
    glEnd();
  }

  virtual void print() const
  { Rcpp::Rcerr << "circle " << s_; }

};


/* The following class approximates a spherical triangle by a list
   of flat triangles */

template <class R_>
class Sphere_triangle : public VTriangle, public Gen_object {
  typedef R_ R;
  CGAL::Sphere_triangle<R> t_;
  CGAL::IO::Color              c_;
public:
  Sphere_triangle() {}

  Sphere_triangle(const CGAL::Sphere_triangle<R>& t,
    CGAL::IO::Color c = CGAL::IO::Color(100,100,120))
    : VTriangle(Approximator<R>::approximate(t)), t_(t), c_(c) {}

  Sphere_triangle(const Sphere_triangle<R>& t) : VTriangle(t), Gen_object()
  { t_ = t.t_; c_ = t.c_; }

  Sphere_triangle<R>& operator=(const Sphere_triangle<R>& t)
  { VTriangle::operator=(t); t_ = t.t_; c_ = t.c_; return *this; }

  virtual ~Sphere_triangle() {}

  const CGAL::Sphere_triangle<R>& original() const
  { return t_; }

  virtual Gen_object* clone() const
  { return new Sphere_triangle<R>(*this); }

  virtual void draw() const {
    CGAL_NEF_TRACEN("draw "<<t_ << " in " << c_);
    VTriangle::const_iterator it;
    VPoint p;
    glColorMaterial(GL_FRONT, GL_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
    glColor3ub(c_.red(),c_.green(),c_.blue());
    glBegin(GL_TRIANGLES);
    for(it = begin(); it != end(); ++it) {
      p = it->vertex(0);
      glNormal3d(p.x(),p.y(),p.z());         //glVertex3d(p.x(),p.y(),p.z());
      glVertex3d(shrink_fac*p.x(),shrink_fac*p.y(),shrink_fac*p.z());
      p = it->vertex(1);
      glNormal3d(p.x(),p.y(),p.z());
      glVertex3d(shrink_fac*p.x(),shrink_fac*p.y(),shrink_fac*p.z());
      p = it->vertex(2);
      glNormal3d(p.x(),p.y(),p.z());
      glVertex3d(shrink_fac*p.x(),shrink_fac*p.y(),shrink_fac*p.z());
    }
    glEnd();
    glDisable(GL_COLOR_MATERIAL);
  }

  virtual void print() const
  { Rcpp::Rcerr << "triangle " << t_; }

};

//----------------------------------------------------------------------------
// the sphere:
//----------------------------------------------------------------------------

 enum { SM_FACES, SM_SKELETON, SM_TRIANGULATION };
 enum { SM_AXES, SM_CUBE };

class Unit_sphere : public OGL_base_object {
  typedef std::list<Gen_object*> Object_list;
  typedef Object_list::const_iterator  Object_const_iterator;
  typedef Object_list::iterator  Object_iterator;

  GLUquadricObj*        sphere_;
  int                   style_;
  bool                  initialized_;
  Object_list           objects_,triangles_,triangle_edges_;
  GLuint                sphere_list_;
  std::vector<bool>     switches;

public:
void init() {
  style_ = SM_FACES;
  switches[0] = true;
  switches[1] = false;
  gluQuadricNormals(sphere_,GLenum(GLU_SMOOTH));
}

Unit_sphere() : switches(2) {
  sphere_ = gluNewQuadric();
  initialized_ = false;
  init();
}

void clear_list()
{ while ( objects_.begin() != objects_.end() ) {
    delete (*objects_.begin());
    objects_.pop_front();
  }
  while ( triangles_.begin() != triangles_.end() ) {
    delete (*triangles_.begin());
    triangles_.pop_front();
  }
  while ( triangle_edges_.begin() != triangle_edges_.end() ) {
    delete (*triangle_edges_.begin());
    triangle_edges_.pop_front();
  }
}

void copy_list(const Unit_sphere& S)
{ Object_const_iterator it;
  CGAL_forall_iterators (it,S.objects_)
    objects_.push_back( (*it)->clone() );
  CGAL_forall_iterators (it,S.triangles_)
    triangles_.push_back( (*it)->clone() );
  CGAL_forall_iterators (it,S.triangle_edges_)
    triangle_edges_.push_back( (*it)->clone() );
}

void print() const
{ Rcpp::Rcerr << "Dumping Unit_sphere:\n";
  for (Object_const_iterator it = objects_.begin();
       it != objects_.end(); ++it)
     (*it)->print();
  Rcpp::Rcerr << std::endl;
  for (Object_const_iterator it = triangles_.begin();
       it != triangles_.end(); ++it)
     (*it)->print();
  Rcpp::Rcerr << std::endl;
}

Unit_sphere(const Unit_sphere& S) : OGL_base_object(), switches(2)
{ CGAL_NEF_TRACEN("copyconstruction");
  sphere_ = gluNewQuadric();
  initialized_ = S.initialized_;
  style_ = S.style_;
  switches[0] = S.switches[0];
  switches[1] = S.switches[1];
  copy_list(S);
}


Unit_sphere& operator=(const Unit_sphere& S)
{ CGAL_NEF_TRACEN("assignment");
  initialized_ = S.initialized_;
  style_ = S.style_;
  switches[0] = S.switches[0];
  switches[1] = S.switches[1];
  clear_list(); copy_list(S);
  return *this;
}

~Unit_sphere() {
  clear_list();
  gluDeleteQuadric(sphere_);
}

template <typename R>
void push_back(const CGAL::Sphere_point<R>& p,
  CGAL::IO::Color c = CGAL::IO::yellow(), unsigned w = 5)
{ objects_.push_back(new Sphere_point<R>(p,c,w)); }

template <typename R>
void push_back(const CGAL::Sphere_segment<R>& s,
  CGAL::IO::Color c = CGAL::IO::black(), unsigned w = 1)
{ objects_.push_back(new Sphere_segment<R>(s,c,w)); }

template <typename R>
void push_back(const CGAL::Sphere_circle<R>& s,
  CGAL::IO::Color c = CGAL::IO::black(), unsigned w = 1)
{ objects_.push_back(new Sphere_circle<R>(s,c,w)); }

template <typename R>
void push_back(const CGAL::Sphere_triangle<R>& t,
  CGAL::IO::Color c = CGAL::IO::white())
{ triangles_.push_back(new Sphere_triangle<R>(t,c)); }

template <typename R>
void push_back_triangle_edge(const CGAL::Sphere_segment<R>& s,
  CGAL::IO::Color c = CGAL::IO::blue(), unsigned w = 1)
{ triangle_edges_.push_back(new Sphere_segment<R>(s,c,w)); }

void set_style(int style) {
  style_ = style;
}

void toggle(int index) {
  switches[index] = !switches[index];
}
private:

void construct_axes() const
{ int i;
  double f(1.02);
  glNewList(sphere_list_+3, GL_COMPILE);
  glLineWidth(2.0);
  // red x-axis and equator
  glColor3f(1.0,0.0,0.0);
  glBegin(GL_LINES);
  glVertex3d(0.0,0.0,0.0);
  glVertex3d(1.2,0.0,0.0);
  glEnd();
  glBegin(GL_LINE_LOOP);
  for(i=0;i<100;i++)
    glVertex3d(f*std::cos(2.0*CGAL_PI*i/100.0),f*std::sin(2.0*CGAL_PI*i/100.0),0.0);
  glEnd();
  // green y-axis and equator
  glColor3f(0.0,1.0,0.0);
  glBegin(GL_LINES);
  glVertex3d(0.0,0.0,0.0);
  glVertex3d(0.0,1.2,0.0);
  glEnd();
  glBegin(GL_LINE_LOOP);
  for(i=0;i<100;i++)
    glVertex3d(0.0,f*std::cos(2.0*CGAL_PI*i/100.0),f*std::sin(2.0*CGAL_PI*i/100.0));
  glEnd();
  // blue z-axis and equator
  glColor3f(0.0,0.0,1.0);
  glBegin(GL_LINES);
  glVertex3d(0.0,0.0,0.0);
  glVertex3d(0.0,0.0,1.2);
  glEnd();
  glBegin(GL_LINE_LOOP);
  for(i=0;i<100;i++)
    glVertex3d(f*std::cos(2.0*CGAL_PI*i/100.0),0.0,f*std::sin(2.0*CGAL_PI*i/100.0));
  glEnd();
  // six coordinate points in pink:
  glPointSize(10);
  glColor3f(1,0,1);
  glBegin(GL_POINTS);
  glVertex3d(1,0,0);
  glVertex3d(0,1,0);
  glVertex3d(0,0,1);
  glVertex3d(-1,0,0);
  glVertex3d(0,-1,0);
  glVertex3d(0,0,-1);
  glEnd(); glEndList();
}

void construct_cube() const
{
  glNewList(sphere_list_+4, GL_COMPILE);
  glColor3f(1,1,0); // yellow
  glLineWidth(2.0);
  glBegin(GL_LINE_LOOP);
  glVertex3d(-1.0,-1.0,-1.0);
  glVertex3d( 1.0,-1.0,-1.0);
  glVertex3d( 1.0, 1.0,-1.0);
  glVertex3d(-1.0, 1.0,-1.0);
  glEnd();
  glBegin(GL_LINE_LOOP);
  glVertex3d(-1.0,-1.0, 1.0);
  glVertex3d( 1.0,-1.0, 1.0);
  glVertex3d( 1.0, 1.0, 1.0);
  glVertex3d(-1.0, 1.0, 1.0);
  glEnd();
  glBegin(GL_LINES);
  glVertex3d(-1.0,-1.0,-1.0);
  glVertex3d(-1.0,-1.0, 1.0);
  glVertex3d( 1.0,-1.0,-1.0);
  glVertex3d( 1.0,-1.0, 1.0);
  glVertex3d( 1.0, 1.0,-1.0);
  glVertex3d( 1.0, 1.0, 1.0);
  glVertex3d(-1.0, 1.0,-1.0);
  glVertex3d(-1.0, 1.0, 1.0);
  glEnd(); glEndList();
}

void construct()
{ initialized_=true;
  sphere_list_ = glGenLists(5);
  CGAL_assertion_msg(sphere_list_!=0,"no display list.");
  // skeleton:
  glNewList(sphere_list_, GL_COMPILE);
  for (Object_const_iterator it = objects_.begin();
       it != objects_.end(); ++it)
     (*it)->draw();
  glEndList();
  // triangles:
  glNewList(sphere_list_+1, GL_COMPILE);
  for (Object_const_iterator it = triangles_.begin();
       it != triangles_.end(); ++it)
     (*it)->draw();
  glEndList();
  glNewList(sphere_list_+2, GL_COMPILE);
  for (Object_const_iterator it = triangle_edges_.begin();
       it != triangle_edges_.end(); ++it)
     (*it)->draw();
  glEndList();
  // orientation features:
  construct_axes();
  construct_cube();
}

public:

//void draw(GLdouble z_vec[3]) const
void draw() const
{
  gluQuadricDrawStyle(sphere_,GLenum(GLU_FILL));
  glEnable(GL_LIGHTING);
  if ( style_ == SM_SKELETON ) {
    glEnable(GL_COLOR_MATERIAL);
    glColor3f(0.7f,0.7f,0.7f);
    gluSphere(sphere_,shrink_fac,50,50);
    glDisable(GL_COLOR_MATERIAL);
  }
  glDisable(GL_LIGHTING);
  if ( !initialized_ ) const_cast<Unit_sphere*>(this)->construct();
  if ( style_ == SM_FACES || style_ == SM_TRIANGULATION ) {
    glEnable(GL_LIGHTING);
    glCallList(sphere_list_+1); // triangle list
    glDisable(GL_LIGHTING);
  }
  if ( style_ == SM_TRIANGULATION ) {
    glCallList(sphere_list_+2); // triangle edge list
  }

  if ( style_ != SM_TRIANGULATION ) {
    glCallList(sphere_list_);
  }
  if ( switches[0] ) glCallList(sphere_list_+3);
  if ( switches[1] ) glCallList(sphere_list_+4);
}


};

template <typename Map_>
class SM_BooleColor
{
  typedef typename Map_::SVertex_const_handle   SVertex_const_handle;
  typedef typename Map_::SHalfedge_const_handle SHalfedge_const_handle;
  typedef typename Map_::SHalfloop_const_handle SHalfloop_const_handle;
  typedef typename Map_::SFace_const_handle     SFace_const_handle;
  typedef typename Map_::Mark Mark;
public:
  Color color(SVertex_const_handle, Mark m) const
  { return ( m ? CGAL::black() : CGAL::white() ); }
  Color color(SHalfedge_const_handle, Mark m) const
  { return ( m ? CGAL::black() : CGAL::white() ); }
  Color color(SHalfloop_const_handle, Mark m) const
  { return ( m ? CGAL::black() : CGAL::white() ); }
  Color color(SFace_const_handle, Mark m) const
  { return ( m ? CGAL_NEF_DGREY : CGAL_NEF_LGREY ); }
};

template <typename Nef_polyhedron>
class NefS2_to_UnitSphere
{
  typedef typename Nef_polyhedron::Sphere_map          Sphere_map;
  typedef typename Nef_polyhedron::Const_decorator     SM_const_decorator;
  typedef CGAL::OGL::SM_BooleColor<Sphere_map>           Color_;
  typedef typename Sphere_map::Sphere_kernel        Sphere_kernel;
  typedef SM_decorator<Sphere_map>                  Decorator;
  typedef CGAL::SM_triangulator<Decorator>                Base;

  typedef typename Sphere_map::SVertex_const_handle SVertex_const_handle;
  typedef typename Sphere_map::SHalfedge_const_handle SHalfedge_const_handle;
  typedef typename Sphere_map::SFace_const_handle SFace_const_handle;
  typedef typename Sphere_map::SVertex_const_iterator SVertex_const_iterator;
  typedef typename Sphere_map::SHalfedge_const_iterator SHalfedge_const_iterator;
  typedef typename Sphere_map::SFace_const_iterator SFace_const_iterator;
  typedef typename Sphere_map::Mark Mark;

  typedef typename Sphere_kernel::Sphere_point    Sphere_point;
  typedef typename Sphere_kernel::Sphere_segment  Sphere_segment;
  typedef typename Sphere_kernel::Sphere_circle   Sphere_circle;
  typedef typename Sphere_kernel::Sphere_triangle Sphere_triangle;

  typedef Color_                                  Color_objects;

 public:
  static CGAL::OGL::Unit_sphere convert(const SM_const_decorator& E_,
                                        const Color_objects& CO_ = Color_objects()) {
    Sphere_map MT_(true);
    Base T_(&MT_, &E_);
    CGAL::OGL::Unit_sphere S_;

    T_.triangulate();

    SHalfedge_const_iterator e;
    CGAL_forall_sedges(e,E_) {
      if ( e->source() == e->twin()->source() ) {
        S_.push_back(e->circle(), CO_.color(e,e->mark()));} else {
        S_.push_back(Sphere_segment(e->source()->point(),
                                    e->twin()->source()->point(),
                                    e->circle()),CO_.color(e,e->mark()));
      }
    }
    // draw sphere circles underlying loops of E_:

    if ( E_.has_shalfloop() )
      S_.push_back(
                   E_.shalfloop()->circle(),
                   CO_.color(E_.shalfloop(),E_.shalfloop()->mark()));

    // draw points underlying vertices of E_:
    SVertex_const_iterator v;
    CGAL_forall_svertices(v,E_)
      S_.push_back(v->point(),CO_.color(v,v->mark()));

    Unique_hash_map<SHalfedge_const_iterator,bool> Done(false);
    CGAL_forall_shalfedges(e,T_) {
      if ( Done[e] ) continue;
      SHalfedge_const_handle en(e->snext()),enn(en->snext());
      CGAL_NEF_TRACEV(T_.incident_triangle(e));
      CGAL_NEF_TRACEN(T_.incident_mark(e)<<T_.incident_mark(en)<<T_.incident_mark(enn));
      CGAL_assertion(T_.incident_mark(e)==T_.incident_mark(en) &&
                     T_.incident_mark(en)==T_.incident_mark(enn));
      Mark m = T_.incident_mark(e);
      Sphere_triangle t = T_.incident_triangle(e);
      S_.push_back(t, (m ? CGAL_NEF_DGREY : CGAL_NEF_LGREY) );
      Done[e]=Done[en]=Done[enn]=true;
    }

    Done.clear(false);
    CGAL_forall_shalfedges(e,T_) {
      if ( Done[e] ) continue;
      S_.push_back_triangle_edge(Sphere_segment(e->source()->point(),
                                                e->twin()->source()->point(),
                                                e->circle()));
      Done[e]=Done[e->twin()]=true;
    }
    return S_;
  }
};

} // OGL
} //namespace CGAL



template <class R>
std::ostream& operator<<(std::ostream& os,
                         const CGAL::OGL::Sphere_segment<R>& s)
{ CGAL::OGL::VSegment::const_iterator it;
   os << s.original() << " ";
  for (it = s.begin(); it != s.end(); ++it)
    os << *it;
  return os;
}

template <class R>
std::ostream& operator<<(std::ostream& os,
                         const CGAL::OGL::Sphere_circle<R>& s)
{ CGAL::OGL::VSegment::const_iterator it;
   os << s.original() << " ";
  for (it = s.begin(); it != s.end(); ++it)
    os << *it;
  return os;
}



template <class R>
std::ostream& operator<<(std::ostream& os,
                         const CGAL::OGL::Sphere_point<R>& p)
{ os << p.original() << CGAL::OGL::VPoint(p); return os; }


#endif //CGAL_SPHERE_GEOMETRY_OGL_H

