#pragma prototyped
#include "circle.h"
#define DEF_RANKSEP 1.00
#define UNSET 10.00
static void
setNStepsToLeaf(Agraph_t* g, Agnode_t* n, Agnode_t* prev)
{
Agnode_t* next;
Agedge_t* ep;
int nsteps = SLEAF(n) + 1;
for (ep = agfstedge(g,n); ep; ep = agnxtedge(g,ep,n)) {
if ((next = ep->tail) == n) next = ep->head;
if (prev == next) continue;
if (nsteps < SLEAF(next)) {
SLEAF(next) = nsteps;
setNStepsToLeaf(g, next, n);
}
}
}
static int
isLeaf (Agraph_t* g, Agnode_t* n)
{
Agedge_t* ep;
Agnode_t* neighp = 0;
Agnode_t* np;
for (ep = agfstedge(g,n); ep; ep = agnxtedge(g,ep,n)) {
if ((np = ep->tail) == n) np = ep->head;
if (n == np) continue;
if (neighp) {
if (neighp != np) return 0;
}
else neighp = np;
}
return 1;
}
static void
initLayout (Agraph_t* g)
{
Agnode_t* n;
int nnodes = agnnodes (g);
int INF = nnodes*nnodes;
for (n = agfstnode(g); n; n = agnxtnode (g,n)) {
SCENTER(n) = INF;
THETA(n) = UNSET;
if (isLeaf(g,n)) SLEAF(n) = 0;
else SLEAF(n) = INF;
}
}
static Agnode_t*
findCenterNode(Agraph_t* g)
{
Agnode_t* n;
Agnode_t* center=NULL;
int maxNStepsToLeaf = 0;
if (agnnodes(g) <= 2) return (agfstnode(g));
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
if (SLEAF(n) == 0)
setNStepsToLeaf(g, n, 0);
}
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
if (SLEAF(n) > maxNStepsToLeaf) {
maxNStepsToLeaf = SLEAF(n);
center = n;
}
}
return center;
}
static void
setNStepsToCenter(Agraph_t* g, Agnode_t* n, Agnode_t* prev)
{
Agnode_t* next;
Agedge_t* ep;
int nsteps = SCENTER(n) + 1;
for (ep = agfstedge(g,n); ep; ep = agnxtedge(g,ep,n)) {
if ((next = ep->tail) == n) next = ep->head;
if (prev == next) continue;
if (nsteps < SCENTER(next)) {
SCENTER(next) = nsteps;
if (SPARENT(next))
NCHILD(SPARENT(next))--;
SPARENT(next) = n;
NCHILD(n)++;
setNStepsToCenter(g, next, n);
}
}
}
static int
setParentNodes(Agraph_t* sg, Agnode_t* center)
{
Agnode_t* n;
int maxn = 0;
SCENTER(center) = 0;
SPARENT(center) = 0;
setNStepsToCenter(sg, center, 0);
for (n = agfstnode(sg); n; n = agnxtnode(sg,n)) {
if (SCENTER(n) > maxn) {
maxn = SCENTER(n);
}
}
return maxn;
}
static void
setSubtreeSize(Agraph_t* g)
{
Agnode_t* n;
Agnode_t* parent;
for (n = agfstnode(g); n; n = agnxtnode (g,n)) {
if (NCHILD(n) > 0) continue;
STSIZE(n)++;
parent = SPARENT(n);
while (parent) {
STSIZE(parent)++;
parent = SPARENT(parent);
}
}
}
static void
setChildSubtreeSpans(Agraph_t* g, Agnode_t* n)
{
Agedge_t* ep;
Agnode_t* next;
double ratio;
ratio = SPAN(n) / STSIZE(n);
for (ep = agfstedge(g,n); ep; ep = agnxtedge(g,ep,n)) {
if ((next = ep->tail) == n) next = ep->head;
if (SPARENT(next) != n) continue;
if (SPAN(next) != 0.0) continue;
SPAN(next) = ratio * STSIZE(next);
if (NCHILD(next) > 0) {
setChildSubtreeSpans(g, next);
}
}
}
static void
setSubtreeSpans(Agraph_t* sg, Agnode_t* center)
{
SPAN(center) = 2*PI;
setChildSubtreeSpans(sg, center);
}
static void
setChildPositions(Agraph_t* sg, Agnode_t* n)
{
Agnode_t* next;
Agedge_t* ep;
double theta;
if (SPARENT(n) == 0)
theta = 0;
else
theta = THETA(n) - SPAN(n)/2;
for (ep = agfstedge(sg,n); ep; ep = agnxtedge(sg,ep,n)) {
if ((next = ep->tail) == n) next = ep->head;
if (SPARENT(next) != n) continue;
if (THETA(next) != UNSET) continue;
THETA(next) = theta + SPAN(next)/2.0 ;
theta += SPAN(next);
if (NCHILD(next) > 0)
setChildPositions(sg, next);
}
}
static void
setPositions(Agraph_t* sg, Agnode_t* center)
{
THETA(center) = 0;
setChildPositions(sg, center);
}
static void
setAbsolutePos(Agraph_t* g)
{
char* p;
Agnode_t* n;
double xf;
double hyp;
p = late_string(g,agfindattr(g->root,"ranksep"),NULL);
if (p) {
if (sscanf(p,"%lf",&xf) == 0) xf = DEF_RANKSEP;
else {if (xf < MIN_RANKSEP) xf = MIN_RANKSEP;}
}
else xf = DEF_RANKSEP;
if (Verbose) fprintf (stderr, "Rank separation = %f\n", xf);
for (n = agfstnode(g); n; n = agnxtnode (g,n)) {
hyp = xf*(SCENTER(n));
ND_pos(n)[0] = hyp*cos(THETA(n));
ND_pos(n)[1] = hyp*sin(THETA(n));
}
}
#if 0
static void
dumpGraph (Agraph_t* g)
{
Agnode_t* n;
char* p;
fprintf (stderr, " : leaf stsz nkids cntr parent span theta\n");
for (n = agfstnode(g); n; n = agnxtnode (g,n)) {
if (SPARENT(n)) p = SPARENT(n)->name;
else p = "<C>";
fprintf (stderr, "%4s :%6d%6d%6d%6d%7s%7.3f%7.3f%8.3f%8.3f\n",
n->name, SLEAF(n), STSIZE(n), NCHILD(n),
SCENTER(n), p, SPAN(n), THETA(n), ND_pos(n)[0], ND_pos(n)[1]);
}
}
#endif
void
circleLayout(Agraph_t* sg, Agnode_t* center)
{
if (agnnodes(sg) == 1) {
Agnode_t* n = agfstnode(sg);
ND_pos(n)[0] = 0;
ND_pos(n)[1] = 0;
return;
}
initLayout(sg);
if (!center)
center = findCenterNode(sg);
if (Verbose) fprintf (stderr, "Center = %s\n", center->name);
setParentNodes(sg,center);
setSubtreeSize(sg);
setSubtreeSpans(sg, center);
setPositions(sg, center);
setAbsolutePos (sg);
}