/* Copyright (c) 1997-2006
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
*/

#ident "$Project: polymake $$Id: SchlegelWindow.cc,v 1.15 2006/03/07 18:06:42 gawrilow Exp $"

#if defined(__GNUC__) && __GNUC__==3 && __GNUC_MINOR__<3 && !defined(__APPLE__) && !defined(__COMO__)
#pragma implementation
#endif

#include <SchlegelWindow.h>
#include <schlegel_common.h>
#include <linalg.h>

namespace polymake { namespace polytope {

namespace {
   template <typename _Vector> inline
   Matrix<typename _Vector::element_type>
   rotation(const GenericVector<_Vector>& H)
   {
      return null_space_oriented(H, -1) / H;
   }
}

SchlegelWindow::SchlegelWindow(const Matrix<double>& V, const Matrix<double>& F, const Graph<>& FG,
			       const Vector<double>& FacetPoint_arg, const Vector<double>& InnerPoint,
			       int facet_index, const double& zoom_arg)
   : d(V.cols()-1), proj_facet(facet_index),
     ViewRay(d+1), FacetPoint(unit_vector<double>(d+1,0))
{
   params["zoom"]=zoom_arg;
   iparams["zoom"]=true;
   // orthogonal transformation: translate the origin to FacetPoint_arg and rotate F[proj_facet] to (0,0,...,1)
   Matrix<double> R=rotation(0.0 | F[proj_facet].slice(1));
   orthogonalize(entire(rows(R.minor(range(1,d-1), All))));
   normalize(entire(rows(R.minor(range(1,d), All))));
   R.col(0).slice(1) = (-FacetPoint_arg * T(R)).slice(1);

   Vertices=V * T(R);
   inv_Rotation=inv(R);
   NeighborFacets=F.minor(FG.neighbors(proj_facet),All) * inv_Rotation;
   ViewRay.slice(1) = (-InnerPoint * T(R)).slice(1);
   inverse_zoom();
   compute_points();
}


double SchlegelWindow::inverse_zoom()
{
   const double alpha=schlegel_nearest_neighbor_crossing(NeighborFacets, FacetPoint, ViewRay);
   if ((constrained= alpha>=0 && alpha<1e8)) ViewRay*=alpha;
   return alpha;
}


void SchlegelWindow::compute_points()
{
  const double zoom = params["zoom"];
   const double z= constrained ? zoom : zoom/(1-zoom), VR_d=-ViewRay[d];
   const sequence visible_coord=range(1,d-1);
   Points=Vertices.minor(All,visible_coord);
   Rows< Matrix<double> >::iterator p_i=rows(Points).begin();
   for (Entire< Matrix<double>::col_type >::const_iterator P_d=entire(Vertices.col(d)); !P_d.at_end(); ++P_d, ++p_i) {
      (*p_i) = ( VR_d * ((*p_i)-FacetPoint.slice(visible_coord)) + (*P_d) * ViewRay.slice(visible_coord) ) /
	       ( (*P_d)/z + VR_d );
   }
}


SchlegelWindow::drag_response
SchlegelWindow::drag_point(dragged_point& dragged, const IncidenceMatrix<>& VIF)
{
   const double zoom = params["zoom"];
   dragged.second-=Points[dragged.first];

   if (sqr(dragged.second)<1e-5) return drag_ignore;

   if (constrained) ViewRay *= zoom;
   else if (zoom!=1) ViewRay *= zoom/(1-zoom);

   const bool on_proj_facet=VIF(proj_facet,dragged.first);
   if (on_proj_facet) {
      inv_sign(dragged.second);
   } else {
      dragged.second *= 1 - ViewRay[d]/Vertices(dragged.first,d);
   }

   const double beta = schlegel_nearest_neighbor_crossing(NeighborFacets,
							  FacetPoint + ViewRay,
							  (0 | dragged.second | 0));
   const drag_response result= beta<=1 ? drag_boundary : drag_OK;

   if (result==drag_boundary) {
      if (beta<=1e-6) return result;
      dragged.second*=beta;
   }
   if (on_proj_facet)
      FacetPoint.slice(range(1,d-1)) += dragged.second;
   else
      ViewRay.slice(range(1,d-1)) += dragged.second;

   if (result==drag_boundary) {
      constrained=true;
      params["zoom"]=1;
   } else {
      const double alpha=inverse_zoom();
      params["zoom"] = constrained ? 1/alpha : 0.5;
   }
   compute_points();
   return result;
}

std::pair< Vector<double>, Vector<double> >
SchlegelWindow::reconstruct_params() const
{
   const Vector<double> FP=FacetPoint * T(inv_Rotation),
                        IP=FP - ViewRay * T(inv_Rotation);
   return std::make_pair(FP,IP);
}

} }


// Local Variables:
// c-basic-offset:3
// End:
