Changeset 1796
 Timestamp:
 11/15/06 14:23:31 (13 years ago)
 Location:
 trunk/loci/visbio/overlays
 Files:

 3 edited
Legend:
 Unmodified
 Added
 Removed

trunk/loci/visbio/overlays/FreeformTool.java
r1724 r1796 33 33 //  Constants  34 34 35 protected static final double DRAWTHRESH = 2.0; // When drawing, how far mouse must be dragged before a new node is added 36 protected static final double DRAGTHRESH = 2.0; // When editing, how far mouse must be dragged before a new node is added 37 //DRAWTHRESH and DRAGTHRESH ought to be the same 38 protected static final double EDITTHRESH = 5.0; // Threshhold within which click must occur to invoke edit mode 35 /** When drawing or editing, how far mouse must be dragged before new node is added */ 36 protected static final double DRAW_THRESH = 2.0; 37 /** Threshhold within which click must occur to invoke edit mode */ 38 protected static final double EDIT_THRESH = 5.0; 39 /** Threshhold within which click must occur to invoke extend mode or reconnect a tendril */ 40 protected static final double RECONNECT_THRESH = 0.5; 39 41 40 42 //  Fields  … … 42 44 /** Curve currently being drawn or modified. */ 43 45 protected OverlayFreeform freeform; 44 45 protected boolean editing; // whether the tool is in edit mode46 protected int prevNodeIndex, nextNodeIndex; // used in editing to track changes to the curve47 46 47 /** Tendril wraps info about an edit to a curve */ 48 protected Tendril tendril; 49 50 protected boolean editing, extending; // whether the tool is in edit mode or extend mode; 51 protected int prevNodeIndex, inserted; // used in editing to track changes to the curve 52 protected float[][] pre, post; // chunks of curve tracked in redrawing 53 48 54 //  Constructor  49 55 … … 51 57 public FreeformTool(OverlayTransform overlay) { 52 58 super(overlay, "Freeform", "Freeform", "freeform.png"); 53 editing = false; 54 prevNodeIndex = nextNodeIndex = 0; 55 } 59 editing = extending = false; 60 prevNodeIndex = inserted = 0; 61 tendril = null; 62 } 63 56 64 //  OverlayTool API methods  57 65 … … 71 79 OverlayFreeform target = getClosestFreeform(x, y); 72 80 73 if (target == null) { 74 //System.out.println("target freeform for editing == null");// TEMP 81 if (target == null) { // not close to any freeforms 75 82 deselectAll(); 76 83 freeform = new OverlayFreeform(overlay, x, y, x, y); 77 84 configureOverlay(freeform); 78 85 overlay.addObject(freeform, pos); 86 extending = true; 79 87 } else { 80 // ACS TODO setSelected for freeform under edit 81 // closestFreeform.setSelected(true); 88 // ACS TODO set selected in Overlays window for freeform under edit 82 89 freeform = target; 83 //print("mouseDown", "Entering edit mode"); // TEMP 84 editing = true; 85 86 double[] distSegWt = freeform.getDistanceEtc(x, y); 90 editing = true; // enter edit mode 91 92 double[] distSegWt = MathUtil.getDistSegWt(freeform.getNodes(), x, y); 87 93 88 94 double dist = distSegWt[0]; … … 92 98 // criteria for entering extend mode or edit mode 93 99 if (seg + 2 == freeform.getNumNodes() && weight == 1.0) { // extend 94 //print("mouseDown", "case 1: nearest point is last node"); // TEMP95 100 editing = false; 101 extending = true; 96 102 } else if (seg == 0 && weight == 0.0) { // extend 97 //print("mouseDown", "case 2: nearest point is first node"); // TEMP98 103 freeform.reverseNodes(); 99 104 editing = false; 105 extending = true; 100 106 } else { // edit 101 //print("mouseDown", "case 3: nearest point is interior"); // TEMP102 107 // project click onto curve and insert a new node there, 103 108 // unless nearest point on curve is a node itself 104 109 // Register this node as previous node 105 110 106 // TEMP print the node array 107 /* 108 float[][] nodes = freeform.getNodes(); // TEMP 109 //print the nodes 110 for (int i=0; i<nodes[0].length; i++) // TEMP 111 System.out.println ("(" + nodes[0][0]+ "," + nodes[1][i]+")"); // TEMP 112 */ 113 //print ("mouseDown", ("x = " + x + " y = " + y + " seg = " + seg)); // TEMP 114 if (weight == 0.0) prevNodeIndex = seg; // nearest point on seg is the start node 115 else if (weight == 1.0) prevNodeIndex = seg + 1; // nearest point on seg is the end node 116 else { 111 if (weight == 0.0) { 112 // nearest point on seg is the start node 113 float[] a = freeform.getNodeCoords(seg); // determine projection on seg. 114 freeform.insertNode (seg+1, a[0], a[1]); 115 tendril = new Tendril (seg, seg+1, true); 116 splitNodes (seg1, seg+2); 117 } else if (weight == 1.0) { 118 // nearest point on seg is the end node 119 float[] a = freeform.getNodeCoords(seg+1); // determine projection on seg. 120 freeform.insertNode(seg+2, a[0], a[1]); 121 tendril = new Tendril (seg+1, seg+2, true); 122 splitNodes (seg, seg+3); 123 } else { 117 124 float[] a = freeform.getNodeCoords(seg); // determine projection on seg. 118 125 float[] b = freeform.getNodeCoords(seg + 1); 119 float dx = b[0]  a[0]; 120 float dy = b[1]  a[1]; 121 float newX = (float) (a[0] + dx * weight); 122 float newY = (float) (a[1] + dy * weight); 123 //print ("mouseDown", ("seg ("+a[0]+","+a[1]+")>("+b[0]+","+b[1]+")"));// TEMP 124 //print ("mouseDown", ("insert prevNode at ("+newX+","+newY+")"));// TEMP 125 freeform.insertNode(seg + 1, newX, newY); 126 prevNodeIndex = seg + 1; 127 } 128 } 126 float[] newXY = computePtOnSegment(a, b, (float) weight); 127 freeform.insertNode(seg + 1, newXY[0], newXY[1]); 128 freeform.insertNode(seg + 1, newXY[0], newXY[1]); 129 tendril = new Tendril(seg+1, seg+2, false); 130 splitNodes (seg+1, seg+3); 131 } // end else 132 } // end else 133 } // end else 134 } // end mouseDown 135 136 /** Wraps info for redrawing a curve. Appears like a 'tendril' on screen */ 137 private class Tendril { 138 public int start, stop, tip; 139 public boolean nodal; 140 141 public Tendril (int start, int stop, boolean nodal) { 142 this.start = start; 143 this.stop = stop; 144 this.nodal = nodal; 145 // initial value of tip is nonsensical. Use as a check. 146 this.tip = 1; 147 } 148 149 public void increment() { 150 tendril.tip++; 151 tendril.start++; 152 tendril.stop++; 153 } 154 } 155 156 /** Splits the node array into two parts. The first part goes from a[0] to a[index1], the second 157 * from a[index2] to a[a.length 1] */ 158 private void splitNodes (int index, int index2) { 159 // splits the array a into two (before the index specified) 160 float[][] a = freeform.getNodes(); 161 // print these guys 162 int depth = a.length; 163 int len = a[0].length; // assumes nonragged array 164 pre = new float[depth][index]; 165 post = new float[depth][lenindex2]; 166 for (int i=0; i < depth; i++) { 167 System.arraycopy(a[i], 0, pre[i], 0, index); 168 System.arraycopy(a[i], index2, post[i], 0, lenindex2); 129 169 } 130 170 } … … 132 172 /** Instructs this tool to respond to a mouse release. */ 133 173 public void mouseUp(float x, float y, int[] pos, int mods) { 134 //prevNodeIndex = 1; 135 //cleanup 174 if (editing && tendril !=null) { 175 // save coords of tendril root 176 float[] c = freeform.getNodeCoords(tendril.start); 177 freeform.deleteBetween(tendril.start1, tendril.stop+1); 178 if (tendril.nodal) { 179 freeform.insertNode(tendril.start, c[0], c[1]); 180 printNodes(); 181 } 182 } 183 184 extending = editing = false; 136 185 deselectAll(); 137 186 if (freeform != null) { 187 inserted = 0; 138 188 editing = false; 139 189 freeform.truncateNodeArray(); … … 148 198 /** Instructs this tool to respond to a mouse drag. */ 149 199 public void mouseDrag(float x, float y, int[] pos, int mods) { 150 boolean shift = (mods & InputEvent.SHIFT_MASK)!=0; 151 // deactivates tendrillike behavior when dragging perpendicular to curve 152 153 /* 154 // WARNING: causes array out of bounds errors down the line 155 // try this to check for entering extend mode 156 if (freeform.getNumNodes() > 3){ 157 if (prevNodeIndex + 2 == freeform.getNumNodes()) { 158 editing = false; 159 freeform.deleteNode(prevNodeIndex + 1); 160 } else if (prevNodeIndex == 1) { 161 editing = false; 162 freeform.deleteNode(prevNodeIndex  1); 163 prevNodeIndex; 200 boolean shift = (mods & InputEvent.SHIFT_MASK) != 0; 201 // NEW!! holding shift suppresses reconnect routine, so you can redraw close to the curve. 202 // Why not use the wacom pen's click too? TODO 203 // I think the pen switch is mapped to Right Click by default, which is already grabbed by VisAD 204 205 if (editing) { 206 207 // get coords of prev node 208 float[] pcoordsf; 209 if (tendril.tip >= 0) { 210 // previous node is tendril.tip 211 pcoordsf = freeform.getNodeCoords(tendril.tip); 212 } else { 213 // previous node is tendril.start 214 pcoordsf = freeform.getNodeCoords(tendril.start); 164 215 } 165 } 166 */ 167 168 if (editing) { 169 // get coords of prev node 170 float[] pcoordsf = freeform.getNodeCoords(prevNodeIndex); 216 171 217 double[] pcoordsd = {(double) pcoordsf[0], (double) pcoordsf[1]}; 172 218 double[] thiscoords = {(double) x, (double) y}; // coords of this mouseDrag event 219 double dragDist = MathUtil.getDistance(pcoordsd, thiscoords); 173 220 174 // if mouseDrag went far enough 175 if (MathUtil.getDistance(pcoordsd, thiscoords) > DRAGTHRESH) { 176 // get distance to curve, nearest segment, weight (to determine nearest point on seg) 177 double[] distSegWt = freeform.getDistanceEtc(x, y); 178 double dist = distSegWt[0]; 179 int seg = (int) distSegWt[1]; 180 double weight = distSegWt[2]; 181 182 //print ("mouseDrag", ("nearest seg = " +seg+ " to " + (seg+1))); // TEMP 183 184 // Determine whether to switch into extend mode 185 if (seg + 2 == freeform.getNumNodes() && weight == 1.0) { // extend 186 //print("mouseDrag", "case 1: nearest point is last node"); // TEMP 187 // nearest node is last node 188 //freeform.setNodeCoords(seg+2, x, y); 221 // compute distance to pre, post chunks of curve 222 double[] distSegWtPre = MathUtil.getDistSegWt(pre, x, y); 223 double[] distSegWtPost = MathUtil.getDistSegWt(post, x, y); 224 225 boolean equalsCase = false; 226 if (distSegWtPre[0] == distSegWtPost[0]) equalsCase = true; 227 // Drag is equally close to both segments, wait for next drag. 228 // This case is extremely unlikely. 229 230 boolean closerToPost = (distSegWtPre[0] > distSegWtPost[0]); 231 double[] distSegWt = (closerToPost) ? distSegWtPost : distSegWtPre; 232 double minDist = distSegWt[0]; 233 int seg = (int) distSegWt[1]; 234 double weight = distSegWt[2]; 235 // ACS TODO what about equals case? wait for next drag? 236 237 // if not close to curve insert node, else reconnect 238 if (minDist > RECONNECT_THRESH) { 239 // insert a node at the drag point if drag went far enough 240 if (dragDist > DRAW_THRESH) { 241 // first drag only 242 if (tendril.tip < 0) { 243 freeform.insertNode(tendril.stop, x, y); 244 tendril.stop++; 245 tendril.tip = tendril.start + 1; 246 } else { // later drags 247 freeform.insertNode(tendril.tip+1, pcoordsf[0], pcoordsf[1]); 248 freeform.insertNode(tendril.tip+1, x, y); 249 tendril.tip++; 250 tendril.stop += 2; 251 } 252 } 253 // tendril.tip always points to a lone node (except for before first drag, where it doesn't point to a 254 // node at all 255 256 } else if (!shift && !equalsCase) { // reconnect with curve and delete nodes; 257 if (tendril.tip < 0) { 258 // tendril hasn't begun. Do nothing. 259 } else { 260 //if (dragDist > DRAW_THRESH) { 261 // insert a node first at drag point, then reconnect 262 // skip this case: There's the possibility that this drag point 263 // is across the curve from the previous drag point, which might 264 // result in a unintended zigzag. 265 //} 266 267 // insert node at nearest point 268 int offset = (closerToPost) ? 1 + tendril.stop : 0; 269 // offset points to either post[0] or pre[0] 270 int endIndex = 0; // lame initial value. 271 if (weight == 0.0) { 272 endIndex = seg + offset; 273 } else if (weight == 1.0) { 274 endIndex = seg + 1 + offset; 275 } else { 276 float[] a = freeform.getNodeCoords(seg + offset); // determine projection on seg. 277 float[] b = freeform.getNodeCoords(seg + 1 + offset); 278 float[] newXY = computePtOnSegment (a, b, (float) weight); 279 int insertIndex = seg + 1 + offset; 280 freeform.insertNode(insertIndex, newXY[0], newXY[1]); 281 if (!closerToPost) { 282 tendril.increment(); 283 } 284 endIndex = insertIndex; 285 } 286 287 if (tendril.tip < endIndex) { 288 freeform.deleteBetween(tendril.tip, endIndex); 289 } else { 290 freeform.deleteBetween(endIndex, tendril.tip); 291 } 189 292 editing = false; 190 //return; 191 } else if (seg == 0 && weight == 0.0) { // reverse and extend 192 //print("mouseDrag", "case 2: nearest point is first node"); // TEMP 193 // nearest node is first node 194 //freeform.setNodeCoords(0, x, y); 195 freeform.reverseNodes(); 196 editing = false; 197 //return; 198 } else { 199 //print("mouseDrag", "case 3: nearest point is an interior node"); // TEMP 200 if (weight == 0.0) { 201 //print ("mouseDrag", "nearest point was node " + seg ); // TEMP 202 nextNodeIndex = seg; // nearest point on seg is the start node 203 } else if (weight == 1.0) { 204 nextNodeIndex = seg + 1; // nearest point on seg is the end node 205 //print ("mouseDrag", "nearest point was node " + (seg+1)); // TEMP 206 } else { 207 //print("mouseDrag", "determining projection coords."); 208 float[] a = freeform.getNodeCoords(seg); // determine projection on seg. 209 float[] b = freeform.getNodeCoords(seg + 1); 210 float dx = b[0]  a[0]; 211 float dy = b[1]  a[1]; 212 float newX = (float) (a[0] + dx * weight); 213 float newY = (float) (a[1] + dy * weight); 214 //print ("mouseDrag", ("seg ("+a[0]+","+a[1]+")>("+b[0]+","+b[1]+")"));// TEMP 215 //print ("mouseDrag", ("insert nextNode at ("+newX+","+newY+")"));// TEMP 216 freeform.insertNode(seg + 1, newX, newY); 217 nextNodeIndex = seg + 1; 218 if (seg + 1 <= prevNodeIndex) prevNodeIndex++; // increment since insert occurs before 219 } 220 221 //debug(x, y); // TEMP 222 //print ("mouseDrag", ("prevNodeIndex=" + prevNodeIndex + " nextNodeIndex=" + nextNodeIndex)); // TEMP 223 if (prevNodeIndex == nextNodeIndex && !shift) { 224 // replace prevnode with two colocational nodes, insert new node at drag point 225 float[] prev = freeform.getNodeCoords(prevNodeIndex); 226 freeform.insertNode(prevNodeIndex+1, prev[0], prev[1]); 227 freeform.insertNode(prevNodeIndex+1, x, y); 228 prevNodeIndex++; 229 } else if (prevNodeIndex > nextNodeIndex) { 230 //print ("mouseDrag", "prevNode > nextNode. Deleting between nextNode and prevNode"); // TEMP 231 //debug(x,y); // TEMP 232 freeform.deleteBetween(nextNodeIndex, prevNodeIndex); 233 freeform.insertNode(nextNodeIndex + 1, x, y); // insert drag point just after next node/just before prevNode 234 prevNodeIndex = nextNodeIndex + 1; 235 //print ("mouseDrag", ("inserted node at nextNodeIndex + 1, prevNodeIndex = nextNodeIndex + 1 = " + prevNodeIndex)); // TEMP 236 //debug(x, y); // TEMP 237 } else if (nextNodeIndex > prevNodeIndex) { 238 //print ("mouseDrag", "prevNode < nextNode. Deleting between nextNode and prevNode"); // TEMP 239 int victims = Math.abs(nextNodeIndex  prevNodeIndex)  1; 240 freeform.deleteBetween(prevNodeIndex, nextNodeIndex); 241 freeform.insertNode(nextNodeIndex  victims, x, y); // insert before next node (now shifted) 242 prevNodeIndex = nextNodeIndex  victims; 243 } 244 } // distance < DRAGTHRESH, keep waiting. 245 } // if block corresponding to check whether extend 246 } else { // end if editing.... 247 //print ("mouseDrag", "extend mode"); // TEMP 293 } 294 } // end reconnect logic 295 } else if (extending) { 248 296 deselectAll(); 249 297 float dx = x  freeform.getLastNodeX(); … … 251 299 double dist = Math.sqrt (dx*dx + dy*dy); 252 300 253 if (dist > DRAWTHRESH) { 254 //print("mouseDrag", ("distance sufficiently large. New node created at (" + x + "," + y + ")")); // TEMP 301 if (dist > RECONNECT_THRESH) { 255 302 freeform.setNextNode(x, y); 256 303 } 257 } // end else 258 overlay.notifyListeners(new TransformEvent(overlay)); 259 }// end mouseDrag 260 304 } else { 305 // case !extending !editing 306 // Q: can you be both not extending and not editing? 307 // A: yes, after succesfully redrawing a segment of the curve. Further drags do nothing for now. 308 } 309 310 overlay.notifyListeners(new TransformEvent(overlay)); 311 } // end mouseDrag 312 261 313 // Additional methods 262 314 … … 274 326 if (currentObject instanceof OverlayFreeform) { 275 327 OverlayFreeform currentFreeform = (OverlayFreeform) currentObject; 276 // rough check: is point within CLICKTHRESH of bounding box (fast)277 if (currentFreeform.getDistance(x, y) < EDIT THRESH) {328 // rough check: is point within EDIT_THRESH of bounding box (fast) 329 if (currentFreeform.getDistance(x, y) < EDIT_THRESH) { 278 330 // fine check: actually compute minimum distance to freeform (slower) 279 double[] distSegWt = currentFreeform.getDistanceEtc(x, y);331 double[] distSegWt = MathUtil.getDistSegWt(currentFreeform.getNodes(), x, y); 280 332 double distance = distSegWt[0]; 281 if (distance < EDIT THRESH && distance < minDistance) {333 if (distance < EDIT_THRESH && distance < minDistance) { 282 334 minDistance = distance; 283 335 closestFreeform = currentFreeform; 284 336 } 285 } // end (.. < EDIT THRESH)337 } // end (.. < EDIT_THRESH) 286 338 } // end if 287 339 } // end for … … 290 342 } 291 343 292 /** Print some helpful debugging info */ 293 private void debug(float x, float y) { 294 System.out.println ("Current drag coords: (" + x + "," + y + ")"); 295 System.out.println ("PrevNodeIndex = " + prevNodeIndex); 296 System.out.println ("NextNodeIndex = " + nextNodeIndex); 297 } 298 344 /** Computes a point along the line segment a[]b[] (2D) based on parameter weight */ 345 private float[] computePtOnSegment (float[] a, float[] b, float weight) { 346 float dx = b[0]  a[0]; 347 float dy = b[1]  a[1]; 348 float newX = (float) (a[0] + dx * weight); 349 float newY = (float) (a[1] + dy * weight); 350 float[] retvals = {newX, newY}; 351 return retvals; 352 } 353 354 /** Prints node array of current freeform; for debugging */ 355 private void printNodes() { 356 if (freeform!=null){ 357 System.out.println("Printing nodes..."); 358 float[][]nodes = freeform.getNodes(); 359 for (int i = 0; i < nodes[0].length; i++){ 360 System.out.println(i+":("+nodes[0][i]+","+nodes[1][i]+")"); 361 } 362 } 363 } 364 299 365 /** Prints a message for debugging */ 300 366 public void print(String methodName, String message) { 
trunk/loci/visbio/overlays/OverlayIO.java
r1390 r1796 71 71 if (!foundHeader) { 72 72 // parse table header from first valid line 73 int numDims = count  10;73 int numDims = count  10; // why 10? 74 74 if (numDims < 0) { 75 75 JOptionPane.showMessageDialog(owner, 
trunk/loci/visbio/overlays/OverlayObject.java
r1717 r1796 1 // 1 2 2 // OverlayObject.java 3 3 // … … 28 28 import java.util.Arrays; 29 29 import visad.*; 30 import loci.visbio.util.MathUtil;31 30 32 31 /** OverlayObject is the superclass of all overlay objects. */ … … 402 401 Arrays.fill(nodes[1], numNodes++, maxNodes, y); 403 402 // i.e., set all remaining nodes (as per maxNodes) to next node coords 404 }405 406 // This could belong to MathUtil.java ACS TODO407 /** Gets actual distance to curve: finds out if the nearest point is a node or a segment;408 *409 * @param x x coordinate of point in question410 * @param y y coordinate of point in question411 * @return an array float[3], with element 0 being node index i of one end of412 * closest line segment (the other end being i+1), and the third being the weight413 * between zero and one for interpolation along the segment (i, i+1)414 */415 public double[] getDistanceEtc(float x, float y) {416 double minDist = Double.MAX_VALUE;417 int seg = 0;418 double weight = 0;419 420 // toss out the trivial case421 if (numNodes == 1) {422 //then distance is the distance to the trivial bounding box after all; could invoke the423 //getDistance method from OverlayFreeform instead.424 double xdist = x  nodes[0][0];425 double ydist = y  nodes[1][0];426 minDist = Math.sqrt(xdist * xdist + ydist * ydist);427 } else {428 429 for (int i=0; i<numNodes1; i++) {430 double[] a = {(double) nodes[0][i], (double) nodes[1][i]};431 double[] b = {(double) nodes[0][i+1], (double) nodes[1][i+1]};432 double[] p = {(double) x, (double) y};433 434 double[] proj = MathUtil.getProjection(a, b, p, true);435 double dist = MathUtil.getDistance(p, proj);436 437 if (dist < minDist) {438 minDist = dist;439 seg = i;440 double segDist = MathUtil.getDistance (a, b);441 double fracDist = MathUtil.getDistance (a, proj);442 weight = fracDist / segDist;443 }444 }445 }446 double[] retvals = {minDist, (double) seg, weight};447 return retvals;448 403 } 449 404
Note: See TracChangeset
for help on using the changeset viewer.