/* Copyright (c) 1997-2004
   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: SpringEmbedder.cc,v 1.2 2004/06/22 16:22:36 gawrilow Exp $"

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

#include <cstdlib>
#include <cmath>
#include <SpringEmbedder.h>

namespace polymake { namespace graph {

void SpringEmbedder::init_params(const argv_option *options)
{
   id_len=1;
   if (options[opt_scale]) {
      const double s=atof(options[opt_scale]);
      id_len=s;
      epsilon_2*=s*s;
   }

   rep=1;
   if (options[opt_repulsion])
      rep*=atof(options[opt_repulsion]);

   obj_function=false;
   obj_factor=1;
   if (options[opt_objective_factor])
      obj_factor*=atof(options[opt_objective_factor]);
}

void SpringEmbedder::adjust_params()
{
   _super::adjust_params();
   const double sn=std::sqrt(double(G.nodes()));
   id_len*=sn;
   l_inv=1/id_len;
}

void SpringEmbedder::apply_objective_function(const Matrix<double>& V, const Vector<double>& Obj)
{
   Vector<double> diff(Obj.size());
   double longest_edge=0;
   for (Entire< Edges<graph_type> >::iterator e=entire(edges(G)); !e.at_end(); ++e) {
      diff=V[e.to_node()]-V[e.from_node()];
      e->real_edge_length=std::sqrt(sqr(diff));
      e->angle=std::acos(Obj*diff/e->real_edge_length);
      longest_edge=std::max(longest_edge,e->real_edge_length);
   }
   obj_function=true;
   obj_factor/=longest_edge;
}

void SpringEmbedder::calculate_forces(std::vector<vector3>& forces)
{
   std::vector<vector3>::iterator f = forces.begin();
   for (Entire< Nodes<graph_type> >::const_iterator this_node=entire(nodes(G));  
	!this_node.at_end(); ++this_node, ++f) {
      f->fill(0.);
      int n=this_node.index();
      graph_type::out_edge_list::const_iterator edge=this_node.out_edges().begin();

      for (int n2=0; n2<n; ++n2) {
	 const vector3 delta=G.node(n2).x - this_node->x;
	 const double delta_sqr=std::max(sqr(delta),epsilon_2), delta_abs=std::sqrt(delta_sqr);
	    
	 if (!edge.at_end() && edge.index()==n2) {
	    // the nodes are neighbors
	    const vector3 attraction= (l_inv/edge->wanted_length - 1/delta_abs) * delta;

	    *f += attraction;
	    forces[n2] -= attraction;

	    if (obj_function) {
	       const double alpha=std::acos(delta[2]/delta_abs);
	       const double M=obj_factor*(alpha-edge->angle)*edge->real_edge_length;
	       (*f)[2] -= M;
	       forces[n2][2] += M;
	    }
	    ++edge;

	 } else {
	    // the nodes are not neighbors
	    const vector3 repulsion= (rep/delta_sqr/delta_abs) * delta;

	    *f -= repulsion;
	    forces[n2] += repulsion;
	 }
      }
   }
      
   for (Entire< Set<int> >::const_iterator fi = entire(fixed_vertices); !fi.at_end(); ++fi)
      forces[*fi].fill(0);
}

} }

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