/* * NAVIEW -- A program to make a modified radial drawing of an RNA * secondary structure. * * Copyright (c) 1988 Robert E. Bruccoleri * Copying of this software, in whole or in part, is permitted * provided that the copies are not made for commercial purposes, * appropriate credit for the use of the software is given, this * copyright notice appears, and notice is given that the copying * is by permission of Robert E. Bruccoleri. Any other copying * requires specific permission. * * See R. Bruccoleri and G. Heinrich, Computer Applications in the * Biosciences, 4, 167-173 (1988) for a full description. * * In November 1997, Michael Zuker made a number of changes to bring * naview up to modern standards. All functions defined in naview are * now declared before main() with arguments and argument types. * When functions are defined, their argument types are declared * with the function and these definitions are removed after the '{'. * The 'void' declaration was used as necessary for functions. * * The troublesome na_scanf function was deleted and replaced by * scanf. Finally, there is now no default for the minimum separation * of bases. A floating point number must be entered. However, as * before an entry < 0 will be moved up to 0 and an entry > 0.5 * will be reduced to 0.5. * * Adapted for use as a subroutine in the Vienna RNA Package * by Ivo Hofacker, May 1998: * deleted output routines, replaced main() by naview_xy_coordinates(), * which fills the X and Y arrays used by PS_rna_plot() etc. * added ansi prototypes and fixed memory leaks. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "ViennaRNA/utils.h" #include "ViennaRNA/naview.h" typedef int LOGICAL; #define logical LOGICAL #define true 1 #define false 0 #define FATAL_ERROR 1 #define SUCCESS 0 #define type_alloc(type) (type *) vrna_alloc(sizeof(type)) #define struct_alloc(structure_name) type_alloc(struct structure_name) #define add_double_list(head,tail,newp) {\ (newp)->next = (newp)->prev = NULL; \ if ((head) == NULL) (head) = (tail) = (newp); \ else { \ (tail)->next = (newp); \ (newp)->prev = (tail); \ (tail) = (newp); \ } \ } static double pi = 3.141592653589793; static double anum = 9999.0; /* * Function data type definitions */ #define minf2(x1, x2) ((x1)<(x2))?(x1):(x2) #define maxf2(x1, x2) ((x1)>(x2))?(x1):(x2) static struct base { int mate; double x,y; logical extracted; struct region *region; } *bases; struct region { int start1,end1,start2,end2; }; struct loop { int nconnection; struct connection **connections; int number; int depth; logical mark; double x,y,radius; }; struct connection { struct loop *loop; struct region *region; int start,end; /* Start and end form the 1st base pair of the region. */ double xrad,yrad,angle; logical extruded; /* True if segment between this connection and the next must be extruded out of the circle */ logical broken; /* True if the extruded segment must be drawn long. */ }; static int nbase, nregion, loop_count; static struct loop *root, *loops; static struct region *regions; static struct loop *construct_loop(int ibase); struct radloop { double radius; int loopnumber; struct radloop *next, *prev; }; static struct radloop *rlphead; static double lencut; static logical debug = false; static void read_in_bases(short *pair_table); static void find_regions(void); static void dump_loops(void); static void find_central_loop(void); static void determine_depths(void); static void traverse_loop(struct loop *lp,struct connection *anchor_connection); static void determine_radius(struct loop *lp,double lencut); static void generate_region(struct connection *cp); static void construct_extruded_segment(struct connection *cp,struct connection *cpnext); static void find_center_for_arc(int n,double b,double *hp,double *thetap); static int depth(struct loop *lp); static logical connected_connection(struct connection *cp, struct connection *cpnext); static int find_ic_middle(int icstart, int icend, struct connection *anchor_connection, struct connection *acp, struct loop *lp); int naview_xy_coordinates(short *pair_table, float *X, float *Y) { int i; nbase = pair_table[0]; /* length */ bases = (struct base *) vrna_alloc(sizeof(struct base)*(nbase+1)); regions = (struct region *) vrna_alloc(sizeof(struct region)*(nbase+1)); read_in_bases(pair_table); lencut = 0.5; rlphead = NULL; find_regions(); loop_count = 0; loops = (struct loop *) vrna_alloc(sizeof(struct loop)*(nbase+1)); construct_loop(0); find_central_loop(); if (debug) dump_loops(); traverse_loop(root,NULL); for (i=0; ii) npairs++; } if (npairs==0) { /* must have at least 1 pair to avoid segfault */ bases[1].mate=nbase; bases[nbase].mate=1; } } static void find_regions(void) /* * Identifies the regions in the structure. */ { int i,mate,nb1; logical *mark; nb1 = nbase + 1; mark = (int *) vrna_alloc(sizeof(int)*nb1); for (i = 0; i < nb1; i++) mark[i] = false; nregion = 0; for (i=0; i<=nbase; i++) { if ( (mate = bases[i].mate) && !mark[i]) { regions[nregion].start1 = i; regions[nregion].end2 = mate; mark[i] = true; mark[mate] = true; bases[i].region = bases[mate].region = ®ions[nregion]; for (i++,mate--; inconnection = 0; retloop->connections = (struct connection **) vrna_alloc(sizeof(struct connection *)); retloop->depth = 0; retloop->number = loop_count; retloop->radius = 0.0; for (rlp = rlphead; rlp; rlp = rlp->next) if (rlp->loopnumber == loop_count) retloop->radius = rlp->radius; i = ibase; do { if ((mate = bases[i].mate) != 0) { rp = bases[i].region; if (!bases[rp->start1].extracted) { if (i == rp->start1) { bases[rp->start1].extracted = true; bases[rp->end1].extracted = true; bases[rp->start2].extracted = true; bases[rp->end2].extracted = true; lp = construct_loop(rp->end1 < nbase ? rp->end1+1 : 0); } else if (i == rp->start2){ bases[rp->start2].extracted = true; bases[rp->end2].extracted = true; bases[rp->start1].extracted = true; bases[rp->end1].extracted = true; lp = construct_loop(rp->end2 < nbase ? rp->end2+1 : 0); } else { vrna_message_error("naview: Error detected in construct_loop. i = %d not found in region table.",i); exit(FATAL_ERROR); } retloop->connections = (struct connection **) realloc(retloop->connections, (++retloop->nconnection+1) * sizeof(struct connection *)); retloop->connections[retloop->nconnection-1] = cp = struct_alloc(connection); retloop->connections[retloop->nconnection] = NULL; cp->loop = lp; cp->region = rp; if (i == rp->start1) { cp->start = rp->start1; cp->end = rp->end2; } else { cp->start = rp->start2; cp->end = rp->end1; } cp->extruded = false; cp->broken = false; lp->connections = (struct connection **) realloc(lp->connections, (++lp->nconnection+1) * sizeof(struct connection *)); lp->connections[lp->nconnection-1] = cp = struct_alloc(connection); lp->connections[lp->nconnection] = NULL; cp->loop = retloop; cp->region = rp; if (i == rp->start1) { cp->start = rp->start2; cp->end = rp->end1; } else { cp->start = rp->start1; cp->end = rp->end2; } cp->extruded = false; cp->broken = false; } i = mate; } if (++i > nbase) i = 0; } while (i != ibase); return retloop; } static void dump_loops(void) /* * Displays all the loops. */ { int il,ilp,irp; struct loop *lp; struct connection *cp,**cpp; printf("\nRoot loop is #%ld\n",(root-loops)+1); for (il=0; il < loop_count; il++) { lp = &loops[il]; printf("Loop %d has %d connections:\n",il+1,lp->nconnection); for (cpp = lp->connections; (cp = *cpp); cpp++) { ilp = (cp->loop - loops) + 1; irp = (cp->region - regions) + 1; printf(" Loop %d Region %d (%d-%d)\n", ilp,irp,cp->start,cp->end); } } } static void find_central_loop(void) /* * Find node of greatest branching that is deepest. */ { struct loop *lp; int maxconn,maxdepth,i; determine_depths(); maxconn = 0; maxdepth = -1; for (i=0; inconnection > maxconn) { maxdepth = lp->depth; maxconn = lp->nconnection; root = lp; } else if (lp->depth > maxdepth && lp->nconnection == maxconn) { maxdepth = lp->depth; root = lp; } } } static void determine_depths(void) /* * Determine the depth of all loops. */ { struct loop *lp; int i,j; for (i=0; idepth = depth(lp); } } static int depth(struct loop *lp) /* * Determines the depth of loop, lp. Depth is defined as the minimum * distance to a leaf loop where a leaf loop is one that has only one * or no connections. */ { struct connection *cp,**cpp; int count,ret,d; if (lp->nconnection <= 1) return 0; if (lp->mark) return -1; lp->mark = true; count = 0; ret = 0; for (cpp=lp->connections; (cp = *cpp); cpp++) { d = depth(cp->loop); if (d >= 0) { if (++count == 1) ret = d; else if (ret > d) ret = d; } } lp->mark = false; return ret+1; } static void traverse_loop(struct loop *lp, struct connection *anchor_connection) /* * This is the workhorse of the display program. The algorithm is * recursive based on processing individual loops. Each base pairing * region is displayed using the direction given by the circle diagram, * and the connections between the regions is drawn by equally spaced * points. The radius of the loop is set to minimize the square error * for lengths between sequential bases in the loops. The "correct" * length for base links is 1. If the least squares fitting of the * radius results in loops being less than 1/2 unit apart, then that * segment is extruded. * * The variable, anchor_connection, gives the connection to the loop * processed in an previous level of recursion. */ { double xs,ys,xe,ye,xn,yn,angleinc,r; double radius,xc,yc,xo,yo,astart,aend,a; struct connection *cp,*cpnext,**cpp,*acp,*cpprev; int i,j,n,ic; double da,maxang; int count,icstart,icend,icmiddle,icroot; logical done,done_all_connections,rooted; int sign; double midx,midy,nrx,nry,mx,my,vx,vy,dotmv,nmidx,nmidy; int icstart1,icup,icdown,icnext,direction; double dan,dx,dy,rr; double cpx,cpy,cpnextx,cpnexty,cnx,cny,rcn,rc,lnx,lny,rl,ac,acn,sx,sy,dcp; int imaxloop; angleinc = 2 * pi / (nbase+1); acp = NULL; icroot = -1; for (cpp=lp->connections, ic = 0; (cp = *cpp); cpp++, ic++) { /* xs = cos(angleinc*cp->start); ys = sin(angleinc*cp->start); xe = cos(angleinc*cp->end); ye = sin(angleinc*cp->end); */ xs = -sin(angleinc*cp->start); ys = cos(angleinc*cp->start); xe = -sin(angleinc*cp->end); ye = cos(angleinc*cp->end); xn = ye-ys; yn = xs-xe; r = sqrt(xn*xn + yn*yn); cp->xrad = xn/r; cp->yrad = yn/r; cp->angle = atan2(yn,xn); if (cp->angle < 0.0) cp->angle += 2*pi; if (anchor_connection != NULL && anchor_connection->region == cp->region) { acp = cp; icroot = ic; } } set_radius: determine_radius(lp,lencut); radius = lp->radius; if (anchor_connection == NULL) xc = yc = 0.0; else { xo = (bases[acp->start].x+bases[acp->end].x) / 2.0; yo = (bases[acp->start].y+bases[acp->end].y) / 2.0; xc = xo - radius * acp->xrad; yc = yo - radius * acp->yrad; } /* * The construction of the connectors will proceed in blocks of * connected connectors, where a connected connector pairs means * two connectors that are forced out of the drawn circle because they * are too close together in angle. */ /* * First, find the start of a block of connected connectors */ if (icroot == -1) icstart = 0; else icstart = icroot; cp = lp->connections[icstart]; count = 0; if (debug) printf("Now processing loop %d\n",lp->number); done = false; do { j = icstart - 1; if (j < 0) j = lp->nconnection - 1; cpprev = lp->connections[j]; if (!connected_connection(cpprev,cp)) { done = true; } else { icstart = j; cp = cpprev; } if (++count > lp->nconnection) { /* * Here everything is connected. Break on maximum angular separation * between connections. */ maxang = -1.0; for (ic = 0; ic < lp->nconnection; ic++) { j = ic + 1; if (j >= lp->nconnection) j = 0; cp = lp->connections[ic]; cpnext = lp->connections[j]; ac = cpnext->angle - cp->angle; if (ac < 0.0) ac += 2*pi; if (ac > maxang) { maxang = ac; imaxloop = ic; } } icend = imaxloop; icstart = imaxloop + 1; if (icstart >= lp->nconnection) icstart = 0; cp = lp->connections[icend]; cp->broken = true; done = true; } } while (!done); done_all_connections = false; icstart1 = icstart; if (debug) printf("Icstart1 = %d\n",icstart1); while (!done_all_connections) { count = 0; done = false; icend = icstart; rooted = false; while (!done) { cp = lp->connections[icend]; if (icend == icroot) rooted = true; j = icend + 1; if (j >= lp->nconnection) { j = 0; } cpnext = lp->connections[j]; if (connected_connection(cp,cpnext)) { if (++count >= lp->nconnection) break; icend = j; } else { done = true; } } icmiddle = find_ic_middle(icstart,icend,anchor_connection,acp,lp); ic = icup = icdown = icmiddle; if (debug) printf("IC start = %d middle = %d end = %d\n", icstart,icmiddle,icend); done = false; direction = 0; while (!done) { if (direction < 0) { ic = icup; } else if (direction == 0) { ic = icmiddle; } else { ic = icdown; } if (ic >= 0) { cp = lp->connections[ic]; if (anchor_connection == NULL || acp != cp) { if (direction == 0) { astart = cp->angle - asin(1.0/2.0/radius); aend = cp->angle + asin(1.0/2.0/radius); bases[cp->start].x = xc + radius*cos(astart); bases[cp->start].y = yc + radius*sin(astart); bases[cp->end].x = xc + radius*cos(aend); bases[cp->end].y = yc + radius*sin(aend); } else if (direction < 0) { j = ic + 1; if (j >= lp->nconnection) j = 0; cp = lp->connections[ic]; cpnext = lp->connections[j]; cpx = cp->xrad; cpy = cp->yrad; ac = (cp->angle + cpnext->angle) / 2.0; if (cp->angle > cpnext->angle) ac -= pi; cnx = cos(ac); cny = sin(ac); lnx = cny; lny = -cnx; da = cpnext->angle - cp->angle; if (da < 0.0) da += 2*pi; if (cp->extruded) { if (da <= pi/2) rl = 2.0; else rl = 1.5; } else { rl = 1.0; } bases[cp->end].x = bases[cpnext->start].x + rl*lnx; bases[cp->end].y = bases[cpnext->start].y + rl*lny; bases[cp->start].x = bases[cp->end].x + cpy; bases[cp->start].y = bases[cp->end].y - cpx; } else { j = ic - 1; if (j < 0) j = lp->nconnection - 1; cp = lp->connections[j]; cpnext = lp->connections[ic]; cpnextx = cpnext->xrad; cpnexty = cpnext->yrad; ac = (cp->angle + cpnext->angle) / 2.0; if (cp->angle > cpnext->angle) ac -= pi; cnx = cos(ac); cny = sin(ac); lnx = -cny; lny = cnx; da = cpnext->angle - cp->angle; if (da < 0.0) da += 2*pi; if (cp->extruded) { if (da <= pi/2) rl = 2.0; else rl = 1.5; } else { rl = 1.0; } bases[cpnext->start].x = bases[cp->end].x + rl*lnx; bases[cpnext->start].y = bases[cp->end].y + rl*lny; bases[cpnext->end].x = bases[cpnext->start].x - cpnexty; bases[cpnext->end].y = bases[cpnext->start].y + cpnextx; } } } if (direction < 0) { if (icdown == icend) { icdown = -1; } else if (icdown >= 0) { if (++icdown >= lp->nconnection) { icdown = 0; } } direction = 1; } else { if (icup == icstart) icup = -1; else if (icup >= 0) { if (--icup < 0) { icup = lp->nconnection - 1; } } direction = -1; } done = icup == -1 && icdown == -1; } icnext = icend + 1; if (icnext >= lp->nconnection) icnext = 0; if (icend != icstart && (! (icstart == icstart1 && icnext == icstart1))) { /* * Move the bases just constructed (or the radius) so * that the bisector of the end points is radius distance * away from the loop center. */ cp = lp->connections[icstart]; cpnext = lp->connections[icend]; dx = bases[cpnext->end].x - bases[cp->start].x; dy = bases[cpnext->end].y - bases[cp->start].y; midx = bases[cp->start].x + dx/2.0; midy = bases[cp->start].y + dy/2.0; rr = sqrt(dx*dx + dy*dy); mx = dx / rr; my = dy / rr; vx = xc - midx; vy = yc - midy; rr = sqrt(dx*dx + dy*dy); vx /= rr; vy /= rr; dotmv = vx*mx + vy*my; nrx = dotmv*mx - vx; nry = dotmv*my - vy; rr = sqrt(nrx*nrx + nry*nry); nrx /= rr; nry /= rr; /* * Determine which side of the bisector the center should be. */ dx = bases[cp->start].x - xc; dy = bases[cp->start].y - yc; ac = atan2(dy,dx); if (ac < 0.0) ac += 2*pi; dx = bases[cpnext->end].x - xc; dy = bases[cpnext->end].y - yc; acn = atan2(dy,dx); if (acn < 0.0) acn += 2*pi; if (acn < ac) acn += 2*pi; if (acn - ac > pi) sign = -1; else sign = 1; nmidx = xc + sign*radius*nrx; nmidy = yc + sign*radius*nry; if (rooted) { xc -= nmidx - midx; yc -= nmidy - midy; } else { for (ic=icstart; ; ++ic >= lp->nconnection ? (ic = 0) : 0) { cp = lp->connections[ic]; i = cp->start; bases[i].x += nmidx - midx; bases[i].y += nmidy - midy; i = cp->end; bases[i].x += nmidx - midx; bases[i].y += nmidy - midy; if (ic == icend) break; } } } icstart = icnext; done_all_connections = icstart == icstart1; } for (ic=0; ic < lp->nconnection; ic++) { cp = lp->connections[ic]; j = ic + 1; if (j >= lp->nconnection) j = 0; cpnext = lp->connections[j]; dx = bases[cp->end].x - xc; dy = bases[cp->end].y - yc; rc = sqrt(dx*dx + dy*dy); ac = atan2(dy,dx); if (ac < 0.0) ac += 2*pi; dx = bases[cpnext->start].x - xc; dy = bases[cpnext->start].y - yc; rcn = sqrt(dx*dx + dy*dy); acn = atan2(dy,dx); if (acn < 0.0) acn += 2*pi; if (acn < ac) acn += 2*pi; dan = acn - ac; dcp = cpnext->angle - cp->angle; if (dcp <= 0.0) dcp += 2*pi; if (fabs(dan-dcp) > pi) { if (cp->extruded) { vrna_message_warning("...from traverse_loop. Loop %d has crossed regions", lp->number); } else if ((cpnext->start - cp->end) != 1) { cp->extruded = true; goto set_radius; /* Forever shamed */ } } if (cp->extruded) { construct_extruded_segment(cp,cpnext); } else { n = cpnext->start - cp->end; if (n < 0) n += nbase + 1; angleinc = dan / n; for (j = 1; j < n; j++) { i = cp->end + j; if (i > nbase) i -= nbase + 1; a = ac + j*angleinc; rr = rc + (rcn-rc)*(a-ac)/dan; bases[i].x = xc + rr*cos(a); bases[i].y = yc + rr*sin(a); } } } for (ic=0; ic < lp->nconnection; ic++) { if (icroot != ic) { cp = lp->connections[ic]; generate_region(cp); traverse_loop(cp->loop,cp); } } n = 0; sx = 0.0; sy = 0.0; for (ic = 0; ic < lp->nconnection; ic++) { j = ic + 1; if (j >= lp->nconnection) j = 0; cp = lp->connections[ic]; cpnext = lp->connections[j]; n += 2; sx += bases[cp->start].x + bases[cp->end].x; sy += bases[cp->start].y + bases[cp->end].y; if (!cp->extruded) { for (j = cp->end + 1; j != cpnext->start; j++) { if (j > nbase) j -= nbase + 1; n++; sx += bases[j].x; sy += bases[j].y; } } } lp->x = sx / n; lp->y = sy / n; /* free connections (ih) */ for (ic = 0; ic < lp->nconnection; ic++) free(lp->connections[ic]); free(lp->connections); } static void determine_radius(struct loop *lp,double lencut) /* * For the loop pointed to by lp, determine the radius of * the loop that will ensure that each base around the loop will * have a separation of at least lencut around the circle. * If a segment joining two connectors will not support this separation, * then the flag, extruded, will be set in the first of these * two indicators. The radius is set in lp. * * The radius is selected by a least squares procedure where the sum of the * squares of the deviations of length from the ideal value of 1 is used * as the error function. */ { double mindit,ci,dt,sumn,sumd,radius,dit; int i,j,end,start,imindit; struct connection *cp,*cpnext; static double rt2_2 = 0.7071068; do { mindit = 1.0e10; for (sumd=0.0, sumn=0.0, i=0; i < lp->nconnection; i++) { cp = lp->connections[i]; j = i + 1; if (j >= lp->nconnection) j = 0; cpnext = lp->connections[j]; end = cp->end; start = cpnext->start; if (start < end) start += nbase + 1; dt = cpnext->angle - cp->angle; if (dt <= 0.0) dt += 2*pi; if (!cp->extruded) ci = start - end; else { if (dt <= pi/2) ci = 2.0; else ci = 1.5; } sumn += dt * (1.0/ci + 1.0); sumd += dt * dt / ci; dit = dt/ci; if (dit < mindit && !cp->extruded && ci > 1.0) { mindit = dit; imindit = i; } } radius = sumn/sumd; if (radius < rt2_2) radius = rt2_2; if (mindit*radius < lencut) { lp->connections[imindit]->extruded = true; } } while (mindit*radius < lencut); if (lp->radius > 0.0) radius = lp->radius; else lp->radius = radius; } static logical connected_connection(struct connection *cp, struct connection *cpnext) /* * Determines if the connections cp and cpnext are connected */ { if (cp->extruded) { return true; } else if (cp->end+1 == cpnext->start) { return true; } else { return false; } } static int find_ic_middle(int icstart, int icend, struct connection *anchor_connection, struct connection *acp, struct loop *lp) /* * Finds the middle of a set of connected connectors. This is normally * the middle connection in the sequence except if one of the connections * is the anchor, in which case that connection will be used. */ { int count,ret,ic,i; logical done; count = 0; ret = -1; ic = icstart; done = false; while (!done) { if (count++ > lp->nconnection * 2) { printf("Infinite loop detected in find_ic_middle\n"); exit(FATAL_ERROR); } if (anchor_connection != NULL && lp->connections[ic] == acp) { ret = ic; } done = ic == icend; if (++ic >= lp->nconnection) { ic = 0; } } if (ret == -1) { for (i=1, ic=icstart; i<(count+1)/2; i++) { if (++ic >= lp->nconnection) ic = 0; } ret = ic; } return ret; } static void generate_region(struct connection *cp) /* * Generates the coordinates for the base pairing region of a connection * given the position of the starting base pair. */ { int l,start,end,i,mate; struct region *rp; rp = cp->region; l = 0; if (cp->start == rp->start1) { start = rp->start1; end = rp->end1; } else { start = rp->start2; end = rp->end2; } if (bases[cp->start].x > anum - 100.0 || bases[cp->end].x > anum - 100.0) { printf("Bad region passed to generate_region. Coordinates not defined.\n"); exit(FATAL_ERROR); } for (i=start+1; i<=end; i++) { l++; bases[i].x = bases[cp->start].x + l*cp->xrad; bases[i].y = bases[cp->start].y + l*cp->yrad; mate = bases[i].mate; bases[mate].x = bases[cp->end].x + l*cp->xrad; bases[mate].y = bases[cp->end].y + l*cp->yrad; } } static void construct_circle_segment(int start, int end) /* * Draws the segment of residue between the bases numbered start * through end, where start and end are presumed to be part of a base * pairing region. They are drawn as a circle which has a chord given * by the ends of two base pairing regions defined by the connections. */ { double dx,dy,rr,h,angleinc,midx,midy,xn,yn,nrx,nry,mx,my,a; int l,j,i; dx = bases[end].x - bases[start].x; dy = bases[end].y - bases[start].y; rr = sqrt(dx*dx + dy*dy); l = end - start; if (l < 0) l += nbase + 1; if (rr >= l) { dx /= rr; dy /= rr; for (j = 1; j < l; j++) { i = start + j; if (i > nbase) i -= nbase + 1; bases[i].x = bases[start].x + dx*(double)j/(double)l; bases[i].y = bases[start].y + dy*(double)j/(double)l; } } else { find_center_for_arc(l-1,rr,&h,&angleinc); dx /= rr; dy /= rr; midx = bases[start].x + dx*rr/2.0; midy = bases[start].y + dy*rr/2.0; xn = dy; yn = -dx; nrx = midx + h*xn; nry = midy + h*yn; mx = bases[start].x - nrx; my = bases[start].y - nry; rr = sqrt(mx*mx + my*my); a = atan2(my,mx); for (j = 1; j < l; j++) { i = start + j; if (i > nbase) i -= nbase + 1; bases[i].x = nrx + rr*cos(a+j*angleinc); bases[i].y = nry + rr*sin(a+j*angleinc); } } } static void construct_extruded_segment(struct connection *cp, struct connection *cpnext) /* * Constructs the segment between cp and cpnext as a circle if possible. * However, if the segment is too large, the lines are drawn between * the two connecting regions, and bases are placed there until the * connecting circle will fit. */ { double astart,aend1,aend2,aave,dx,dy,a1,a2,ac,rr,da,dac; int start,end,n,nstart,nend; logical collision; astart = cp->angle; aend2 = aend1 = cpnext->angle; if (aend2 < astart) aend2 += 2*pi; aave = (astart + aend2) / 2.0; start = cp->end; end = cpnext->start; n = end - start; if (n < 0) n += nbase + 1; da = cpnext->angle - cp->angle; if (da < 0.0) { da += 2*pi; } if (n == 2) construct_circle_segment(start,end); else { dx = bases[end].x - bases[start].x; dy = bases[end].y - bases[start].y; rr = sqrt(dx*dx + dy*dy); dx /= rr; dy /= rr; if (rr >= 1.5 && da <= pi/2) { nstart = start + 1; if (nstart > nbase) nstart -= nbase + 1; nend = end - 1; if (nend < 0) nend += nbase + 1; bases[nstart].x = bases[start].x + 0.5*dx; bases[nstart].y = bases[start].y + 0.5*dy; bases[nend].x = bases[end].x - 0.5*dx; bases[nend].y = bases[end].y - 0.5*dy; start = nstart; end = nend; } do { collision = false; construct_circle_segment(start,end); nstart = start + 1; if (nstart > nbase) nstart -= nbase + 1; dx = bases[nstart].x - bases[start].x; dy = bases[nstart].y - bases[start].y; a1 = atan2(dy,dx); if (a1 < 0.0) a1 += 2*pi; dac = a1 - astart; if (dac < 0.0) dac += 2*pi; if (dac > pi) collision = true; nend = end - 1; if (nend < 0) nend += nbase + 1; dx = bases[nend].x - bases[end].x; dy = bases[nend].y - bases[end].y; a2 = atan2(dy,dx); if (a2 < 0.0) a2 += 2*pi; dac = aend1 - a2; if (dac < 0.0) dac += 2*pi; if (dac > pi) collision = true; if (collision) { ac = minf2(aave,astart + 0.5); bases[nstart].x = bases[start].x + cos(ac); bases[nstart].y = bases[start].y + sin(ac); start = nstart; ac = maxf2(aave,aend2 - 0.5); bases[nend].x = bases[end].x + cos(ac); bases[nend].y = bases[end].y + sin(ac); end = nend; n -= 2; } } while (collision && n > 1); } } static void find_center_for_arc(int n,double b,double *hp,double *thetap) /* * Given n points to be placed equidistantly and equiangularly on a * polygon which has a chord of length, b, find the distance, h, from the * midpoint of the chord for the center of polygon. Positive values * mean the center is within the polygon and the chord, whereas * negative values mean the center is outside the chord. Also, the * radial angle for each polygon side is returned in theta. * * The procedure uses a bisection algorithm to find the correct * value for the center. Two equations are solved, the angles * around the center must add to 2*pi, and the sides of the polygon * excluding the chord must have a length of 1. */ { double h,hhi,hlow,r,disc,theta,e,phi; int iter; #define maxiter 500 hhi = (n+1) / pi; hlow = - hhi - b/(n+1.000001-b); /* changed to prevent div by zero if (ih) */ if (b<1) hlow = 0; /* otherwise we might fail below (ih) */ iter = 0; do { h = (hhi + hlow) / 2.0; r = sqrt(h*h + b*b/4.0); /* if (r<0.5) {r = 0.5; h = 0.5*sqrt(1-b*b);} */ disc = 1.0 - 0.5/(r*r); if (fabs(disc) > 1.0) { vrna_message_error("Unexpected large magnitude discriminant = %g %g", disc,r); exit(FATAL_ERROR); } theta = acos(disc); /* theta = 2*acos(sqrt(1-1/(4*r*r))); */ phi = acos(h/r); e = theta * (n+1) + 2*phi - 2*pi; if (e > 0.0) { hlow = h; } else { hhi = h; } } while (fabs(e) > 0.0001 && ++iter < maxiter); if (iter >= maxiter) { vrna_message_warning("Iteration failed in find_center_for_arc"); h = 0.0; theta = 0.0; } *hp = h; *thetap = theta; }