+/*
+ VARNA is a tool for the automated drawing, visualization and annotation of the secondary structure of RNA, designed as a companion software for web servers and databases.
+ Copyright (C) 2008 Kevin Darty, Alain Denise and Yann Ponty.
+ electronic mail : Yann.Ponty@lri.fr
+ paper mail : LRI, bat 490 Université Paris-Sud 91405 Orsay Cedex France
+
+ This file is part of VARNA version 3.1.
+ VARNA version 3.1 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 3 of the License, or (at your option) any later version.
+
+ VARNA version 3.1 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.
+
+ You should have received a copy of the GNU General Public License along with VARNA version 3.1.
+ If not, see http://www.gnu.org/licenses.
+ */
+package fr.orsay.lri.varna.models.naView;
+
+
+import java.util.ArrayList;
+
+import fr.orsay.lri.varna.exceptions.ExceptionNAViewAlgorithm;
+import fr.orsay.lri.varna.interfaces.InterfaceVARNAListener;
+import fr.orsay.lri.varna.interfaces.InterfaceVARNAObservable;
+
+public class NAView {
+ private final double ANUM = 9999.0;
+ private final int MAXITER = 500;
+
+ private ArrayList<Base> bases;
+ private int nbase, nregion, loop_count;
+
+ private Loop root = new Loop();
+ private ArrayList<Loop> loops;
+
+ private ArrayList<Region> regions;
+
+ private Radloop rlphead = new Radloop();
+
+ private double lencut=0.8;
+ private final double RADIUS_REDUCTION_FACTOR = 1.4;
+
+ // show algorithm step by step
+ private boolean debug = false;
+
+ private double angleinc;
+
+ private double _h;
+
+ private ArrayList<InterfaceVARNAListener> _listeVARNAListener = new ArrayList<InterfaceVARNAListener>();
+
+ private boolean noIterationFailureYet = true;
+
+ double HELIX_FACTOR = 0.6;
+ double BACKBONE_DISTANCE = 27;
+
+ public int naview_xy_coordinates(ArrayList<Short> pair_table2,
+ ArrayList<Double> x, ArrayList<Double> y)
+ throws ExceptionNAViewAlgorithm {
+ if (debug)
+ System.out.println("naview_xy_coordinates");
+ if (pair_table2.size() == 0)
+ return 0;
+ int i;
+ ArrayList<Integer> pair_table = new ArrayList<Integer>(pair_table2
+ .size() + 1);
+ pair_table.add(pair_table2.size());
+
+ for (int j = 0; j < pair_table2.size(); j++) {
+ pair_table.add(pair_table2.get(j) + 1);
+ }
+
+ if (debug) {
+ infoStructure(pair_table);
+ }
+ // length
+ nbase = pair_table.get(0);
+ bases = new ArrayList<Base>(nbase + 1);
+
+ for (int index = 0; index < bases.size(); index++) {
+ bases.add(new Base());
+ }
+
+ regions = new ArrayList<Region>();
+ for (int index = 0; index < nbase + 1; index++) {
+ regions.add(new Region());
+ }
+
+ read_in_bases(pair_table);
+
+ if (debug)
+ infoBasesMate();
+
+ rlphead = null;
+
+ find_regions();
+
+ if (debug)
+ infoRegions();
+
+ loop_count = 0;
+ loops = new ArrayList<Loop>(nbase + 1);
+ for (int index = 0; index < nbase + 1; index++) {
+ loops.add(new Loop());
+ }
+
+ construct_loop(0);
+
+ if (debug)
+ infoBasesExtracted();
+
+ find_central_loop();
+
+ if (debug)
+ infoRoot();
+
+ if (debug)
+ dump_loops();
+
+ traverse_loop(root, null);
+
+ for (i = 0; i < nbase; i++) {
+ x.add(100 + BACKBONE_DISTANCE * bases.get(i + 1).getX());
+ y.add(100 + BACKBONE_DISTANCE * bases.get(i + 1).getY());
+ }
+
+ return nbase;
+ }
+
+ private void infoStructure(ArrayList<Integer> pair_table) {
+ System.out.println("structure:");
+ for (int j = 0; j < pair_table.size(); j++) {
+ System.out.print("#" + j + ":" + pair_table.get(j) + "\t");
+ if (j % 10 == 0)
+ System.out.println();
+ }
+ System.out.println();
+ }
+
+ private void infoBasesMate() {
+ System.out.println("Bases mate:");
+ for (int index = 0; index < bases.size(); index++) {
+ System.out.print("#" + index + ":" + bases.get(index).getMate()
+ + "\t");
+ if (index % 10 == 0)
+ System.out.println();
+ }
+ System.out.println();
+ }
+
+ private void infoRegions() {
+ System.out.println("regions:");
+ for (int index = 0; index < regions.size(); index++) {
+ System.out.print("(" + regions.get(index).getStart1() + ","
+ + regions.get(index).getStart2() + ";"
+ + regions.get(index).getEnd1() + ","
+ + regions.get(index).getEnd2() + ")\t\t");
+ if (index % 5 == 0)
+ System.out.println();
+ }
+ System.out.println();
+ }
+
+ private void infoBasesExtracted() {
+ System.out.println("Bases extracted:");
+ for (int index = 0; index < bases.size(); index++) {
+ System.out.print("i=" + index + ":"
+ + bases.get(index).isExtracted() + "\t");
+ if (index % 5 == 0)
+ System.out.println();
+ }
+ System.out.println();
+ }
+
+ private void infoRoot() {
+ System.out.println("root" + root.getNconnection() + ";"
+ + root.getNumber());
+ System.out.println("\troot : ");
+ System.out.println("\tdepth=" + root.getDepth());
+ System.out.println("\tmark=" + root.isMark());
+ System.out.println("\tnumber=" + root.getNumber());
+ System.out.println("\tradius=" + root.getRadius());
+ System.out.println("\tx=" + root.getX());
+ System.out.println("\ty=" + root.getY());
+ System.out.println("\tnconnection=" + root.getNconnection());
+ }
+
+ private void read_in_bases(ArrayList<Integer> pair_table) {
+ if (debug)
+ System.out.println("read_in_bases");
+
+ int i, npairs;
+
+ // Set up an origin.
+ bases.add(new Base());
+ bases.get(0).setMate(0);
+ bases.get(0).setExtracted(false);
+ bases.get(0).setX(ANUM);
+ bases.get(0).setY(ANUM);
+
+ for (npairs = 0, i = 1; i <= nbase; i++) {
+ bases.add(new Base());
+ bases.get(i).setExtracted(false);
+ bases.get(i).setX(ANUM);
+ bases.get(i).setY(ANUM);
+ bases.get(i).setMate(pair_table.get(i));
+ if ((int) pair_table.get(i) > i)
+ npairs++;
+ }
+ // must have at least 1 pair to avoid segfault
+ if (npairs == 0) {
+ bases.get(1).setMate(nbase);
+ bases.get(nbase).setMate(1);
+ }
+ }
+
+ /**
+ * Identifies the regions in the structure.
+ */
+ private void find_regions()
+
+ {
+ if (debug)
+ System.out.println("find_regions");
+ int i, mate, nb1;
+ nb1 = nbase + 1;
+ ArrayList<Boolean> mark = new ArrayList<Boolean>(nb1);
+ for (i = 0; i < nb1; i++)
+ mark.add(false);
+ nregion = 0;
+ for (i = 0; i <= nbase; i++) {
+ if ((mate = bases.get(i).getMate()) != 0 && !mark.get(i)) {
+ regions.get(nregion).setStart1(i);
+ regions.get(nregion).setEnd2(mate);
+ mark.set(i, true);
+ mark.set(mate, true);
+ bases.get(i).setRegion(regions.get(nregion));
+ bases.get(mate).setRegion(regions.get(nregion));
+ for (i++, mate--; i < mate && bases.get(i).getMate() == mate; i++, mate--) {
+ mark.set(mate, true);
+ mark.set(i, true);
+ bases.get(i).setRegion(regions.get(nregion));
+ bases.get(mate).setRegion(regions.get(nregion));
+ }
+ regions.get(nregion).setEnd1(--i);
+ regions.get(nregion).setStart2(mate + 1);
+ if (debug) {
+ if (nregion == 0)
+ System.out.printf("\nRegions are:\n");
+ System.out.printf(
+ "Region %d is %d-%d and %d-%d with gap of %d.\n",
+ nregion + 1, regions.get(nregion).getStart1(),
+ regions.get(nregion).getEnd1(), regions
+ .get(nregion).getStart2(), regions.get(
+ nregion).getEnd2(), regions.get(nregion)
+ .getStart2()
+ - regions.get(nregion).getEnd1() + 1);
+ }
+ nregion++;
+ }
+ }
+ }
+
+ /**
+ * Starting at residue ibase, recursively constructs the loop containing
+ * said base and all deeper bases.
+ *
+ * @throws ExceptionNAViewAlgorithm
+ */
+ private Loop construct_loop(int ibase) throws ExceptionNAViewAlgorithm {
+ if (debug)
+ System.out.println("construct_loop");
+ int i, mate;
+ Loop retloop = new Loop(), lp = new Loop();
+ Connection cp = new Connection();
+ Region rp = new Region();
+ Radloop rlp = new Radloop();
+ retloop = loops.get(loop_count++);
+ retloop.setNconnection(0);
+ //System.out.println(""+ibase+" "+nbase);
+ //ArrayList<Connection> a = new ArrayList<Connection>(nbase + 1);
+ //retloop.setConnections(a);
+// for (int index = 0; index < nbase + 1; index++)
+// retloop.getConnections().add(new Connection());
+ //for (int index = 0; index < nbase + 1; index++)
+ // retloop.addConnection(index,new Connection());
+ retloop.setDepth(0);
+ retloop.setNumber(loop_count);
+ retloop.setRadius(0.0);
+ for (rlp = rlphead; rlp != null; rlp = rlp.getNext())
+ if (rlp.getLoopnumber() == loop_count)
+ retloop.setRadius(rlp.getRadius());
+ i = ibase;
+ do {
+ if ((mate = bases.get(i).getMate()) != 0) {
+ rp = bases.get(i).getRegion();
+ if (!bases.get(rp.getStart1()).isExtracted()) {
+ if (i == rp.getStart1()) {
+ bases.get(rp.getStart1()).setExtracted(true);
+ bases.get(rp.getEnd1()).setExtracted(true);
+ bases.get(rp.getStart2()).setExtracted(true);
+ bases.get(rp.getEnd2()).setExtracted(true);
+ lp = construct_loop(rp.getEnd1() < nbase ? rp.getEnd1() + 1
+ : 0);
+ } else if (i == rp.getStart2()) {
+ bases.get(rp.getStart2()).setExtracted(true);
+ bases.get(rp.getEnd2()).setExtracted(true);
+ bases.get(rp.getStart1()).setExtracted(true);
+ bases.get(rp.getEnd1()).setExtracted(true);
+ lp = construct_loop(rp.getEnd2() < nbase ? rp.getEnd2() + 1
+ : 0);
+ } else {
+ throw new ExceptionNAViewAlgorithm(
+ "naview:Error detected in construct_loop. i = "
+ + i + " not found in region table.\n");
+ }
+ retloop.setNconnection(retloop.getNconnection() + 1);
+ cp = new Connection();
+ retloop.setConnection(retloop.getNconnection() - 1, cp);
+ retloop.setConnection(retloop.getNconnection(), null);
+ cp.setLoop(lp);
+ cp.setRegion(rp);
+ if (i == rp.getStart1()) {
+ cp.setStart(rp.getStart1());
+ cp.setEnd(rp.getEnd2());
+ } else {
+ cp.setStart(rp.getStart2());
+ cp.setEnd(rp.getEnd1());
+ }
+ cp.setExtruded(false);
+ cp.setBroken(false);
+ lp.setNconnection(lp.getNconnection() + 1);
+ cp = new Connection();
+ lp.setConnection(lp.getNconnection() - 1, cp);
+ lp.setConnection(lp.getNconnection(), null);
+ cp.setLoop(retloop);
+ cp.setRegion(rp);
+ if (i == rp.getStart1()) {
+ cp.setStart(rp.getStart2());
+ cp.setEnd(rp.getEnd1());
+ } else {
+ cp.setStart(rp.getStart1());
+ cp.setEnd(rp.getEnd2());
+ }
+ cp.setExtruded(false);
+ cp.setBroken(false);
+ }
+ i = mate;
+ }
+ if (++i > nbase)
+ i = 0;
+ } while (i != ibase);
+ return retloop;
+ }
+
+ /**
+ * Displays all the loops.
+ */
+ private void dump_loops() {
+ System.out.println("dump_loops");
+ int il, ilp, irp;
+ Loop lp;
+ Connection cp;
+
+ System.out.printf("\nRoot loop is #%d\n", loops.indexOf(root) + 1);
+ for (il = 0; il < loop_count; il++) {
+ lp = loops.get(il);
+ System.out.printf("Loop %d has %d connections:\n", il + 1, lp
+ .getNconnection());
+ for (int i = 0; (cp = lp.getConnection(i)) != null; i++) {
+ ilp = (loops.indexOf(cp.getLoop())) + 1;
+ irp = (regions.indexOf(cp.getRegion())) + 1;
+ System.out.printf(" Loop %d Region %d (%d-%d)\n", ilp, irp, cp
+ .getStart(), cp.getEnd());
+ }
+ }
+ }
+
+ /**
+ * Find node of greatest branching that is deepest.
+ */
+ private void find_central_loop() {
+ if (debug)
+ System.out.println("find_central_loop");
+ Loop lp = new Loop();
+ int maxconn, maxdepth, i;
+
+ determine_depths();
+ maxconn = 0;
+ maxdepth = -1;
+ for (i = 0; i < loop_count; i++) {
+ lp = loops.get(i);
+ if (lp.getNconnection() > maxconn) {
+ maxdepth = lp.getDepth();
+ maxconn = lp.getNconnection();
+ root = lp;
+ } else if (lp.getDepth() > maxdepth
+ && lp.getNconnection() == maxconn) {
+ maxdepth = lp.getDepth();
+ root = lp;
+ }
+ }
+ }
+
+ /**
+ * Determine the depth of all loops.
+ */
+ private void determine_depths() {
+ if (debug)
+ System.out.println("determine_depths");
+ Loop lp = new Loop();
+ int i, j;
+
+ for (i = 0; i < loop_count; i++) {
+ lp = loops.get(i);
+ for (j = 0; j < loop_count; j++)
+ loops.get(j).setMark(false);
+ lp.setDepth(depth(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.
+ */
+ private int depth(Loop lp) {
+ if (debug)
+ System.out.println("depth");
+ int count, ret, d;
+
+ if (lp.getNconnection() <= 1)
+ return 0;
+ if (lp.isMark())
+ return -1;
+ lp.setMark(true);
+ count = 0;
+ ret = 0;
+ for (int i = 0; lp.getConnection(i) != null; i++) {
+ d = depth(lp.getConnection(i).getLoop());
+ if (d >= 0) {
+ if (++count == 1)
+ ret = d;
+ else if (ret > d)
+ ret = d;
+ }
+ }
+ lp.setMark(false);
+ return ret + 1;
+ }
+
+ /**
+ * 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.
+ *
+ * @throws ExceptionNAViewAlgorithm
+ */
+ private void traverse_loop(Loop lp, Connection anchor_connection)
+ throws ExceptionNAViewAlgorithm {
+ if (debug)
+ System.out.println(" traverse_loop");
+ double xs, ys, xe, ye, xn, yn, angleinc, r;
+ double radius, xc, yc, xo, yo, astart, aend, a;
+ Connection cp, cpnext, acp, cpprev;
+ int i, j, n, ic;
+ double da, maxang;
+ int count, icstart, icend, icmiddle, icroot;
+ boolean 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 = 0;
+
+ angleinc = 2 * Math.PI / (nbase + 1);
+ acp = null;
+ icroot = -1;
+ int indice = 0;
+
+ for (ic = 0; (cp = lp.getConnection(indice)) != null; indice++, ic++) {
+ // xs = cos(angleinc*cp.setStart(); ys = sin(angleinc*cp.setStart();
+ // xe =
+ // cos(angleinc*cp.setEnd()); ye = sin(angleinc*cp.setEnd());
+ xs = -Math.sin(angleinc * cp.getStart());
+ ys = Math.cos(angleinc * cp.getStart());
+ xe = -Math.sin(angleinc * cp.getEnd());
+ ye = Math.cos(angleinc * cp.getEnd());
+ xn = ye - ys;
+ yn = xs - xe;
+ r = Math.sqrt(xn * xn + yn * yn);
+ cp.setXrad(xn / r);
+ cp.setYrad(yn / r);
+ cp.setAngle(Math.atan2(yn, xn));
+ if (cp.getAngle() < 0.0)
+ cp.setAngle(cp.getAngle() + 2 * Math.PI);
+ if (anchor_connection != null
+ && anchor_connection.getRegion() == cp.getRegion()) {
+ acp = cp;
+ icroot = ic;
+ }
+ }
+ // remplacement d'une etiquette de goto
+ set_radius: while (true) {
+ determine_radius(lp, lencut);
+ radius = lp.getRadius()/RADIUS_REDUCTION_FACTOR;
+ if (anchor_connection == null)
+ xc = yc = 0.0;
+ else {
+ xo = (bases.get(acp.getStart()).getX() + bases
+ .get(acp.getEnd()).getX()) / 2.0;
+ yo = (bases.get(acp.getStart()).getY() + bases
+ .get(acp.getEnd()).getY()) / 2.0;
+ xc = xo - radius * acp.getXrad();
+ yc = yo - radius * acp.getYrad();
+ }
+
+ // 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.getConnection(icstart);
+ count = 0;
+ if (debug)
+ {
+ System.out.printf("Now processing loop %d\n", lp.getNumber());
+ System.out.println(" "+lp);
+ }
+ done = false;
+ do {
+ j = icstart - 1;
+ if (j < 0)
+ j = lp.getNconnection() - 1;
+ cpprev = lp.getConnection(j);
+ if (!connected_connection(cpprev, cp)) {
+ done = true;
+ } else {
+ icstart = j;
+ cp = cpprev;
+ }
+ if (++count > lp.getNconnection()) {
+ // Here everything is connected. Break on maximum angular
+ // separation between connections.
+
+ maxang = -1.0;
+ for (ic = 0; ic < lp.getNconnection(); ic++) {
+ j = ic + 1;
+ if (j >= lp.getNconnection())
+ j = 0;
+ cp = lp.getConnection(ic);
+ cpnext = lp.getConnection(j);
+ ac = cpnext.getAngle() - cp.getAngle();
+ if (ac < 0.0)
+ ac += 2 * Math.PI;
+ if (ac > maxang) {
+ maxang = ac;
+ imaxloop = ic;
+ }
+ }
+ icend = imaxloop;
+ icstart = imaxloop + 1;
+ if (icstart >= lp.getNconnection())
+ icstart = 0;
+ cp = lp.getConnection(icend);
+ cp.setBroken(true);
+ done = true;
+ }
+ } while (!done);
+ done_all_connections = false;
+ icstart1 = icstart;
+ if (debug)
+ System.out.printf(" Icstart1 = %d\n", icstart1);
+ while (!done_all_connections) {
+ count = 0;
+ done = false;
+ icend = icstart;
+ rooted = false;
+ while (!done) {
+ cp = lp.getConnection(icend);
+ if (icend == icroot)
+ rooted = true;
+ j = icend + 1;
+ if (j >= lp.getNconnection()) {
+ j = 0;
+ }
+ cpnext = lp.getConnection(j);
+ if (connected_connection(cp, cpnext)) {
+ if (++count >= lp.getNconnection())
+ break;
+ icend = j;
+ } else {
+ done = true;
+ }
+ }
+ icmiddle = find_ic_middle(icstart, icend, anchor_connection,
+ acp, lp);
+ ic = icup = icdown = icmiddle;
+ if (debug)
+ System.out.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.getConnection(ic);
+ if (anchor_connection == null || acp != cp) {
+ if (direction == 0) {
+ astart = cp.getAngle()
+ - Math.asin(1.0 / 2.0 / radius);
+ aend = cp.getAngle()
+ + Math.asin(1.0 / 2.0 / radius);
+ bases.get(cp.getStart()).setX(
+ xc + radius * Math.cos(astart));
+ bases.get(cp.getStart()).setY(
+ yc + radius * Math.sin(astart));
+ bases.get(cp.getEnd()).setX(
+ xc + radius * Math.cos(aend));
+ bases.get(cp.getEnd()).setY(
+ yc + radius * Math.sin(aend));
+ } else if (direction < 0) {
+ j = ic + 1;
+ if (j >= lp.getNconnection())
+ j = 0;
+ cp = lp.getConnection(ic);
+ cpnext = lp.getConnection(j);
+ cpx = cp.getXrad();
+ cpy = cp.getYrad();
+ ac = (cp.getAngle() + cpnext.getAngle()) / 2.0;
+ if (cp.getAngle() > cpnext.getAngle())
+ ac -= Math.PI;
+ cnx = Math.cos(ac);
+ cny = Math.sin(ac);
+ lnx = cny;
+ lny = -cnx;
+ da = cpnext.getAngle() - cp.getAngle();
+ if (da < 0.0)
+ da += 2 * Math.PI;
+ if (cp.isExtruded()) {
+ if (da <= Math.PI / 2)
+ rl = 2.0;
+ else
+ rl = 1.5;
+ } else {
+ rl = 1.0;
+ }
+ bases.get(cp.getEnd()).setX(
+ bases.get(cpnext.getStart()).getX()
+ + rl * lnx);
+ bases.get(cp.getEnd()).setY(
+ bases.get(cpnext.getStart()).getY()
+ + rl * lny);
+ bases.get(cp.getStart()).setX(
+ bases.get(cp.getEnd()).getX() + cpy);
+ bases.get(cp.getStart()).setY(
+ bases.get(cp.getEnd()).getY() - cpx);
+ } else {
+ j = ic - 1;
+ if (j < 0)
+ j = lp.getNconnection() - 1;
+ cp = lp.getConnection(j);
+ cpnext = lp.getConnection(ic);
+ cpnextx = cpnext.getXrad();
+ cpnexty = cpnext.getYrad();
+ ac = (cp.getAngle() + cpnext.getAngle()) / 2.0;
+ if (cp.getAngle() > cpnext.getAngle())
+ ac -= Math.PI;
+ cnx = Math.cos(ac);
+ cny = Math.sin(ac);
+ lnx = -cny;
+ lny = cnx;
+ da = cpnext.getAngle() - cp.getAngle();
+ if (da < 0.0)
+ da += 2 * Math.PI;
+ if (cp.isExtruded()) {
+ if (da <= Math.PI / 2)
+ rl = 2.0;
+ else
+ rl = 1.5;
+ } else {
+ rl = 1.0;
+ }
+ bases.get(cpnext.getStart()).setX(
+ bases.get(cp.getEnd()).getX() + rl
+ * lnx);
+ bases.get(cpnext.getStart()).setY(
+ bases.get(cp.getEnd()).getY() + rl
+ * lny);
+ bases.get(cpnext.getEnd()).setX(
+ bases.get(cpnext.getStart()).getX()
+ - cpnexty);
+ bases.get(cpnext.getEnd()).setY(
+ bases.get(cpnext.getStart()).getY()
+ + cpnextx);
+ }
+ }
+ }
+ if (direction < 0) {
+ if (icdown == icend) {
+ icdown = -1;
+ } else if (icdown >= 0) {
+ if (++icdown >= lp.getNconnection()) {
+ icdown = 0;
+ }
+ }
+ direction = 1;
+ } else {
+ if (icup == icstart)
+ icup = -1;
+ else if (icup >= 0) {
+ if (--icup < 0) {
+ icup = lp.getNconnection() - 1;
+ }
+ }
+ direction = -1;
+ }
+ done = icup == -1 && icdown == -1;
+ }
+ icnext = icend + 1;
+ if (icnext >= lp.getNconnection())
+ 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.getConnection(icstart);
+ cpnext = lp.getConnection(icend);
+ dx = bases.get(cpnext.getEnd()).getX()
+ - bases.get(cp.getStart()).getX();
+ dy = bases.get(cpnext.getEnd()).getY()
+ - bases.get(cp.getStart()).getY();
+ midx = bases.get(cp.getStart()).getX() + dx / 2.0;
+ midy = bases.get(cp.getStart()).getY() + dy / 2.0;
+ rr = Math.sqrt(dx * dx + dy * dy);
+ mx = dx / rr;
+ my = dy / rr;
+ vx = xc - midx;
+ vy = yc - midy;
+ rr = Math.sqrt(dx * dx + dy * dy);
+ vx /= rr;
+ vy /= rr;
+ dotmv = vx * mx + vy * my;
+ nrx = dotmv * mx - vx;
+ nry = dotmv * my - vy;
+ rr = Math.sqrt(nrx * nrx + nry * nry);
+ nrx /= rr;
+ nry /= rr;
+
+ // Determine which side of the bisector the center should
+ // be.
+
+ dx = bases.get(cp.getStart()).getX() - xc;
+ dy = bases.get(cp.getStart()).getY() - yc;
+ ac = Math.atan2(dy, dx);
+ if (ac < 0.0)
+ ac += 2 * Math.PI;
+ dx = bases.get(cpnext.getEnd()).getX() - xc;
+ dy = bases.get(cpnext.getEnd()).getY() - yc;
+ acn = Math.atan2(dy, dx);
+ if (acn < 0.0)
+ acn += 2 * Math.PI;
+ if (acn < ac)
+ acn += 2 * Math.PI;
+ if (acn - ac > Math.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;;) {
+ cp = lp.getConnection(ic);
+ i = cp.getStart();
+ bases.get(i).setX(
+ bases.get(i).getX() + nmidx - midx);
+ bases.get(i).setY(
+ bases.get(i).getY() + nmidy - midy);
+ i = cp.getEnd();
+ bases.get(i).setX(
+ bases.get(i).getX() + nmidx - midx);
+ bases.get(i).setY(
+ bases.get(i).getY() + nmidy - midy);
+ if (ic == icend)
+ break;
+ if (++ic >= lp.getNconnection())
+ ic = 0;
+ }
+ }
+ }
+ icstart = icnext;
+ done_all_connections = icstart == icstart1;
+ }
+ for (ic = 0; ic < lp.getNconnection(); ic++) {
+ cp = lp.getConnection(ic);
+ j = ic + 1;
+ if (j >= lp.getNconnection())
+ j = 0;
+ cpnext = lp.getConnection(j);
+ dx = bases.get(cp.getEnd()).getX() - xc;
+ dy = bases.get(cp.getEnd()).getY() - yc;
+ rc = Math.sqrt(dx * dx + dy * dy);
+ ac = Math.atan2(dy, dx);
+ if (ac < 0.0)
+ ac += 2 * Math.PI;
+ dx = bases.get(cpnext.getStart()).getX() - xc;
+ dy = bases.get(cpnext.getStart()).getY() - yc;
+ rcn = Math.sqrt(dx * dx + dy * dy);
+ acn = Math.atan2(dy, dx);
+ if (acn < 0.0)
+ acn += 2 * Math.PI;
+ if (acn < ac)
+ acn += 2 * Math.PI;
+ dan = acn - ac;
+ dcp = cpnext.getAngle() - cp.getAngle();
+ if (dcp <= 0.0)
+ dcp += 2 * Math.PI;
+ if (Math.abs(dan - dcp) > Math.PI) {
+ if (cp.isExtruded()) {
+ warningEmition("Warning from traverse_loop. Loop "
+ + lp.getNumber() + " has crossed regions\n");
+ } else if ((cpnext.getStart() - cp.getEnd()) != 1) {
+ cp.setExtruded(true);
+ continue set_radius; // remplacement du goto
+ }
+ }
+ if (cp.isExtruded()) {
+ construct_extruded_segment(cp, cpnext);
+ } else {
+ n = cpnext.getStart() - cp.getEnd();
+ if (n < 0)
+ n += nbase + 1;
+ angleinc = dan / n;
+ for (j = 1; j < n; j++) {
+ i = cp.getEnd() + j;
+ if (i > nbase)
+ i -= nbase + 1;
+ a = ac + j * angleinc;
+ rr = rc + (rcn - rc) * (a - ac) / dan;
+ bases.get(i).setX(xc + rr * Math.cos(a));
+ bases.get(i).setY(yc + rr * Math.sin(a));
+ }
+ }
+ }
+ break;
+ }
+ for (ic = 0; ic < lp.getNconnection(); ic++) {
+ if (icroot != ic) {
+ cp = lp.getConnection(ic);
+ generate_region(cp);
+ traverse_loop(cp.getLoop(), cp);
+ }
+ }
+ n = 0;
+ sx = 0.0;
+ sy = 0.0;
+ for (ic = 0; ic < lp.getNconnection(); ic++) {
+ j = ic + 1;
+ if (j >= lp.getNconnection())
+ j = 0;
+ cp = lp.getConnection(ic);
+ cpnext = lp.getConnection(j);
+ n += 2;
+ sx += bases.get(cp.getStart()).getX()
+ + bases.get(cp.getEnd()).getX();
+ sy += bases.get(cp.getStart()).getY()
+ + bases.get(cp.getEnd()).getY();
+ if (!cp.isExtruded()) {
+ for (j = cp.getEnd() + 1; j != cpnext.getStart(); j++) {
+ if (j > nbase)
+ j -= nbase + 1;
+ n++;
+ sx += bases.get(j).getX();
+ sy += bases.get(j).getY();
+ }
+ }
+ }
+ lp.setX(sx / n);
+ lp.setY(sy / n);
+ }
+
+ /**
+ * 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.
+ */
+ private void determine_radius(Loop lp, double lencut) {
+ if (debug)
+ System.out.println(" Determine_radius");
+ double mindit, ci, dt, sumn, sumd, radius, dit;
+ int i, j, end, start, imindit = 0;
+ Connection cp = new Connection(), cpnext = new Connection();
+ double rt2_2 = 0.7071068;
+
+ do {
+ mindit = 1.0e10;
+ for (sumd = 0.0, sumn = 0.0, i = 0; i < lp.getNconnection(); i++) {
+ cp = lp.getConnection(i);
+ j = i + 1;
+ if (j >= lp.getNconnection())
+ j = 0;
+ cpnext = lp.getConnection(j);
+ end = cp.getEnd();
+ start = cpnext.getStart();
+ if (start < end)
+ start += nbase + 1;
+ dt = cpnext.getAngle() - cp.getAngle();
+ if (dt <= 0.0)
+ dt += 2 * Math.PI;
+ if (!cp.isExtruded())
+ ci = start - end;
+ else {
+ if (dt <= Math.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.isExtruded() && ci > 1.0) {
+ mindit = dit;
+ imindit = i;
+ }
+ }
+ radius = sumn / sumd;
+ if (radius < rt2_2)
+ radius = rt2_2;
+ if (mindit * radius < lencut) {
+ lp.getConnection(imindit).setExtruded(true);
+ }
+ } while (mindit * radius < lencut);
+ if (lp.getRadius() > 0.0)
+ radius = lp.getRadius();
+ else
+ lp.setRadius(radius);
+ }
+
+ /**
+ * Determines if the connections cp and cpnext are connected
+ */
+ private boolean connected_connection(Connection cp, Connection cpnext) {
+ if (debug)
+ System.out.println(" Connected_connection");
+ if (cp.isExtruded()) {
+ return true;
+ } else if (cp.getEnd() + 1 == cpnext.getStart()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @throws ExceptionNAViewAlgorithm
+ */
+ private int find_ic_middle(int icstart, int icend,
+ Connection anchor_connection, Connection acp, Loop lp)
+ throws ExceptionNAViewAlgorithm {
+ if (debug)
+ System.out.println(" Find_ic_middle");
+ int count, ret, ic, i;
+ boolean done;
+
+ count = 0;
+ ret = -1;
+ ic = icstart;
+ done = false;
+ while (!done) {
+ if (count++ > lp.getNconnection() * 2) {
+ throw new ExceptionNAViewAlgorithm(
+ "Infinite loop detected in find_ic_middle");
+ }
+ if (anchor_connection != null && lp.getConnection(ic) == acp) {
+ ret = ic;
+ }
+ done = ic == icend;
+ if (++ic >= lp.getNconnection()) {
+ ic = 0;
+ }
+ }
+ if (ret == -1) {
+ for (i = 1, ic = icstart; i < (count + 1) / 2; i++) {
+ if (++ic >= lp.getNconnection())
+ ic = 0;
+ }
+ ret = ic;
+ }
+ return ret;
+ }
+
+ /**
+ * Generates the coordinates for the base pairing region of a connection
+ * given the position of the starting base pair.
+ *
+ * @throws ExceptionNAViewAlgorithm
+ */
+ private void generate_region(Connection cp) throws ExceptionNAViewAlgorithm {
+ if (debug)
+ System.out.println(" Generate_region");
+ int l, start, end, i, mate;
+ Region rp;
+
+ rp = cp.getRegion();
+ l = 0;
+ if (cp.getStart() == rp.getStart1()) {
+ start = rp.getStart1();
+ end = rp.getEnd1();
+ } else {
+ start = rp.getStart2();
+ end = rp.getEnd2();
+ }
+ if (bases.get(cp.getStart()).getX() > ANUM - 100.0
+ || bases.get(cp.getEnd()).getX() > ANUM - 100.0) {
+ throw new ExceptionNAViewAlgorithm(
+ "Bad region passed to generate_region. Coordinates not defined.");
+ }
+ for (i = start + 1; i <= end; i++) {
+ l++;
+ bases.get(i).setX(
+ bases.get(cp.getStart()).getX() + HELIX_FACTOR * l
+ * cp.getXrad());
+ bases.get(i).setY(
+ bases.get(cp.getStart()).getY() + HELIX_FACTOR * l
+ * cp.getYrad());
+ mate = bases.get(i).getMate();
+ bases.get(mate).setX(
+ bases.get(cp.getEnd()).getX() + HELIX_FACTOR * l
+ * cp.getXrad());
+ bases.get(mate).setY(
+ bases.get(cp.getEnd()).getY() + HELIX_FACTOR * l
+ * cp.getYrad());
+
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @throws ExceptionNAViewAlgorithm
+ */
+ private void construct_circle_segment(int start, int end)
+ throws ExceptionNAViewAlgorithm {
+ if (debug)
+ System.out.println(" Construct_circle_segment");
+ double dx, dy, rr, midx, midy, xn, yn, nrx, nry, mx, my, a;
+ int l, j, i;
+
+ dx = bases.get(end).getX() - bases.get(start).getX();
+ dy = bases.get(end).getY() - bases.get(start).getY();
+ rr = Math.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.get(i).setX(
+ bases.get(start).getX() + dx * (double) j / (double) l);
+ bases.get(i).setY(
+ bases.get(start).getY() + dy * (double) j / (double) l);
+ }
+ } else {
+ find_center_for_arc((l - 1), rr);
+ dx /= rr;
+ dy /= rr;
+ midx = bases.get(start).getX() + dx * rr / 2.0;
+ midy = bases.get(start).getY() + dy * rr / 2.0;
+ xn = dy;
+ yn = -dx;
+ nrx = midx + _h * xn;
+ nry = midy + _h * yn;
+ mx = bases.get(start).getX() - nrx;
+ my = bases.get(start).getY() - nry;
+ rr = Math.sqrt(mx * mx + my * my);
+ a = Math.atan2(my, mx);
+ for (j = 1; j < l; j++) {
+ i = start + j;
+ if (i > nbase)
+ i -= nbase + 1;
+ bases.get(i).setX(nrx + rr * Math.cos(a + j * angleinc));
+ bases.get(i).setY(nry + rr * Math.sin(a + j * angleinc));
+ }
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @throws ExceptionNAViewAlgorithm
+ */
+ private void construct_extruded_segment(Connection cp, Connection cpnext)
+ throws ExceptionNAViewAlgorithm {
+ if (debug)
+ System.out.println(" Construct_extruded_segment");
+ double astart, aend1, aend2, aave, dx, dy, a1, a2, ac, rr, da, dac;
+ int start, end, n, nstart, nend;
+ boolean collision;
+
+ astart = cp.getAngle();
+ aend2 = aend1 = cpnext.getAngle();
+ if (aend2 < astart)
+ aend2 += 2 * Math.PI;
+ aave = (astart + aend2) / 2.0;
+ start = cp.getEnd();
+ end = cpnext.getStart();
+ n = end - start;
+ if (n < 0)
+ n += nbase + 1;
+ da = cpnext.getAngle() - cp.getAngle();
+ if (da < 0.0) {
+ da += 2 * Math.PI;
+ }
+ if (n == 2)
+ construct_circle_segment(start, end);
+ else {
+ dx = bases.get(end).getX() - bases.get(start).getX();
+ dy = bases.get(end).getY() - bases.get(start).getY();
+ rr = Math.sqrt(dx * dx + dy * dy);
+ dx /= rr;
+ dy /= rr;
+ if (rr >= 1.5 && da <= Math.PI / 2) {
+ nstart = start + 1;
+ if (nstart > nbase)
+ nstart -= nbase + 1;
+ nend = end - 1;
+ if (nend < 0)
+ nend += nbase + 1;
+ bases.get(nstart).setX(bases.get(start).getX() + 0.5 * dx);
+ bases.get(nstart).setY(bases.get(start).getY() + 0.5 * dy);
+ bases.get(nend).setX(bases.get(end).getX() - 0.5 * dx);
+ bases.get(nend).setY(bases.get(end).getY() - 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.get(nstart).getX() - bases.get(start).getX();
+ dy = bases.get(nstart).getY() - bases.get(start).getY();
+ a1 = Math.atan2(dy, dx);
+ if (a1 < 0.0)
+ a1 += 2 * Math.PI;
+ dac = a1 - astart;
+ if (dac < 0.0)
+ dac += 2 * Math.PI;
+ if (dac > Math.PI)
+ collision = true;
+ nend = end - 1;
+ if (nend < 0)
+ nend += nbase + 1;
+ dx = bases.get(nend).getX() - bases.get(end).getX();
+ dy = bases.get(nend).getY() - bases.get(end).getY();
+ a2 = Math.atan2(dy, dx);
+ if (a2 < 0.0)
+ a2 += 2 * Math.PI;
+ dac = aend1 - a2;
+ if (dac < 0.0)
+ dac += 2 * Math.PI;
+ if (dac > Math.PI)
+ collision = true;
+ if (collision) {
+ ac = minf2(aave, astart + 0.5);
+ bases.get(nstart).setX(
+ bases.get(start).getX() + Math.cos(ac));
+ bases.get(nstart).setY(
+ bases.get(start).getY() + Math.sin(ac));
+ start = nstart;
+ ac = maxf2(aave, aend2 - 0.5);
+ bases.get(nend).setX(bases.get(end).getX() + Math.cos(ac));
+ bases.get(nend).setY(bases.get(end).getY() + Math.sin(ac));
+ end = nend;
+ n -= 2;
+ }
+ } while (collision && n > 1);
+ }
+ }
+
+ /**
+ * 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*Math.PI, and the sides of the polygon excluding the chord must
+ * have a length of 1.
+ *
+ * @throws ExceptionNAViewAlgorithm
+ */
+ private void find_center_for_arc(double n, double b)
+ throws ExceptionNAViewAlgorithm {
+ if (debug)
+ System.out.println(" Find_center_for_arc");
+ double h, hhi, hlow, r, disc, theta, e, phi;
+ int iter;
+
+ hhi = (n + 1.0) / Math.PI;
+ // changed to prevent div by zero if (ih)
+ hlow = -hhi - b / (n + 1.000001 - b);
+ if (b < 1)
+ // otherwise we might fail below (ih)
+ hlow = 0;
+ iter = 0;
+ do {
+ h = (hhi + hlow) / 2.0;
+ r = Math.sqrt(h * h + b * b / 4.0);
+ // if (r<0.5) {r = 0.5; h = 0.5*Math.sqrt(1-b*b);}
+ disc = 1.0 - 0.5 / (r * r);
+ if (Math.abs(disc) > 1.0) {
+ throw new ExceptionNAViewAlgorithm(
+ "Unexpected large magnitude discriminant = " + disc
+ + " " + r);
+ }
+ theta = Math.acos(disc);
+ // theta = 2*Math.acos(Math.sqrt(1-1/(4*r*r)));
+ phi = Math.acos(h / r);
+ e = theta * (n + 1) + 2 * phi - 2 * Math.PI;
+ if (e > 0.0) {
+ hlow = h;
+ } else {
+ hhi = h;
+ }
+ } while (Math.abs(e) > 0.0001 && ++iter < MAXITER);
+ if (iter >= MAXITER) {
+ if (noIterationFailureYet) {
+ warningEmition("Iteration failed in find_center_for_arc");
+ noIterationFailureYet = false;
+ }
+ h = 0.0;
+ theta = 0.0;
+ }
+ _h = h;
+ angleinc = theta;
+ }
+
+ private double minf2(double x1, double x2) {
+ return ((x1) < (x2)) ? (x1) : (x2);
+ }
+
+ private double maxf2(double x1, double x2) {
+ return ((x1) > (x2)) ? (x1) : (x2);
+ }
+
+ public void warningEmition(String warningMessage) throws ExceptionNAViewAlgorithm {
+ throw (new ExceptionNAViewAlgorithm(warningMessage));
+ }
+}