/* Copyright (c) 1997-2007
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 7579 2007-01-22 11:34:46Z gawrilow $"
#include <cstdlib>
#include <cmath>
#include <SpringEmbedder.h>
namespace polymake { namespace graph {
void SpringEmbedder::init_params(const argv_option *options)
{
if (options[opt_scale]) {
scale=std::atof(options[opt_scale]);
epsilon_2*=scale*scale;
} else {
scale=1;
}
if (options[opt_repulsion])
rep=std::atof(options[opt_repulsion]);
else
rep=1;
if (options[opt_z_factor])
z_factor=std::atof(options[opt_z_factor]);
else
z_factor=1;
}
void SpringEmbedder::adjust_edge_lengths(bool different_edge_lengths)
{
double min_edge_weight, avg_edge_weight;
if (different_edge_lengths) {
min_edge_weight=std::numeric_limits<double>::infinity();
avg_edge_weight=0;
for (Entire< Edges<graph_type> >::const_iterator e=entire(edges(G)); !e.at_end(); ++e) {
if (e->wanted_length <= 0)
throw std::runtime_error("non-positive edge length encountered");
if (e->wanted_length < min_edge_weight)
min_edge_weight=e->wanted_length;
avg_edge_weight += e->wanted_length;
}
avg_edge_weight/=min_edge_weight;
} else {
avg_edge_weight=scale;
min_edge_weight=1/scale;
}
int n_edges=0;
for (Entire< Edges<graph_type> >::iterator e=entire(edges(G)); !e.at_end(); ++e, ++n_edges)
e->inv_wanted_length=min_edge_weight/e->wanted_length;
if (different_edge_lengths)
avg_edge_weight/=n_edges;
const double n_nodes=G.nodes();
scale=avg_edge_weight/4*std::sqrt(n_nodes);
const double avg_deg=2*n_edges/n_nodes;
if (avg_deg>=3) {
const double x=2*M_PI/avg_deg;
scale*=std::sqrt(std::sin(x)/x);
}
#ifdef POLY_DEBUG
cout << "initial adjustment:"
"\n min_edge_weight=" << min_edge_weight
<< "\n avg_edge_weight=" << avg_edge_weight
<< "\n avg_degree=" << avg_deg
<< "\n n_nodes=" << n_nodes
<< "\n scale=" << scale
<< endl;
#endif
}
void SpringEmbedder::set_z_ordering(Poly& p, const argv_option *options)
{
Vector<double> Obj=p.give(options[opt_z_ordering]);
if (!strcmp(options[opt_z_ordering], "LINEAR_OBJECTIVE")) {
const Matrix<double> V=p.give("VERTICES");
Obj=V*Obj;
const Set<int> far_vertices=p.give("FAR_FACE");
if (!far_vertices.empty()) {
if (options[opt_skip_node_attr]) {
// a coarse hack: currently this option is used with BOUNDED_SUBGRAPH only
Obj=Obj.slice(~far_vertices);
} else {
const double max_obj=accumulate(Obj, operations::max()),
min_obj=accumulate(Obj, operations::min());
for (Entire< Set<int> >::const_iterator f=entire(far_vertices); !f.at_end(); ++f)
if (Obj[*f]>0)
Obj[*f]=2*max_obj-min_obj;
else
Obj[*f]=2*min_obj-max_obj;
}
}
} else if (options[opt_skip_node_attr]) {
throw std::runtime_error("don't know how to select vertices");
}
Entire< Vector<double> >::const_iterator obj=entire(Obj);
double z_order_min=*obj, z_order_max=z_order_min;
while (!(++obj).at_end())
pm::assign_min_max(z_order_min, z_order_max, *obj);
const double z_mid=(z_order_max+z_order_min)/2;
z_order_max-=z_order_min;
if (z_order_max > 1e-3*scale) {
z_ordering.resize(Obj.size());
obj=entire(Obj);
for (Entire< Vector<double> >::iterator z=entire(z_ordering); !z.at_end(); ++z, ++obj)
*z=(*obj-z_mid)/z_order_max;
}
}
void SpringEmbedder::calculate_forces(std::vector<vector3>& forces)
{
std::vector<vector3>::iterator f=forces.begin();
Entire< Vector<double> >::const_iterator z=entire(z_ordering);
double new_z_min=G.node(0).x[2], new_z_max=new_z_min;
z_max-=z_min;
if (gravity) barycenter.fill(0.);
for (Entire< Nodes<graph_type> >::const_iterator this_node=entire(nodes(G));
!this_node.at_end(); ++this_node, ++f) {
f->fill(0.);
if (gravity) barycenter += this_node->x;
if (!z.at_end()) {
const double this_z=this_node->x[2];
(*f)[2] = ((*z)*z_max - this_z) * z_factor;
pm::assign_min_max(new_z_min, new_z_max, this_z);
++z;
}
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= (edge->inv_wanted_length - 1/delta_abs) * delta;
*f += attraction;
forces[n2] -= attraction;
++edge;
} else {
// the nodes are not neighbors
const vector3 repulsion= (rep/delta_sqr/delta_abs) * delta;
*f -= repulsion;
forces[n2] += repulsion;
}
}
}
z_min=new_z_min; z_max=new_z_max;
if (gravity) {
barycenter /= G.nodes();
if (!z_ordering.empty()) barycenter[2]=0;
}
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:
syntax highlighted by Code2HTML, v. 0.9.1