Changeset 2885


Ignore:
Timestamp:
06/18/07 14:50:42 (12 years ago)
Author:
sorber
Message:

Added class FreeformExtension; moved some FreeformTool logic to this class.
Moved some methods from FreeformTool to utility classes. Put thresholds and other constants into a separate class in overlays package.

Location:
trunk/loci/visbio
Files:
2 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/loci/visbio/overlays/FreeformTool.java

    r2875 r2885  
    4646   * must be dragged before new node is added. 
    4747   */ 
    48   protected static final double DRAW_THRESH = 2.0; 
     48  protected static final double DRAW_THRESH = 
     49    OverlayNumericStrategy.getDrawThreshold(); 
    4950 
    5051  /** 
     
    5253   * in order to erase it 
    5354   */ 
    54   protected static final double ERASE_THRESH = 10.0; 
     55  protected static final double ERASE_THRESH = 
     56    OverlayNumericStrategy.getEraseThreshold(); 
    5557 
    5658  /** Threshhold within which click must occur to invoke edit mode. */ 
    57   protected static final double EDIT_THRESH = 6.0; 
     59  protected static final double EDIT_THRESH = 
     60    OverlayNumericStrategy.getEditThreshold(); 
    5861 
    5962  /** 
    6063   * Threshhold within which click must occur to 
    61    * invoke extend mode or reconnect a tendril. 
    62    */ 
    63   protected static final double RECONNECT_THRESH = 1.0; 
     64   * invoke extend mode or reconnect a freeformExtension. 
     65   */ 
     66  protected static final double RECONNECT_THRESH = 
     67    OverlayNumericStrategy.getReconnectThreshold(); 
    6468 
    6569  /** How close mouse must be to end node to resume drawing */ 
    66   protected static final double RESUME_THRESH = 10.0; 
     70  protected static final double RESUME_THRESH = 
     71    OverlayNumericStrategy.getResumeThreshold(); 
    6772 
    6873  /** Smoothing factor for "single exponential smoothing" */ 
    69   protected static final float S = 0.35f; 
     74  protected static final float S = (float) 
     75    OverlayNumericStrategy.getSmoothingFactor(); 
    7076 
    7177  /** Constant for "erase" mode. */ 
     
    9298  protected Vector otherFreefs; 
    9399 
    94   /** Tendril wraps info about an edit to a curve */ 
    95   protected Tendril tendril; 
     100  /** FreeformExtension wraps info about an edit to a curve */ 
     101  protected FreeformExtension freeformExtension; 
    96102 
    97103  /** Stores the current mode (INIT, ERASE, CHILL, DRAW, EDIT) */ 
    98104  protected int mode; 
    99  
    100   /** Chunks of curve tracked during EDIT */ 
    101   protected float[][] pre, post; 
    102105 
    103106  /** Point at which mouseDown occurs. */ 
     
    109112  public FreeformTool(OverlayTransform overlay) { 
    110113    super(overlay, "Freeform", "Freeform", "freeform.png"); 
    111     tendril = null; 
     114    freeformExtension = null; 
    112115    otherFreefs = new Vector(); 
    113116    setMode(CHILL); 
     
    174177              float[] a = freeform.getNodeCoords(seg+1); 
    175178              freeform.insertNode(seg+2, a[0], a[1]); 
    176               tendril = new Tendril (seg+1, seg+2, true); 
    177               splitNodes (freeform, seg, seg+3); 
     179              freeformExtension = new FreeformExtension(freeform, seg+1, seg+2, 
     180                  true); 
    178181            } 
    179182            else { 
    180183              // determine projection on seg. 
    181               // insert a pair of nodes there, starting a tendril 
     184              // insert a pair of nodes there, starting a freeformExtension 
    182185              float[] a = freeform.getNodeCoords(seg); 
    183186              float[] b = freeform.getNodeCoords(seg + 1); 
    184               float[] newXY = computePtOnSegment(a, b, (float) weight); 
     187              float[] newXY = MathUtil.computePtOnSegment(a, b, (float) weight); 
    185188              freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
    186189              freeform.insertNode(seg + 1, newXY[0], newXY[1]); 
    187               tendril = new Tendril(seg+1, seg+2, false); 
    188               splitNodes (freeform, seg+1, seg+3); 
     190              freeformExtension = new FreeformExtension(freeform, seg+1, seg+2, 
     191                  false); 
    189192            } 
    190193            setMode(EDIT); 
     
    257260 
    258261      if (minDist < DRAW_THRESH) { 
    259         System.out.println("I'm here"); 
    260         System.out.println("freeform has the following nodes:"); 
    261         freeform.printNodes(); 
    262         System.out.println("other freeform has the following nodes:"); 
    263         OverlayFreeform other = (OverlayFreeform) otherFreefs.get(index); 
    264         other.printNodes(); 
    265         String s = closerToHead ? "head" : "tail"; 
    266         System.out.println("connecting freeform to " + s +  
    267             " of other freeform"); 
    268  
    269262        connectFreeforms(freeform, 
    270263          (OverlayFreeform) otherFreefs.get(index), closerToHead); 
     
    279272 
    280273        if (distPxl > DRAW_THRESH) { 
    281           float[] s = smooth(new float[]{dx, dy}, last); 
     274          float[] s = OverlayUtil.smooth(new float[]{dx, dy}, last, S); 
    282275          freeform.setNextNode(s); 
    283276          double len = freeform.getCurveLength(); 
     
    305298    } 
    306299    else if (mode == EDIT) { 
    307       // extend tendril and or reconnect 
     300      // extend freeformExtension and or reconnect 
    308301      // get coords of prev node 
    309       float[] prvCrdsFlt; 
    310       if (tendril.tip >= 0) { 
    311         // previous node is tendril.tip 
    312         prvCrdsFlt = freeform.getNodeCoords(tendril.tip); 
    313       } 
    314       else { 
    315         // previous node is tendril.start 
    316         prvCrdsFlt = freeform.getNodeCoords(tendril.start); 
    317       } 
    318  
    319       double[] prvCrdsDbl= {(double) prvCrdsFlt[0], (double) prvCrdsFlt[1]}; 
    320       int[] prvCrdsPxl = CursorUtil.domainToPixel(display, prvCrdsDbl); 
    321       double[] prvCrdsPxlDbl = {(double) prvCrdsPxl[0], (double) prvCrdsPxl[1]}; 
    322  
    323       // coords of this mouseDrag event 
    324       double[] drag = {dpx, dpy}; 
    325       double dragDist = MathUtil.getDistance(drag, prvCrdsPxlDbl); 
    326  
    327       // compute distance to pre, post chunks of curve 
    328       // floats and ints 
    329       double[][] preDbl = floatsToPixelDoubles(display, pre); 
    330       double[][] postDbl = floatsToPixelDoubles(display, post); 
    331  
    332       double[] distSegWtPre = MathUtil.getDistSegWt(preDbl, dpx, dpy); 
    333       double[] distSegWtPost = MathUtil.getDistSegWt(postDbl, dpx, dpy); 
    334  
    335       boolean equalsCase = false; 
    336       if (distSegWtPre[0] == distSegWtPost[0]) equalsCase = true; 
    337       // Drag is equally close to both segments, wait for next drag. 
    338       // This case is extremely unlikely. 
    339  
    340       boolean closerToPost = (distSegWtPre[0] > distSegWtPost[0]); 
    341       double[] distSegWt = (closerToPost) ? distSegWtPost : distSegWtPre; 
    342       double minDist = distSegWt[0]; 
    343       int seg = (int) distSegWt[1]; 
    344       double weight = distSegWt[2]; 
    345       // ACS TODO what about equals case? wait for next drag? 
    346  
    347       // if not close to curve insert node, else reconnect 
    348       if (minDist > RECONNECT_THRESH) { 
    349         // insert a node at the drag point if drag went far enough 
    350         if (dragDist > DRAW_THRESH) { 
    351           // first drag only 
    352           float[] s = smooth (new float[] {dx, dy}, prvCrdsFlt); 
    353           if (tendril.tip < 0) { 
    354             freeform.insertNode(tendril.stop, s[0], s[1]); 
    355             tendril.stop++; 
    356             tendril.tip = tendril.start + 1; 
    357           } 
    358           else { // later drags 
    359             freeform.insertNode(tendril.tip+1, prvCrdsFlt[0], prvCrdsFlt[1]); 
    360             freeform.insertNode(tendril.tip+1, s[0], s[1]); 
    361             tendril.tip++; 
    362             tendril.stop += 2; 
    363           } 
    364         } 
    365         // tendril.tip always points to a lone node (except for before first 
    366         // drag, where it doesn't point to a node at all) 
    367       } 
    368       else if (!shift && !equalsCase) { 
    369         // reconnect with curve and delete nodes; 
    370         if (tendril.tip < 0) { 
    371           // tendril hasn't begun.  Do nothing. 
    372         } 
    373         else { 
    374           //if (dragDist > DRAW_THRESH) { 
    375             // insert a node first at drag point, then reconnect 
    376             // skip this case:  There's the possibility that this drag point 
    377             // is across the curve from the previous drag point, which might 
    378             // result in a unintended zig-zag. 
    379           //} 
    380  
    381           // insert node at nearest point 
    382           int offset = (closerToPost) ? 1 + tendril.stop : 0; 
    383           // offset points to either post[0] or pre[0] 
    384           int endIndex = 0; // lame initial value. 
    385           if (weight == 0.0) { 
    386             endIndex = seg + offset; 
    387           } 
    388           else if (weight == 1.0) { 
    389             endIndex = seg + 1 + offset; 
    390           } 
    391           else { 
    392             // determine projection on seg. 
    393             float[] a = freeform.getNodeCoords(seg + offset); 
    394             float[] b = freeform.getNodeCoords(seg + 1 + offset); 
    395             float[] newXY = computePtOnSegment(a, b, (float) weight); 
    396             int insertIndex = seg + 1 + offset; 
    397             freeform.insertNode(insertIndex, newXY[0], newXY[1]); 
    398             if (!closerToPost) { 
    399               tendril.increment(); 
    400             } 
    401             endIndex = insertIndex; 
    402           } 
    403  
    404           if (tendril.tip < endIndex) { 
    405             freeform.deleteBetween(tendril.tip, endIndex); 
    406           } 
    407           else { 
    408             freeform.deleteBetween(endIndex, tendril.tip); 
    409           } 
    410           freeform.computeLength(); 
    411           setMode(CHILL); 
    412         } 
    413       } // end reconnect logic 
    414  
     302      boolean reconnected = freeformExtension.extendOrReconnect(display, dx, dy, 
     303          px, py, shift); 
     304      if (reconnected) { 
     305        freeform.computeLength(); 
     306        setMode(CHILL); 
     307      } 
    415308    } 
    416309    overlay.notifyListeners(new TransformEvent(overlay)); 
     
    424317    } 
    425318    else if (mode == EDIT) { 
    426       // save coords of tendril root 
    427       float[] c = freeform.getNodeCoords(tendril.start); 
    428       freeform.deleteBetween(tendril.start-1, tendril.stop+1); 
    429       if (tendril.nodal) { 
    430         freeform.insertNode(tendril.start, c[0], c[1]); 
     319      // save coords of freeformExtension root 
     320      float[] c = freeform.getNodeCoords(freeformExtension.start); 
     321      freeform.deleteBetween(freeformExtension.start-1, 
     322          freeformExtension.stop+1); 
     323      if (freeformExtension.nodal) { 
     324        freeform.insertNode(freeformExtension.start, c[0], c[1]); 
    431325      } 
    432326    } 
     
    439333  // -- Helper methods for mouse methods 
    440334 
    441   /** Calculates smoothed coordinates using "single exponential smoothing" 
    442    *  as described in Littlewood and Inman, _Computer-assisted DNA length 
    443    *  measurements..._. Nucleic Acids Research, V 10 No. 5. (1982) p. 1694 
    444    */ 
    445   private float[] smooth(float[] un, float[] cn1) { 
    446     float[] cn = new float[2]; 
    447     for (int i=0; i<2; i++) { 
    448       cn[i] = S * un[i] + (1 - S) * cn1[i]; 
    449     } 
    450     return cn; 
    451   } 
    452  
    453   /** 
    454    * Splits the node array into two parts.  The first part goes from a[0] to 
    455    * a[index-1], the second from a[index2] to a[a.length -1]. 
    456    */ 
    457   private void splitNodes(OverlayFreeform freeform, int index, int index2) { 
    458     // splits the array a into two (before the index specified) 
    459     float[][] a = freeform.getNodes(); 
    460     // print these guys 
    461     int depth = a.length; 
    462     int len = a[0].length; // assumes non-ragged array 
    463     pre =  new float[depth][index]; 
    464     post = new float[depth][len-index2]; 
    465     for (int i=0; i < depth; i++) { 
    466       System.arraycopy(a[i], 0, pre[i], 0, index); 
    467       System.arraycopy(a[i], index2, post[i], 0, len-index2); 
    468     } 
    469   } 
    470335 
    471336  /** Compiles a list of other freeforms at the current dimensional position. */ 
     
    614479          // Should the getDistance method return a distance in 
    615480          // pixel coordinates? 
    616           double[][] nodesDbl = floatsToPixelDoubles(display, 
     481          double[][] nodesDbl = OverlayUtil.floatsToPixelDoubles(display, 
    617482              currentFreeform.getNodes()); 
    618483          double[] distSegWt = 
     
    630495  } 
    631496 
    632   /** 
    633    * Computes a point along the line segment 
    634    * a[]-b[] (2D) based on parameter weight. 
    635    */ 
    636   private float[] computePtOnSegment(float[] a, float[] b, float weight) { 
    637     float dx = b[0] - a[0]; 
    638     float dy = b[1] - a[1]; 
    639     float newX = (float) (a[0] + dx * weight); 
    640     float newY = (float) (a[1] + dy * weight); 
    641     float[] retvals = {newX, newY}; 
    642     return retvals; 
    643   } 
    644  
    645497  /** Gets distance in pixels between a click and a point in domain coords. */ 
    646498  private double getPixelDistanceToClick(DisplayImpl display, float[] p, int[] 
     
    656508  } 
    657509 
    658   /** Casts an array of floats to doubles. */ 
    659   private double[][] floatsToPixelDoubles(DisplayImpl d, float[][] nodes) { 
    660     double[][] nodesDbl = new double[nodes.length][nodes[0].length]; 
    661     for (int j=0; j<nodes[0].length; j++) { 
    662       int[] c = CursorUtil.domainToPixel(d, new double[]{ 
    663         (double) nodes[0][j], (double) nodes[1][j]}); 
    664       nodesDbl[0][j] = (double) c[0]; 
    665       nodesDbl[1][j] = (double) c[1]; 
    666     } 
    667     return nodesDbl; 
    668   } 
    669  
    670510  /** Prints node array of current freeform; for debugging. */ 
    671511  private void printNodes(float[][] nodes) { 
     
    693533    } 
    694534  } 
    695  
    696535 
    697536  // -- Inner Classes --  
     
    744583    } 
    745584  } 
    746  
    747   /**  
    748    * The Tendril class wraps information for the temporary section of a  
    749    * freeform, which appears like a 'tendril' on screen, created during an edit 
    750    * of an existing freeform.  
    751    * The FreeformTool uses this information to modify the freeform if the 
    752    * edit is successful, or to remove the tendril if the edit is aborted. 
    753    * 
    754    * The section of the freeform represented by a tendril is actually a 
    755    * loop, not a single line, like loop of thread pushed through a needle.  
    756    * The loop has two parts, between start and tip and between tip and stop. 
    757    * If an edit is completed, half of the tendril  
    758    */ 
    759   protected class Tendril { 
    760      
    761     // -- Fields -- 
    762  
    763     /** The index of the last node of the tendril. */ 
    764     public int start; 
    765  
    766     /** The index of the last node of the tendril. */ 
    767     public int stop;  
    768  
    769     /** The index of the tip node of the tendril. */ 
    770     public int tip; 
    771  
    772     /** Whether this tendril began on a node or in the middle of a segment. */ 
    773     public boolean nodal; 
    774  
    775     /** Constructs a new tendril. */ 
    776     public Tendril (int start, int stop, boolean nodal) { 
    777       this.start = start; 
    778       this.stop = stop; 
    779       this.nodal = nodal; 
    780       // initial value of tip is nonsensical.  Use as a check. 
    781       this.tip = -1; 
    782     } 
    783  
    784     /** Update the tendril to reflect that another node has been drawn. */ 
    785     public void increment() { 
    786       tendril.tip++; 
    787       tendril.start++; 
    788       tendril.stop++; 
    789     } 
    790   } 
    791585} // end class FreeformTool 
  • trunk/loci/visbio/overlays/OverlayUtil.java

    r2865 r2885  
    10641064  } 
    10651065 
     1066  // -- Math Methods -- 
     1067 
     1068  /** Calculates smoothed coordinates using "single exponential smoothing" 
     1069   *  as described in Littlewood and Inman, _Computer-assisted DNA length 
     1070   *  measurements..._. Nucleic Acids Research, V 10 No. 5. (1982) p. 1694 
     1071   */ 
     1072  public static float[] smooth(float[] un, float[] cn1, float S) { 
     1073    float[] cn = new float[2]; 
     1074    for (int i=0; i<2; i++) { 
     1075      cn[i] = S * un[i] + (1 - S) * cn1[i]; 
     1076    } 
     1077    return cn; 
     1078  } 
     1079 
     1080  /** Casts an array of floats to doubles. */ 
     1081  public static double[][] floatsToPixelDoubles(DisplayImpl d, float[][] nodes) { 
     1082    double[][] nodesDbl = new double[nodes.length][nodes[0].length]; 
     1083    for (int j=0; j<nodes[0].length; j++) { 
     1084      int[] c = CursorUtil.domainToPixel(d, new double[]{ 
     1085        (double) nodes[0][j], (double) nodes[1][j]}); 
     1086      nodesDbl[0][j] = (double) c[0]; 
     1087      nodesDbl[1][j] = (double) c[1]; 
     1088    } 
     1089    return nodesDbl; 
     1090  } 
     1091 
    10661092  /** Prints a VisAD style group of points. */ 
    10671093  public static void print(float[][] points) { 
  • trunk/loci/visbio/util/MathUtil.java

    r2870 r2885  
    247247    double[] retvals = {minDist, (double) seg, weight}; 
    248248    return retvals; 
     249  } 
     250 
     251  /** 
     252   * Computes a point along the line segment a[]-b[] based on the parameter 
     253   * weight. 
     254   */ 
     255  public static float[] computePtOnSegment(float[] a, float[] b, float weight) { 
     256    if (a.length != b.length) return null; 
     257    int len = a.length; 
     258    float[] p = new float[len]; 
     259    for (int i=0; i<len; i++) { 
     260      float d = b[i] - a[i]; 
     261      p[i] = a[i] + d * weight; 
     262    } 
     263    return p; 
    249264  } 
    250265 
Note: See TracChangeset for help on using the changeset viewer.