View Javadoc
1   package com.github.celldynamics.quimp.plugin.ecmm;
2   
3   import com.github.celldynamics.quimp.Vert;
4   import com.github.celldynamics.quimp.geom.ExtendedVector2d;
5   import com.github.celldynamics.quimp.utils.QuimpToolsCollection;
6   
7   import ij.IJ;
8   import ij.process.ImageProcessor;
9   
10  /**
11   * ODE Solver.
12   * 
13   * @author rtyson
14   *
15   */
16  public class ODEsolver {
17  
18    private static boolean inside;
19  
20    /**
21     * Default constructor.
22     */
23    public ODEsolver() {
24    }
25  
26    /**
27     * Euler solver.
28     * 
29     * @param v vertex
30     * @param s sector
31     * @return ?
32     */
33    public static ExtendedVector2d euler(Vert v, Sector s) {
34      // Vect2d[] history = new Vect2d[ECMp.maxIter];
35      int x;
36      int y;
37      int lastSampleX = -1;
38      int lastSampleY = -1; // store where last sample was
39      double dist = 0; // distance migrated
40      double tempFlu;
41      Vert edge;
42      ExtendedVector2d p;
43      ExtendedVector2d pp;
44  
45      v.snapped = false;
46  
47      if (ECMp.ANA) { // sample at boundary
48        p = v.getPoint();
49        x = (int) Math.round(p.getX());
50        y = (int) Math.round(p.getY());
51        lastSampleX = x;
52        lastSampleY = y;
53        tempFlu = ODEsolver.sampleFluo(ECMp.image, x, y);
54        v.fluores[0].intensity = tempFlu;
55        v.fluores[0].x = x; // store in first slot
56        v.fluores[0].y = y;
57      }
58  
59      if (ECMp.plot) {
60        ECMM_Mapping.plot.setColor(0, 0, 0);
61      }
62  
63      p = new ExtendedVector2d(v.getX(), v.getY());
64      pp = new ExtendedVector2d(v.getX(), v.getY()); // previouse position
65  
66      // history[0] = new Vect2d(p.getX(), p.getY());
67  
68      boolean maxHit = false;
69      int i = 1;
70      ExtendedVector2d k;
71  
72      for (; i < ECMp.maxIter - 1; i++) {
73        // IJ.log("\tIt " + i); //debug
74        if (ODEsolver.proximity(p, s) || (ECMp.ANA && dist >= (ECMp.anaMigDist)) || maxHit) {
75          // stop when within d of the target segment or
76          // if migrated more than the ana set cortex width (in pixels)
77          pp.setX(p.getX());
78          pp.setY(p.getY());
79  
80          // if(!ECMp.ANA) { // no need to snap ana result. landing coord
81          // not needed
82          edge = ODEsolver.snap(p, s);
83          dist += ExtendedVector2d.lengthP2P(pp, p);
84          v.distance = QuimpToolsCollection.speedToScale(dist, ECMp.scale, ECMp.frameInterval);
85          // if (s.expanding && !ECMp.ANA) {
86          v.setLandingCoord(p, edge);
87          // }
88          // }
89  
90          if (ECMp.plot && ECMp.drawPaths) {
91            ECMM_Mapping.plot.setColor(0, 0, 0);
92            ECMM_Mapping.plot.drawLine(pp, p);
93          }
94  
95          v.snapped = true;
96          // System.out.println("iterations: " + i);
97          break;
98        }
99  
100       k = ODEsolver.dydt(p, s);
101       k.multiply(ECMp.h);
102 
103       pp.setX(p.getX());
104       pp.setY(p.getY());
105       p.setX(p.getX() + k.getX());
106       p.setY(p.getY() + k.getY());
107       dist += ExtendedVector2d.lengthP2P(pp, p);
108 
109       if (ECMp.plot && ECMp.drawPaths) {
110         // ECMM_Mapping.plot.setColor(1, 0, 0);
111         ECMM_Mapping.plot.drawLine(pp, p);
112       }
113       // history[i] = new Vect2d(p.getX(), p.getY());
114 
115       if (ECMp.ANA) { // sample
116         x = (int) Math.round(p.getX());
117         y = (int) Math.round(p.getY());
118         if (!(x == lastSampleX && y == lastSampleY)) { // on sample new locations
119           lastSampleX = x;
120           lastSampleY = y;
121           tempFlu = ODEsolver.sampleFluo(ECMp.image, x, y);
122 
123           if (tempFlu > v.fluores[0].intensity) { // store first one
124             // if((tempFlu / v.fluores[0].intensity)<1.1){
125             // maxHit = true;
126             // }
127             v.fluores[0].intensity = tempFlu;
128             v.fluores[0].x = x;
129             v.fluores[0].y = y;
130 
131           }
132         }
133       }
134 
135       ECMp.its++;
136     }
137 
138     if (ECMp.plot && !v.snapped && ECMp.drawFails) { // mark the start point of failed nodes
139       ECMM_Mapping.plot.setColor(1, 0, 0);
140       // p.print(v.getTrackNum() + "p: ");
141       // pp.print(v.getTrackNum() + "pp: ");
142       ECMM_Mapping.plot.drawCircle(v.getPoint(), 5);
143     }
144 
145     return p;
146   }
147 
148   /**
149    * Get first derivative.
150    * 
151    * @param p ExtendedVector2d
152    * @param s Sector
153    * @return dy/dt
154    */
155   public static ExtendedVector2d/../../../../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d dydt(ExtendedVector2d p, Sector s) {
156     ExtendedVector2d result = fieldAt(p, s);
157     result.multiply(ECMp.mobileQ);
158 
159     if (true) { // Math.abs(result.length()) > ECMp.maxVertF) {
160       // IJ.log("!WARNING-max force exceeded: " +
161       // Math.abs(result.length()));
162       result.makeUnit();
163       result.multiply(ECMp.maxVertF);
164     }
165     return result;
166   }
167 
168   /**
169    * Proximity.
170    * 
171    * @param p ExtendedVector2d
172    * @param s Sector
173    * @return ?
174    */
175   public static boolean proximity(ExtendedVector2d p, Sector s) {
176     // could test against the chrages or the actual contour.
177     // if using polar lines can use actual contour
178     // Vert v = s.getTarStart();
179     // if(true) return false;
180     // Vert v = s.tarCharges.getHead();
181     Vert v = s.getTarStart();
182     do {
183       double d = ExtendedVector2d.distPointToSegment(p, v.getPoint(), v.getNext().getPoint());
184       // IJ.log("\t\tprox to: " + d); //debug
185       if (d <= ECMp.d) {
186         return true;
187       }
188       v = v.getNext();
189     } while (!v.isIntPoint());
190     return false;
191   }
192 
193   private static Vert snap(ExtendedVector2d p, Sector s) {
194     // snap p to the closest segment of target contour
195     ExtendedVector2d current;
196     Vert closestEdge;
197     double distance;// = ECMp.d + 1; // must be closer then d+1, good starting value
198     double tempDis;
199 
200     Vert v = s.getTarStart().getPrev(); // include the edge to the starting intersect pount
201     distance = ExtendedVector2d.distPointToSegment(p, v.getPoint(), v.getNext().getPoint());
202     v = v.getNext();
203     closestEdge = v;
204     do {
205       current = ExtendedVector2d.pointToSegment(p, v.getPoint(), v.getNext().getPoint());
206       tempDis = ExtendedVector2d.lengthP2P(p, current);
207 
208       if (tempDis < distance) {
209         closestEdge = v;
210         distance = tempDis;
211       }
212       v = v.getNext();
213     } while (!v.isIntPoint());
214 
215     // p.setX(closest.getX());
216     // p.setY(closest.getY());
217 
218     return closestEdge;
219   }
220 
221   private static ExtendedVector2d/../../../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d fieldAt(ExtendedVector2d p, Sector s) {
222 
223     // Use line charges or point charges. remove if for speed
224     // return fieldAtLines(p, s);
225     if (ECMp.lineCharges) {
226       return fieldAtLines(p, s);
227     } else {
228       return fieldAtPoints(p, s);
229     }
230   }
231 
232   private static ExtendedVector2d/../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d fieldAtPoints(ExtendedVector2d p, Sector s) {
233     // calc the field size at p according to to migrating and target charges
234     ExtendedVector2deom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d field = new ExtendedVector2d(0, 0);
235     ExtendedVector2dom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d totalF = new ExtendedVector2d(0, 0);
236 
237     Vert v = s.migCharges.getHead();
238     do {
239 
240       forceP(field, p, v.getPoint(), ECMp.migQ, ECMp.migPower);
241       totalF.addVec(field);
242       // totalF.print("\ttotlaF = ");
243       v = v.getNext();
244     } while (!v.getPrev().isIntPoint() || v.getPrev().isHead());
245 
246     v = s.tarCharges.getHead();
247     do {
248       forceP(field, p, v.getPoint(), ECMp.tarQ, ECMp.tarPower);
249       totalF.addVec(field);
250       v = v.getNext();
251     } while (!v.getPrev().isIntPoint() || v.getPrev().isHead());
252 
253     return totalF;
254   }
255 
256   private static void forceP(ExtendedVector2d../../../../../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d./../../../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d force, ExtendedVector2d../../../../../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d p, ExtendedVector2d pq,
257           double q, double power) {
258     double r = ExtendedVector2d.lengthP2P(pq, p);
259     // System.out.println("\t r = " + r);
260     if (r == 0) {
261       force.setX(250);
262       force.setY(250);
263       IJ.log("!WARNING-FORCE INFINITE");
264       return;
265     }
266     r = Math.abs(Math.pow(r, power));
267     ExtendedVector2d unitV = ExtendedVector2d.unitVector(pq, p);
268     double multiplier = (ECMp.k * (q / r));
269     force.setX(unitV.getX() * multiplier);
270     force.setY(unitV.getY() * multiplier);
271   }
272 
273   private static ExtendedVector2d./../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d fieldAtLines(ExtendedVector2d p, Sector s) {
274     // calc the field size at p according to to migrating and target charges
275     ExtendedVector2deom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d field = new ExtendedVector2d(0, 0);
276     ExtendedVector2dom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d totalF = new ExtendedVector2d(0, 0);
277     double polarDir;
278 
279     // inside or outside sector?
280     inside = s.insideCharges(p);
281 
282     if (!inside) {
283       polarDir = -1;
284       // System.out.println("switched");
285     } else {
286       polarDir = 1;
287     }
288 
289     Vert v = s.migCharges.getHead();
290     do {
291 
292       // forceL(field, p, v.getPoint(), v.getNext().getPoint(),
293       // ECMp.migQ);
294 
295       /*
296        * //times by the outerDirection to make lines polar sideDis =
297        * Vect2d.distPoinToInfLine(p, v.getPoint(), v.getNext().getPoint()); if (sideDis < 0) {
298        * polarDir = s.outerDirection * -1; } else { polarDir = s.outerDirection; }
299        *
300        */
301 
302       forceLpolar(field, p, v.getPoint(), v.getNext().getPoint(), ECMp.migQ, ECMp.migPower,
303               polarDir);
304 
305       totalF.addVec(field);
306       v = v.getNext();
307     } while (!v.isIntPoint() || v.isHead());
308 
309     v = s.tarCharges.getHead();
310     do {
311       // forceL(field, p, v.getPoint(), v.getNext().getPoint(),
312       // ECMp.tarQ);
313 
314       /*
315        * sideDis = Vect2d.distPoinToInfLine(p, v.getPoint(), v.getNext().getPoint()); if
316        * (sideDis < 0) { polarDir = s.outerDirection ; } else { polarDir = s.outerDirection *
317        * -1; }
318        *
319        */
320 
321       forceLpolar(field, p, v.getPoint(), v.getNext().getPoint(), ECMp.tarQ, ECMp.tarPower,
322               polarDir);
323 
324       totalF.addVec(field);
325       v = v.getNext();
326     } while (!v.isIntPoint() || v.isHead());
327 
328     return totalF;
329   }
330 
331   private static void forceLpolar(ExtendedVector2d../../../../../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d./../../../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d force, ExtendedVector2d../../../../../com/github/celldynamics/quimp/geom/ExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d p, ExtendedVector2d s1,
332           ExtendedVector2d s2, double q, double power, double orientation) {
333     double l = ExtendedVector2d.lengthP2P(s1, s2);
334     ExtendedVector2d ru = ExtendedVector2d.unitVector(s2, p);
335     double r = ExtendedVector2d.lengthP2P(s2, p);
336     ExtendedVector2d rpU = ExtendedVector2d.unitVector(s1, p);
337     double rp = ExtendedVector2d.lengthP2P(s1, p);
338 
339     double d = (((rp + r) * (rp + r)) - (l * l)) / (2 * l);
340     // double d = ( Math.pow((rp + r), power) - (L * L)) / (2 * L);
341     double multiplier = ((ECMp.k * q) / d);
342     rpU.addVec(ru);
343 
344     force.setX(rpU.getX() * multiplier * orientation);
345     force.setY(rpU.getY() * multiplier * orientation);
346   }
347 
348   /**
349    * Get intensity value for given coordinates.
350    * 
351    * @param ip image to sample intensities
352    * @param x screen coordinate
353    * @param y screen coordinate
354    * @return mean intensity within 9-point stencil located at x,y
355    */
356   public static double sampleFluo(ImageProcessor ip, int x, int y) {
357     double tempFlu = ip.getPixelValue(x, y) + ip.getPixelValue(x - 1, y)
358             + ip.getPixelValue(x + 1, y) + ip.getPixelValue(x, y - 1) + ip.getPixelValue(x, y + 1)
359             + ip.getPixelValue(x - 1, y - 1) + ip.getPixelValue(x + 1, y + 1)
360             + ip.getPixelValue(x + 1, y - 1) + ip.getPixelValue(x - 1, y + 1);
361     tempFlu = tempFlu / 9d;
362     return tempFlu;
363   }
364 }