#include #include #include #include #include "distance_matrix.h" #include "utils.h" #define PUBLIC #define PRIVATE static #define MAXTAXA_FOR_LABELS 200 typedef struct{ int set1; int set2; float distance; float distance2; } Union; typedef struct _Node_ { float height; float brr; float brl; int whoami; int size; struct _Node_ *father; struct _Node_ *left; struct _Node_ *right; } Node; PUBLIC void PSplot_phylogeny(Union *cluster, char *filename, char *type); PRIVATE Node *W2Phylo(Union *cluster); PRIVATE Node *Nj2Phylo(Union *cluster); PRIVATE void free_phylo_tree(Node *root); PRIVATE void fill_height_into_tree(Node *root); PRIVATE void fill_br_from_height(Node *root); PRIVATE void plot_branch(Node *root, FILE *fp); PRIVATE void format_number(float x); PRIVATE char str[10]; PRIVATE float threshold; PRIVATE int print_labels=1; /* --------------------------------------------------------------------------*/ PUBLIC void PSplot_phylogeny(Union *cluster, char *filename, char *type) { int n; char outfile[50]; char *tmp, *tfont; Node *root; Node *tempnode; FILE *fp; float xsize, ysize, tfontsize, lfontsize, lwidth; n = cluster[0].set1; switch(type[0]) { case 'W' : root = W2Phylo(cluster); break; case 'N' : root = Nj2Phylo(cluster); break; default : return; } outfile[0] = '\0'; tmp = get_taxon_label(-1); /* retrieve data set identifier */ if(tmp) {strcat(outfile,tmp); strcat(outfile,"_"); free(tmp);} strcat(outfile,filename); fp = fopen(outfile,"w"); if (fp==NULL) { fprintf(stderr,"couldn't open %s -- not doing treeplot\n", outfile); return; } xsize = root->size; tempnode = root; while(tempnode->right) tempnode = tempnode->right; ysize = tempnode->height; threshold = ysize*0.06; lwidth = MIN2(1.5, 8/sqrt((double)n)); tfontsize = MIN2(15,550./(10+n)); lfontsize = 2./3.*tfontsize; if (n>30) tfont = "Helvetica"; else tfont = "Times-Roman"; if(n > MAXTAXA_FOR_LABELS) print_labels=0; else print_labels=1; fprintf(fp,"%%!PS-Adobe-2.0 EPSF-1.2\n"); fprintf(fp,"%%%%Title: TreePlot (%s)\n",type); fprintf(fp,"%%%%Creator: AnalyseDists\n"); fprintf(fp,"%%%%CreationDate: %s", time_stamp()); /* BoundingBox is only approximate */ fprintf(fp,"%%%%BoundingBox: 35 45 535 640\n"); fprintf(fp,"%%%%Pages: 1\n"); fprintf(fp,"%%%%EndComments: No comment!\n"); fprintf(fp,"288.5 50 translate\n"); fprintf(fp,"%3.1f setlinewidth\n" "/cmtx matrix currentmatrix def\n", lwidth); fprintf(fp,"500 %g div 360 %g div scale\n", xsize, ysize); fprintf(fp,"/rotshow {gsave cmtx setmatrix\n"); fprintf(fp," 90 rotate 5 %4.1f rmoveto show grestore} def\n", -tfontsize/4); fprintf(fp,"/cshow {gsave cmtx setmatrix\n" " /Helvetica findfont %4.1f scalefont setfont\n" " 90 rotate 0 %3.1f rmoveto\n" " dup stringwidth pop 2 div neg 0 rmoveto show\n" " grestore} def\n", lfontsize, lfontsize/5); fprintf(fp,"/%s findfont %4.1f scalefont setfont\n", tfont, tfontsize); fprintf(fp,"0 0 moveto\n"); plot_branch(root,fp); fprintf(fp,"cmtx setmatrix stroke\nshowpage\n"); free_phylo_tree(root); fclose(fp); } /* --------------------------------------------------------------------------*/ PRIVATE Node *W2Phylo(Union *cluster) { int i,n; float b; Node **taxa; Node *father; n=cluster[0].set1; taxa = (Node **) space(sizeof(Node*)*(n+1)); b = sqrt(MAX2(cluster[n-1].distance,0.)); for(i=1;i<=n;i++){ taxa[i] = (Node *) space(sizeof(Node)); taxa[i]->whoami = i; taxa[i]->size = 1; taxa[i]->height = b; } for(i=1;iwhoami = 0; father->left = taxa[cluster[i].set1]; father->right = taxa[cluster[i].set2]; father->size = father->left->size+father->right->size; b = sqrt(MAX2(cluster[n-1].distance-cluster[i].distance,0.)); father->height = b; father->left->father = father; father->right->father = father; taxa[cluster[i].set1] = father; } free(taxa); father->whoami = -1; fill_br_from_height(father); return father; } /* --------------------------------------------------------------------------*/ PRIVATE void fill_br_from_height(Node *root) { root->brr = root->right->height - root->height; root->brl = root->left->height - root->height; if(!root->left->whoami) fill_br_from_height(root->left); if(!root->right->whoami) fill_br_from_height(root->right); return; } /* -------------------------------------------------------------------------- */ #define ORDER(X) \ if ((X)->brl>(X)->brr) { \ xnode=(X)->right; \ (X)->right=(X)->left; \ (X)->left=xnode; \ br = (X)->brr; \ (X)->brr=(X)->brl; \ (X)->brl=br; \ } PRIVATE Node *Nj2Phylo(Union *cluster) { int i,n, br; float h1,h2,maxdist,dist; Node **taxa; Node *father; Node *topnode; /* of the longest path in the tree */ Node *tempnode, *xnode; Node *root; maxdist = 0.; n=cluster[0].set1; taxa = (Node **) space(sizeof(Node*)*(n+1)); for(i=1;i<=n;i++){ taxa[i] = (Node *) space(sizeof(Node)); taxa[i]->whoami = i; taxa[i]->size = 1; } for(i=1;iwhoami = 0; h1 = cluster[i].distance +taxa[cluster[i].set1]->height; h2 = cluster[i].distance2+taxa[cluster[i].set2]->height; dist = h1 + h2; if(dist > maxdist) { maxdist = dist; topnode = father; } if(h1<=h2) { father->brr = cluster[i].distance2; father->brl = cluster[i].distance; father->left = taxa[cluster[i].set1]; father->right = taxa[cluster[i].set2]; } else { father->brr = cluster[i].distance; father->brl = cluster[i].distance2; father->left = taxa[cluster[i].set2]; father->right = taxa[cluster[i].set1]; } father->height = MAX2(h1,h2); father->size = father->left->size + father->right->size; father->left->father = father; father->right->father = father; taxa[cluster[i].set1] = father; } /* new root is inserted in the middle of the longest path */ dist = maxdist/2.; tempnode = topnode; while (tempnode->height > dist) tempnode = tempnode->right; topnode = tempnode->father; /* new root must go between topnode and tempnode */ h1 = dist - tempnode->height; h2 = topnode->height - dist ; root = (Node *) space(sizeof(Node)); root->father = NULL; root->whoami = -1; root->right = tempnode; root->left = topnode; root->height = 0.0; root->brr = h1; root->brl = h2; root->size = n; topnode->size = n - tempnode->size; tempnode = root; while(topnode->father) { topnode->right = topnode->father; topnode->brr = topnode->father->brr; topnode->father = tempnode; tempnode = topnode; topnode = topnode->right; topnode->size = tempnode->size - tempnode->left->size; ORDER(tempnode); } /* topnode is now old root, remove it */ if (tempnode->right==topnode) { tempnode->right = topnode->left; tempnode->brr = topnode->brr + topnode->brl; tempnode->right->father = tempnode; } else { tempnode->left = topnode->left; tempnode->brl = topnode->brr + topnode->brl; tempnode->left->father = tempnode; } free(topnode); ORDER(root); fill_height_into_tree(root); return root; } /* -------------------------------------------------------------------------- */ PRIVATE void fill_height_into_tree(Node *root) { if(root->whoami>0) return; root->left->height = root->height + root->brl; root->right->height = root->height + root->brr; fill_height_into_tree(root->left); fill_height_into_tree(root->right); return; } /* -------------------------------------------------------------------------- */ PRIVATE void free_phylo_tree(Node *root) { if(root->left) free_phylo_tree(root->left); if(root->right) free_phylo_tree(root->right); free(root); } /* -------------------------------------------------------------------------- */ PRIVATE void plot_branch(Node *root, FILE *fp) { char *label; fprintf(fp,"currentpoint %g 0 rlineto 0 %g rlineto \n", -(float)(root->left->size)/2., root->brr); if((print_labels)&&(root->brr > threshold)) { format_number(root->brr); fprintf(fp,"currentpoint 0 %g rlineto \n", -root->brr/2.); fprintf(fp,"(%s) cshow moveto\n", str); } if(root->right->whoami==0) plot_branch(root->right,fp); else if(print_labels) { label = get_taxon_label(root->right->whoami); fprintf(fp, "(%s) rotshow\n", label); free(label); } fprintf(fp,"moveto\n"); fprintf(fp,"currentpoint %g 0 rlineto 0 %g rlineto \n", +(float)(root->right->size)/2., root->brl); if((print_labels)&&(root->brl > threshold)) { format_number(root->brl); fprintf(fp,"currentpoint 0 %g rlineto \n", -root->brl/2.); fprintf(fp,"(%s) cshow moveto\n",str); } if(root->left->whoami==0) plot_branch(root->left,fp); else if(print_labels) { label = get_taxon_label(root->left->whoami); fprintf(fp, "(%s) rotshow\n", label); free(label); } fprintf(fp,"moveto\n"); } /* -------------------------------------------------------------------------- */ PRIVATE void format_number(float x) { if(x >= 1.e5) { sprintf(str, "%4.1g",x ); return;} if(x >= 100) { sprintf(str, "%.0f" ,x ); return;} if(x >= 10) { sprintf(str, "%.1f", x ); return;} if(x >= 1) { sprintf(str, "%.2f", x ); return;} if(x >= 0.1) { sprintf(str, "%.2f", x ); return;} if(x >= 0.01) { sprintf(str, "%.3f", x ); return;} sprintf(str, "%4.1g",x ); return; } /* -------------------------------------------------------------------------- */