View Javadoc
1   package com.github.celldynamics.quimp.plugin.ecmm;
2   
3   import com.github.celldynamics.quimp.Outline;
4   import com.github.celldynamics.quimp.Vert;
5   import com.github.celldynamics.quimp.geom.ExtendedVector2d;
6   
7   import ij.IJ;
8   
9   /**
10   * Create mapping between outlines.
11   * 
12   * @author rtyson
13   *
14   */
15  class Mapping {
16  
17    Outline o1;
18    Outline o2;
19    Sector[] sectors;
20  
21    public Mapping(Outlineef="../../../../../../com/github/celldynamics/quimp/Outline.html#Outline">Outline oo1, Outline oo2) {
22      o1 = oo1;
23      o2 = oo2;
24      ECMp.numINTS = 0;
25  
26      if (ECMp.ANA || ECMp.forceNoSectors) { // for ANA force no intersection points
27        insertFake();
28        o1.updateNormals(true);
29        o2.updateNormals(true);
30        formSectors();
31        return;
32      }
33  
34      // shift them slightly
35      ECMp.numINTS = calcIntersects(); // temp intersect points are inserted
36  
37      if (ECMp.numINTS == 0) {
38        System.out.println("No intersects found");
39        insertFake();
40        o1.updateNormals(true);
41        o2.updateNormals(true);
42        formSectors();
43      } else {
44        if (ECMp.inspectSectors) {
45          if (!inspectInts()) {
46            IJ.log("    invalid outline intersections. Intersects corrected");
47            if (ECMp.plot && ECMp.drawFails) {
48              ECMM_Mapping.plot.writeText("Intersects corrected");
49            }
50            rebuildInts();
51  
52          }
53        }
54        if (ECMp.plot && ECMp.drawIntersects) {
55          drawIntersects();
56        }
57        // System.out.println("Num intersects: " + INTS);
58  
59        o1.updateNormals(true);
60        o2.updateNormals(true);
61        formSectors();
62      }
63    }
64  
65    public void printSector(int i) {
66      sectors[i].print();
67    }
68  
69    private int calcIntersects() {
70      // inserts intersect point (intPoints) into both outlines
71      int ints = 0;
72  
73      Vert na;
74      Vert nb;
75      Vert temp; // node 1 of edges A and B
76      double[] intersect = new double[2];
77      int state;
78  
79      na = o1.getHead();
80      do {
81        // a different outline so no problem with adjacent edges being flagged as crossing
82        nb = o2.getHead();
83        // edgeBcount = 1;
84        do {
85          state = ExtendedVector2d.segmentIntersection(na.getX(), na.getY(), na.getNext().getX(),
86                  na.getNext().getY(), nb.getX(), nb.getY(), nb.getNext().getX(), nb.getNext().getY(),
87                  intersect);
88  
89          if (state == 1) {
90            // result.print("intersect at : ");
91            ints++;
92            temp = o1.insertVert(na);
93            temp.setX(intersect[0]);
94            temp.setY(intersect[1]);
95            temp.setIntPoint(true, ints);
96            na = na.getNext();
97  
98            temp = o2.insertVert(nb);
99            temp.setX(intersect[0]);
100           temp.setY(intersect[1]);
101           temp.setIntPoint(true, ints);
102           nb = nb.getNext();
103         }
104         nb = nb.getNext();
105       } while (!nb.isHead());
106       na = na.getNext();
107     } while (!na.isHead());
108 
109     return ints;
110   }
111 
112   private void insertFake() {
113     // insert one fake intersect point just after the heads done when no intersections exist
114     ExtendedVector2d pos =
115             ExtendedVector2d.vecP2P(o1.getHead().getPoint(), o1.getHead().getNext().getPoint());
116     pos.multiply(0.5);
117     pos.addVec(o1.getHead().getPoint()); // half way between head and next vert
118 
119     Vert temp = o1.insertVert(o1.getHead());
120     temp.setX(pos.getX());
121     temp.setY(pos.getY());
122     temp.setIntPoint(true, 1);
123     //
124     pos = ExtendedVector2d.vecP2P(o2.getHead().getPoint(), o2.getHead().getNext().getPoint());
125     pos.multiply(0.5);
126     pos.addVec(o2.getHead().getPoint()); // half way between head and next vert
127 
128     temp = o2.insertVert(o2.getHead());
129     temp.setX(pos.getX());
130     temp.setY(pos.getY());
131     temp.setIntPoint(true, 1);
132     ECMp.numINTS++;
133   }
134 
135   private boolean inspectInts() {
136     // System.out.println("finding inverted intersects");
137     // make sure the intersect points form proper sectors
138     // by removing intersectiosn that form inverted sectors
139 
140     boolean valid = true; // made false if an inverse or loose sector is found
141 
142     Vert v1 = o1.getHead();
143     Vert v2;
144     Vert v1p;
145     Vert v2p;
146     Vert v2m;
147 
148     for (int j = 0; j < ECMp.numINTS; j++) {
149       do {
150         v1 = v1.getNext();
151       } while (!v1.isIntPoint()); // find next int point
152 
153       v2 = o2.getHead();
154       do {
155         if (v2.isIntPoint()) {
156           if (v2.intsectID == v1.intsectID) {
157             break; // find matching in o2
158           }
159         }
160         v2 = v2.getNext();
161       } while (true);
162 
163       // System.out.println(j + " :looking at");
164       // v1.print();
165       // v2.print();
166 
167       v1p = v1;
168       do {
169         v1p = v1p.getNext();
170       } while (!v1p.isIntPoint()); // find next intersect from v1
171 
172       v2p = v2;
173       do {
174         v2p = v2p.getNext();
175       } while (!v2p.isIntPoint()); // find next intersect, same direction
176 
177       v2m = v2;
178       do {
179         v2m = v2m.getPrev();
180       } while (!v2m.isIntPoint()); // find next intersect oposit direction from v2
181 
182       if (v1p.intsectID == v2p.intsectID) {
183         // System.out.println("Found valid sector");
184         if (v1.intState == 0) {
185           v1.intState = 1; // green
186         }
187         if (v2.intState == 0) {
188           v2.intState = 1;
189         }
190 
191       } else if (v1p.intsectID == v2m.intsectID) {
192         System.out.println("found inverse sector");
193         v1.intState = 3;
194         v2.intState = 3;
195 
196         // v1p.intState = 3;
197         // v2m.intState = 3;
198         valid = false;
199       } else {
200         // System.out.println("Found loose sector");
201         valid = false;
202         if (v1.intState == 0) {
203           v1.intState = 2; // blue
204         }
205         if (v2.intState == 0) {
206           v2.intState = 2;
207         }
208         if (v1.intState == 3) {
209           v1.intState = 4; // blue
210         }
211         if (v2.intState == 3) {
212           v2.intState = 4;
213         }
214       }
215     }
216 
217     return valid;
218   }
219 
220   /**
221    * Attempts to remove the correct intersects to leave only valid intersections.
222    * 
223    * <p>Done by adding back in inverted ints found by findInvertedInts()
224    */
225   private void rebuildInts() {
226 
227     System.out.println("Rebuilding intersects");
228     // find a good sector to start with (intState==1)
229     Vert v1 = o1.getHead();
230     boolean found = false;
231     do {
232       if (v1.isIntPoint() && v1.intState == 1) {
233         found = true;
234         break;
235       }
236       v1 = v1.getNext();
237     } while (!v1.isHead());
238 
239     // if no valid sectors use a loose vert to start from (and cross
240     // fingers)
241     if (!found) {
242       v1 = o1.getHead();
243       do {
244         if (v1.isIntPoint() && (v1.intState == 4 || v1.intState == 2)) {
245           found = true;
246           break;
247         }
248         v1 = v1.getNext();
249       } while (!v1.isHead());
250     }
251     if (!found) {
252       System.out.println("    ISSUE! ECMM.01 - NO valid sectors exist! (guessing correct sectors)");
253       v1 = Outline.findIntersect(v1, 4);
254     }
255 
256     // find matching intersection in o2
257     Vert v2 = Outline.findIntersect(o2.getHead(), v1.intsectID);
258     // System.out.println("done finding a start");
259 
260     // from v1, retain intersect points that allow building of good sectors, and delete the
261     // others.
262     int startingInt = v1.intsectID;
263     Vert v1p;
264     Vert v2p;
265     Vert v1pp;
266     Vert v2pp;
267     double ratio1;
268     double ratio2; // ratio of sector lengths for 2 possible solutions
269     int d1;
270     int d2;
271     int d3;
272     int d4;
273 
274     if (ECMp.plot && ECMp.drawFails) {
275       ECMM_Mapping.plot.setColor(0, 0.8, 0); // deleted colour
276     }
277 
278     do {
279       // System.out.println("Iteration");
280       v1p = v1;
281       v2p = v2;
282       v1p = Outline.getNextIntersect(v1);
283       v2p = Outline.getNextIntersect(v2);
284       if (v1p.intsectID == v2p.intsectID) {
285         v1 = v1p;
286         v2 = v2p;
287       } else {
288         v1pp = Outline.findIntersect(v1p, v2p.intsectID);
289         v2pp = Outline.findIntersect(v2p, v1p.intsectID);
290         // System.out.println("found vpp intersects");
291         ratio1 = Outline.invertsBetween(v1, v1pp);
292         ratio2 = Outline.invertsBetween(v2, v2pp);
293 
294         if (ratio1 == ratio2) {
295           // System.out.println("using distance measure");
296           // use Distance measure to choose
297           d1 = Outline.distBetweenInts(v1, v1pp);
298           d2 = Outline.distBetweenInts(v2, v2pp);
299           d3 = Outline.distBetweenInts(v1, v1p);
300           d4 = Outline.distBetweenInts(v2, v2p);
301           ratio1 = (d1 > d3) ? d1 / d3 : d3 / d1;
302           ratio2 = (d2 > d4) ? d2 / d4 : d4 / d2;
303         }
304 
305         if (ratio1 < ratio2) { // delete ints on o1 should be < ยง
306           do {
307             v1 = v1.getNext();
308             if (v1.intsectID == v2p.intsectID) {
309               break;
310             }
311             if (v1.isIntPoint()) {
312               if (v1.intsectID == startingInt) {
313                 // System.out.println("Removing starting INT!");
314               }
315               o1.removeVert(v1);
316               // also delete in o1
317               o2.removeVert(Outline.findIntersect(o2.getHead(), v1.intsectID));
318               // System.out.println("removed o2 intersects");
319               if (ECMp.plot && ECMp.drawFails) {
320                 ECMM_Mapping.plot.drawCross(v1.getPoint(), 5);
321               }
322             }
323           } while (true);
324           v2 = v2p;
325         } else { // delete ints on o2
326           do {
327             v2 = v2.getNext();
328             if (v2.intsectID == v1p.intsectID) {
329               break;
330             }
331             if (v2.isIntPoint()) {
332               if (v2.intsectID == startingInt) {
333                 // System.out.println("Removing starting INT!");
334               }
335               o2.removeVert(v2);
336               // also delete in o2
337               o1.removeVert(Outline.findIntersect(o1.getHead(), v2.intsectID));
338               // System.out.println("removed o1 intersects");
339               if (ECMp.plot && ECMp.drawFails) {
340                 ECMM_Mapping.plot.drawCross(v2.getPoint(), 5);
341               }
342             }
343           } while (true);
344           v1 = v1p;
345         }
346       }
347     } while (v1.intsectID != startingInt);
348 
349     // count remaining intersects
350     v1 = o1.getHead();
351     int intersects = 0;
352     do {
353       if (v1.isIntPoint()) {
354         intersects++;
355       }
356       v1 = v1.getNext();
357     } while (!v1.isHead());
358 
359     ECMp.numINTS = intersects;
360     System.out.println("finished rebuilding. INTS:" + ECMp.numINTS);
361   }
362 
363   private void drawIntersects() {
364     if (!ECMp.plot) {
365       return;
366     }
367 
368     ECMM_Mapping.plot.setColor(0, 0.8, 0);
369     Vert v1 = o1.getHead();
370     do {
371       if (v1.isIntPoint()) {
372         ECMM_Mapping.plot.drawCross(v1.getPoint(), 6);
373         ECMM_Mapping.plot.drawCircle(v1.getPoint(), 12);
374       }
375       v1 = v1.getNext();
376     } while (!v1.isHead());
377 
378   }
379 
380   /**
381    * Forms sectors based on the intPoints inserted by 'calcIntersects'. A sector is simply a
382    * pointer to the sectors starting intPoint checkValid();
383    */
384   private void formSectors() {
385 
386     if (ECMp.numINTS == 0) {
387       // IJ.error("NO INTERSECTS");
388       System.out.println("No Intersects"); // should never happen. fake ones insterted
389     }
390     sectors = new Sector[ECMp.numINTS];
391 
392     Vert vo1 = o1.getHead();
393     Vert vo2 = o2.getHead();
394 
395     for (int i = 0; i < ECMp.numINTS; i++) {
396       do {
397         vo1 = vo1.getNext();
398       } while (!vo1.isIntPoint());
399       do {
400         vo2 = vo2.getNext();
401         if (vo2.isIntPoint()) {
402           if (vo2.intsectID == vo1.intsectID) {
403             break; // find matching intersect
404           }
405         }
406       } while (true);
407 
408       if (ECMp.numINTS == 1) { // no intersects present, forced or otherwise
409         sectors[0] = new Sector(0);
410         sectors[0].setStarts(vo1, vo2);
411         break;
412       } else {
413         if (i == 0) {
414           sectors[i] = new Sector(i);
415           sectors[i].setStarts(vo1, vo2);
416           sectors[ECMp.numINTS - 1] = new Sector(ECMp.numINTS - 1); // set as ends for last sector
417           // sectors[INTS - 1].setEnds(vo1, vo2);
418         } else if (i == ECMp.numINTS - 1) {
419           sectors[i].setStarts(vo1, vo2);
420           // sectors[i - 1].setEnds(vo1, vo2);
421         } else {
422           sectors[i] = new Sector(i);
423           sectors[i].setStarts(vo1, vo2);
424           // sectors[i - 1].setEnds(vo1, vo2);
425         }
426       }
427     }
428 
429     if (ECMp.numINTS == 1) { // no intersects present, forced or otherwise
430       sectors[0].constructWhole(o1.calcArea(), o2.calcArea());
431     } else {
432       for (int i = 0; i < ECMp.numINTS; i++) {
433         sectors[i].construct(); // calc lengths, determin exp or contr, make charges
434         // sectors[i].showPlot();
435       }
436     }
437   }
438 
439   /**
440    * Migrate outlines.
441    * 
442    * @return mapped outline. Compute also intensities in {@link Vert#fluores}
443    */
444   public Outline migrate() {
445     Vert newVert; // placed at the marker
446     ExtendedVector2d newPos;
447     Sector s;
448 
449     Vertcs/quimp/Vert.html#Vert">Vert mapHead = new Vert(-1);
450     OutlineOutline.html#Outline">Outline mappedOutline = new Outline(mapHead);
451     Vert currentMapVert = mapHead;
452     for (int i = 0; i < sectors.length; i++) {
453       s = sectors[i];
454       Vert v = s.getMigStart().getNext(); // starting vert, don't migrate the intpoint
455 
456       do {
457         // if (ECMp.chargeDensity != -1) { //nar. polar charges sort
458         // this out
459         // tempVert = s.addTempCharge(v);
460         // }
461         // IJ.log("migrating x:" + v.getX() + ", y:" + v.getY());
462         // //debug
463         newPos = ODEsolver.euler(v, s);
464         if (!v.snapped) {
465           ECMp.unSnapped++;
466           IJ.log("    node failed to map (" + ECMp.unSnapped + ") - removed");
467           if (!ECMp.ANA && ECMp.plot && ECMp.drawFails) {
468             ECMM_Mapping.plot.writeText("FN(" + ECMp.unSnapped + ")");
469           }
470           v = v.getNext();
471 
472           // System.out.println("sector expand: " +s.expanding+",
473           // trueExpand: "+s.trueExpand + ", outDirection: " +
474           // s.outerDirection);
475           // s.tarCharges.print();
476           // s.migCharges.print();
477           continue;
478         }
479 
480         newVert = mappedOutline.insertVert(currentMapVert);
481         newVert.tarLandingCoord = v.fLandCoord; // so we always have a reference to where we landed
482         if (s.expansion) { // expanding or retracting based on area change (not length of sector)
483           newVert.distance = -v.distance; // ?????????????? why neg
484         } else {
485           newVert.distance = v.distance;
486         }
487 
488         if (!s.forwardMap) {
489           newVert.setX(v.getX());
490           newVert.setY(v.getY());
491           newVert.gCoord = v.gLandCoord;// + 1;
492           newVert.fCoord = v.fLandCoord;// + 1;
493         } else {
494           newVert.setX(newPos.getX());
495           newVert.setY(newPos.getY());
496           newVert.gCoord = v.gCoord;
497           newVert.fCoord = v.coord;
498         }
499 
500         if (ECMp.ANA) {
501           // newVert.fluores = v.cloneFluo();
502           newVert.setFluores(v.fluores);
503           newVert.setTrackNum(v.getTrackNum());
504         }
505         currentMapVert = newVert;
506         v = v.getNext();
507       } while (!v.isIntPoint());
508 
509     }
510     mappedOutline.removeVert(mapHead);
511 
512     return mappedOutline;
513   }
514 
515   public Sector getSector(int i) {
516     if (i < 0 || i > ECMp.numINTS) {
517       IJ.error("sectors out of bounds - 250");
518     }
519     return sectors[i];
520   }
521 
522 }