/*******************************************************************************
+
+  LEDA 3.5
+
+  _d3_window.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/
#include <LEDA/d3_window.h>
#include <LEDA/vector.h>
#include <math.h>

poly_anim::poly_anim(window& W, const polyeder& H) {
grey_scales = true;
draw_solid = true;
elim = true;
speed = 12; 
draw_edges = true;
rot1 = 0;
rot2 = 0;
init(W,H);
}

void poly_anim::init(window& W, const polyeder& H) 
{ pos1.init(H,vector(3));
  node v;
  forall_nodes(v,H) 
  { pos1[v][0] = H[v].xcoordD();
    pos1[v][1] = H[v].ycoordD();
    pos1[v][2] = H[v].zcoordD();
  }
  Wp = &W;
  Hp = &H;
  zoom_f = 1;
}


poly_anim::poly_anim(window& W, const f_polyeder& H) {
grey_scales = true;
draw_solid = true;
elim = true;
speed = 12; 
draw_edges = true;
rot1 = 0;
rot2 = 0;
init(W,H);
}


void poly_anim::init(window& W, const f_polyeder& H) 
{ pos1.init(H,vector(3));
  node v;
  forall_nodes(v,H) 
  { pos1[v][0] = H[v].xcoord();
    pos1[v][1] = H[v].ycoord();
    pos1[v][2] = H[v].zcoord();
  }
  Wp = &W;
  Hp = &H;
  zoom_f = 1;
}



static void rotate(float alpha1,float alpha2, vector& p)
{ 
  // rotate 3d-point p about the origin
  // by alpha2 in yz-plane and alpha1 in xy-plane

    if (alpha1 == 0  && alpha2 == 0) return;

    double R  = hypot(p[1],p[2]);

    if (R > 0)
    { double phi = asin(p[1]/R);
      if (p[2] < 0) phi = LEDA_PI - phi;
      p[1]  = ((R != 0) ? R*sin(phi+alpha2) : 0);
      p[2]  = ((R != 0) ? R*cos(phi+alpha2) : 0);
     }

    R = hypot(p[0],p[2]);

    if (R > 0)
    { double phi = asin(p[0]/R);
      if (p[2] < 0) phi = LEDA_PI - phi;
      p[0]  = ((R != 0) ? R*sin(phi+alpha1) : 0);
      p[2]  = ((R != 0) ? R*cos(phi+alpha1) : 0);
    }
}


inline bool z_visible(const vector& a, const vector& b, const vector& c)
{ // returns true iff (a,b,c) is visible from (0,0,-infty)
  return orientation(point(a[0],a[1]),point(b[0],b[1]),point(c[0],c[1])) > 0;
 } 


  

void poly_anim::draw()
{ 
  node_array<point> d2pos(*Hp);

  list<point> V;
  node v;

  forall_nodes(v,*Hp) 
  { point p(zoom_f*pos1[v][0],zoom_f*pos1[v][1]);
    d2pos[v] = p;
    if (Hp->degree(v) == 0) V.append(p);
   }

  Wp->start_buffering();
  Wp->clear();
  Wp->draw_point(0,0,red);


  if (Hp->number_of_edges() > 0)
  {
    edge_array<double> label(*Hp,0);

    edge e;
    forall_edges(e,*Hp) 
    { if (label[e] != 0) continue;
      edge e1 = Hp->face_cycle_succ(e);
      node a = source(e);
      node b = source(e1);
      node c = target(e1);

      double d = 0;

      if (elim || draw_solid)
      { vector v1 = pos1[b] - pos1[a];
        vector v2 = pos1[c] - pos1[a];
        double cx = v1[2]*v2[1]-v1[1]*v2[2]; 
        double cy = v1[0]*v2[2]-v1[2]*v2[0];
        double cz = v1[1]*v2[0]-v1[0]*v2[1];
        vector n = vector(cx,cy,cz).norm();
        //vector r = vector(0.5,0.25,1).norm();
        vector r = vector(0,0,1);
        d = -n*r;
        if (d == 0) d = -1;
      }
      else d = 1;


      int intens =  80 + int(150*d);

      color col = grey1;

      if (draw_solid && grey_scales)
      { color col1(intens,intens,intens);
        if (col1.is_ok())
          col = col1;
        else
        { grey_scales = false;
          error_handler(0, "Sorry, cannot allocate grey scales.\
                            Probably there are too many colors\
                            used by another X client (e.g. netscape).");
         }
       }

      list<point> pol;
      edge x = e;
      do { label[x] = d;
           if (draw_solid && d > 0) pol.append(d2pos[source(x)]);
           x = Hp->face_cycle_succ(x);
         } while (x != e);
   
      if (!pol.empty()) Wp->draw_filled_polygon(pol,col);
     }
  

     forall_edges(e,*Hp)
     { edge r = Hp->reversal(e);
       if (label[e] < 0 && label[r] < 0) continue;

    // int c1 = ((polyeder*)Hp)->inf(e);
    // int c2 = ((polyeder*)Hp)->inf(Hp->reversal(e));
    // color col = black;
    // if (colored_edges && c1 > 1) col = color(c1);
    // if (colored_edges && c2 > 1) col = color(c2);
  
       color col = black;

       if (draw_solid && grey_scales && !draw_edges)
       { double d = (label[e] + label[r])/2;
         if (d < 0) d = 0;
         int i =  80 + int(150*d);
         col = color(i,i,i);
        }

       Wp->draw_segment(d2pos[source(e)],d2pos[target(e)],col);
       label[e] = label[r] = -1;
      }
   }

  point p;
  forall(p,V) Wp->draw_point(p,blue);

  Wp->flush_buffer();
  Wp->stop_buffering();
}

void poly_anim::zoom(double f)
{ double x,y;
  int val;
  while (Wp->get_event(val,x,y) != button_release_event)
  { zoom_f *= f;
    node v;
    forall_nodes(v,*Hp) rotate(rot1,rot2,pos1[v]);
    draw();
   }
}


int poly_anim::move()
{ 
  double x=0,y=0;
  int val;
  int c = 0;


  while (c < 32)
  //while (c < 16)
  { int e = Wp->get_event(val,x,y);
    if (e == button_press_event) 
    { if (val == MOUSE_BUTTON(1)) zoom(1.05);
      if (val == MOUSE_BUTTON(3)) zoom(0.95);
      if (val == MOUSE_BUTTON(2)) zoom_f = 1.0;
      return val;
     }
    if (e == motion_event) c = 0;
    if (e == no_event) c++;
   }

  if (Hp->number_of_nodes() == 0) return NO_BUTTON;

  //if (val == MOUSE_BUTTON(1))
  { vector vec(x,y);
    double l = vec.length();
    double d = (Wp->xmax() - Wp->xmin())/10;
    double f = speed/800 * int(l/d+0.5);
    vec = vec.norm();
    rot1 = f * vec[0];
    rot2 = f * vec[1];
   }

  //if (rot1 == 0 && rot2 == 0) return NO_BUTTON;


  node v;
  forall_nodes(v,*Hp) rotate(rot1,rot2,pos1[v]);

  draw();

  return NO_BUTTON;
}

