3 TrakEM2 plugin for ImageJ(C).
4 Copyright (C) 2007-2009 Albert Cardona.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation (http://www.gnu.org/licenses/gpl.txt )
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 You may contact Albert Cardona at acardona at ini.phys.ethz.ch
20 Institute of Neuroinformatics, University of Zurich / ETH, Switzerland.
23 package ini
.trakem2
.vector
;
25 import java
.util
.ArrayList
;
26 import java
.util
.Collections
;
27 import java
.util
.Random
;
29 import org
.scijava
.java3d
.Transform3D
;
30 import org
.scijava
.vecmath
.Point3d
;
31 import org
.scijava
.vecmath
.Vector3d
;
34 import ij
.measure
.Calibration
;
37 public class VectorString3D
implements VectorString
{
40 private double[] x
, y
, z
;
41 /** Vectors, after resampling. */
42 private double[] vx
, vy
, vz
;
43 /** Relative vectors, after calling 'relative()'. */
44 private double[] rvx
, rvy
, rvz
;
45 /** Length of points and vectors - since arrays may be a little longer. */
46 private int length
= 0;
47 /** The point interdistance after resampling. */
48 private double delta
= 0;
50 /** Bits: 000CRXYZ where C=closed, R=reversed, X=mirrored in X, Y=mirrored in Y, Z=mirrored in Z. */
51 private byte tags
= 0;
53 static public final byte CLOSED
= (1<<4);
54 static public final byte REVERSED
= (1<<3);
55 static public final byte MIRROR_X
= (1<<2);
56 static public final byte MIRROR_Y
= (1<<1);
57 static public final byte MIRROR_Z
= 1;
59 private Calibration cal
= null;
61 /** Dependent arrays that will get resampled along. */ // TODO for calibration pruposes, they should be associated with a specific x, y, or z
62 private double[][] dep
;
64 // DANGER: the orientation of the curve can't be checked like in 2D. There is no up and down in the 3D space.
65 /** A list of lists of Point3d, each one containing the points from which the points in this VectorString3D come from. */
66 private ArrayList
<ArrayList
<Point3d
>> source
= null;
67 /** The number of VectorString3D that have contributed to this one, via createInterpolated and others. */
68 private int n_sources
= 1;
70 public VectorString3D(final double[] x
, final double[] y
, final double[] z
, final boolean closed
) throws Exception
{
71 if (!(x
.length
== y
.length
&& x
.length
== z
.length
)) throw new Exception("x,y,z must have the same length.");
72 this.length
= x
.length
;
76 if (closed
) this.tags ^
= CLOSED
;
79 /** Add an array that will get resampled along; must be of the same length as the value returned by length() */
80 public void addDependent(final double[] a
) throws Exception
{
81 if (a
.length
!= this.length
) throw new Exception("Dependent array must be of the same size as thevalue returned by length()");
83 dep
= new double[1][];
87 final double[][] dep2
= new double[dep
.length
+ 1][];
88 for (int i
=0; i
<dep
.length
; i
++) dep2
[i
] = dep
[i
];
93 public double[] getDependent(final int i
) {
97 /** Return the average point interdistance. */
98 public double getAverageDelta() {
100 for (int i
=length
-1; i
>0; i
--) {
101 d
+= Math
.sqrt( Math
.pow(x
[i
] - x
[i
-1], 2) + Math
.pow(y
[i
] - y
[i
-1], 2) + Math
.pow(z
[i
] - z
[i
-1], 2));
106 /** If not resampled, the returned delta is zero. */
108 public double getDelta() { return delta
; }
112 public void resample(final double delta
) {
113 resample(delta
, false);
116 /** Homogenize the average point interdistance to 'delta'.
117 * If delta is the same as the desired, it WILL RETURN NULL even if with_source is true.
118 * If with_source, then returns the list of lists of points that contributed to each resampled point.
121 public void resample(final double delta
, final boolean with_source
) {
122 if (Math
.abs(delta
- this.delta
) < 0.0000001) {
126 this.delta
= delta
; // store for checking purposes
127 this.resample(with_source
);
130 /** The length of this string, that is, the number of points (and vectors) in it. */
132 public final int length() { return length
; }
134 public double[] getPoints(final int dim
) {
143 public double[] getVectors(final int dim
) {
152 public double getPoint(final int dim
, final int i
) {
161 public double getVector(final int dim
, final int i
) {
163 case 0: return vx
[i
];
164 case 1: return vy
[i
];
165 case 2: return vy
[i
];
169 public double getRelativeVector(final int dim
, final int i
) {
171 case 0: return rvx
[i
];
172 case 1: return rvy
[i
];
173 case 2: return rvy
[i
];
179 public final boolean isClosed() {
180 return 0 != (tags
& CLOSED
);
183 public void debug() {
184 System
.out
.println("#### " + getClass().getName() + " ####");
185 for (int i
=0; i
<x
.length
; i
++) {
186 System
.out
.println( i
+ ": " + x
[i
] + "," + y
[i
] + ", " + z
[i
]);
188 System
.out
.println("#### END ####");
192 private class ResamplingData
{
194 private double[] rx
, ry
, rz
,
196 private double[][] dep
;
198 /** Initialize with a starting length. */
199 ResamplingData(final int length
, final double[][] dep
) {
201 rx
= new double[length
];
202 ry
= new double[length
];
203 rz
= new double[length
];
205 vx
= new double[length
];
206 vy
= new double[length
];
207 vz
= new double[length
];
209 if (null != dep
) this.dep
= new double[dep
.length
][length
];
211 /** Arrays are enlarged if necessary.*/
212 final void setP(final int i
, final double xval
, final double yval
, final double zval
) {
213 if (i
>= rx
.length
) resize(i
+10);
218 /** Arrays are enlarged if necessary.*/
220 final void setV(final int i, final double xval, final double yval, final double zval) {
221 if (i >= rx.length) resize(i+10);
227 /** Arrays are enlarged if necessary.*/
228 final void setPV(final int i
, final double rxval
, final double ryval
, final double rzval
, final double xval
, final double yval
, final double zval
) {
229 if (i
>= rx
.length
) resize(i
+10);
237 final void resize(final int new_length
) {
238 this.rx
= Util
.copy(this.rx
, new_length
);
239 this.ry
= Util
.copy(this.ry
, new_length
);
240 this.rz
= Util
.copy(this.rz
, new_length
);
241 this.vx
= Util
.copy(this.vx
, new_length
);
242 this.vy
= Util
.copy(this.vy
, new_length
);
243 this.vz
= Util
.copy(this.vz
, new_length
);
245 // java doesn't have generators! ARGH
246 final double[][] dep2
= new double[dep
.length
][];
247 for (int i
=0; i
<dep
.length
; i
++) dep2
[i
] = Util
.copy(dep
[i
], new_length
);
251 final double x(final int i
) { return rx
[i
]; }
252 final double y(final int i
) { return ry
[i
]; }
253 final double z(final int i
) { return rz
[i
]; }
254 /** Distance from point rx[i],ry[i], rz[i] to point x[j],y[j],z[j] */
255 final double distance(final int i
, final double x
, final double y
, final double z
) {
256 return Math
.sqrt(Math
.pow(x
- rx
[i
], 2)
257 + Math
.pow(y
- ry
[i
], 2)
258 + Math
.pow(z
- rz
[i
], 2));
260 final void put(final VectorString3D vs
, final int length
) {
261 vs
.x
= Util
.copy(this.rx
, length
); // crop away empty slots
262 vs
.y
= Util
.copy(this.ry
, length
);
263 vs
.z
= Util
.copy(this.rz
, length
);
264 vs
.vx
= Util
.copy(this.vx
, length
);
265 vs
.vy
= Util
.copy(this.vy
, length
);
266 vs
.vz
= Util
.copy(this.vz
, length
);
269 vs
.dep
= new double[dep
.length
][];
270 for (int i
=0; i
<dep
.length
; i
++) vs
.dep
[i
] = Util
.copy(dep
[i
], length
);
273 final void setDeps(final int i
, final double[][] src_dep
, final int[] ahead
, final double[] weight
, final int len
) {
274 if (null == dep
) return;
275 if (i
>= rx
.length
) resize(i
+10);
277 for (int k
=0; k
<dep
.length
; k
++) {
278 for (int j
=0; j
<len
; j
++) {
279 dep
[k
][i
] += src_dep
[k
][ahead
[j
]] * weight
[j
];
281 } // above, the ahead and weight arrays (which have the same length) could be of larger length than the given 'len', thus len is used.
285 static class Vector
{
286 private double x
, y
, z
;
287 private double length
;
288 // 0 coords and 0 length, virtue of the 'calloc'
290 Vector(final double x
, final double y
, final double z
) {
293 Vector(final Vector v
) {
297 this.length
= v
.length
;
300 final public Object
clone() {
301 return new Vector(this);
303 final void set(final double x
, final double y
, final double z
) {
307 this.length
= computeLength();
309 final void normalize() {
310 if (0 == length
) return;
311 // check if length is already 1
312 if (Math
.abs(1 - length
) < 0.00000001) return; // already normalized
316 this.length
= computeLength(); // should be 1
318 final double computeLength() {
319 return Math
.sqrt(x
*x
+ y
*y
+ z
*z
);
321 final double length() {
324 final void scale(final double factor
) {
328 this.length
= computeLength();
330 final void add(final Vector v
, final boolean compute_length
) {
334 if (compute_length
) this.length
= computeLength();
336 final void setLength(final double len
) {
340 final void put(final int i
, final ResamplingData r
) {
341 r
.setPV(i
, r
.x(i
-1) + this.x
, r
.y(i
-1) + this.y
, r
.z(i
-1) + this.z
, this.x
, this.y
, this.z
);
344 final void put(final double[] d
) {
350 final void put(final double[][] d
, final int col
) {
355 final void put(final int i
, final double[] x
, final double[] y
, final double[] z
) {
360 final Vector
getCrossProduct(final Vector v
) {
361 // (a1; a2; a3) x (b1; b2; b3) = (a2b3 - a3b2; a3b1 - a1b3; a1b2 - a2b1)
362 return new Vector(y
* v
.z
- z
* v
.y
,
366 final void setCrossProduct(final Vector v
, final Vector w
) {
367 this.x
= v
.y
* w
.z
- v
.z
* w
.y
;
368 this.y
= v
.z
* w
.x
- v
.x
* w
.z
;
369 this.z
= v
.x
* w
.y
- v
.y
* w
.x
;
371 /** Change coordinate system. */
372 final void changeRef(final Vector v_delta
, final Vector v_i1
, final Vector v_new1
) { // this vector works like new2
373 // ortogonal system 1: the target
375 final Vector a2
= new Vector( v_new1
); // vL
377 final Vector a1
= a2
.getCrossProduct(v_i1
); // vQ
379 final Vector a3
= a2
.getCrossProduct(a1
);
380 // no need //a3.normalize();
382 final double[][] m1
= new double[3][3];
386 final Matrix mat1
= new Matrix(m1
);
388 // ortogonal system 2: the current
390 final Vector b2
= new Vector( v_delta
); // vA
392 final Vector b3
= a1
.getCrossProduct(b2
); // vQ2
394 final double[][] m2
= new double[3][3];
398 final Matrix mat2
= new Matrix(m2
).transpose();
400 final Matrix R
= mat1
.times(mat2
);
401 final Matrix mthis
= new Matrix(new double[]{this.x
, this.y
, this.z
}, 1);
402 // The rotated vector as a one-dim matrix
403 // (i.e. the rescued difference vector as a one-dimensional matrix)
404 final Matrix v_rot
= R
.transpose().times(mthis
.transpose()); // 3x3 times 3x1, hence the transposing of the 1x3
405 final double[][] arr
= v_rot
.getArray();
411 final Point3d
asPoint3d() {
412 return new Point3d(x
, y
, z
);
416 private final void recalculate(final double[] w
, final int length
, final double sum_
) {
419 for (q
=0; q
<length
; q
++) {
423 double error
= 1.0 - sum
;
424 // make it be an absolute value
430 } else if (sum
> 1.0) {
431 recalculate(w
, length
, sum
);
435 /** If argument with_source is true, then returns an ArrayList of ArrayList of Point3d, with lists of all points that contributed to each point. */
436 private void resample(final boolean with_source
) {
438 final double MAX_DISTANCE
= 2.5 * delta
;
439 final int MA
= (int)(MAX_DISTANCE
/ getAverageDelta());
440 final int MAX_AHEAD
= MA
< 6 ?
6 : MA
;
442 // convenient data carrier and editor
443 final ResamplingData r
= new ResamplingData(this.length
, this.dep
);
444 final Vector vector
= new Vector();
446 // The source points that generate each point:
447 if (with_source
&& null == this.source
) {
448 // add all original points to a new list of lists (so that many VectorString3D may be combined, while keeping all source points for each final point)
449 this.source
= new ArrayList
<ArrayList
<Point3d
>>();
450 for (int g
=0; g
<length
; g
++) {
451 final ArrayList
<Point3d
> ap
= new ArrayList
<Point3d
>();
452 ap
.add(new Point3d(x
[g
], y
[g
], z
[g
]));
457 final ArrayList
<ArrayList
<Point3d
>> new_source
= with_source ?
458 new ArrayList
<ArrayList
<Point3d
>>() : null;
461 // first resampled point is the same as point zero
462 r
.setP(0, x
[0], y
[0], z
[0]);
463 r
.setDeps(0, dep
, new int[]{0}, new double[]{1.0}, 1);
464 // the first vector is 0,0,0 unless the path is closed, in which case it contains the vector from last-to-first.
466 if (with_source
) new_source
.add(new ArrayList
<Point3d
>(this.source
.get(0)));
470 // index over rx,ry,rz (resampled points)
473 int t
, s
, ii
, u
, iu
, k
;
475 double dist_ahead
, dist1
, dist2
, sum
;
476 final double[] w
= new double[MAX_AHEAD
];
477 final double[] distances
= new double[MAX_AHEAD
];
478 final Vector
[] ve
= new Vector
[MAX_AHEAD
];
480 for (next_ahead
= 0; next_ahead
< MAX_AHEAD
; next_ahead
++) ve
[next_ahead
] = new Vector();
481 final int[] ahead
= new int[MAX_AHEAD
];
485 // start infinite loop
486 for (;prev_i
<= i
;) {
487 if (prev_i
> i
|| (!isClosed() && i
== this.length
-1)) break;
488 // get distances of MAX_POINTs ahead from the previous point
490 for (t
=0; t
<MAX_AHEAD
; t
++) {
492 // fix 's' if it goes over the end
493 if (s
>= this.length
) {
494 if (isClosed()) s
-= this.length
;
497 dist_ahead
= r
.distance(j
-1, x
[s
], y
[s
], z
[s
]);
498 if (dist_ahead
< MAX_DISTANCE
) {
499 ahead
[next_ahead
] = s
;
500 distances
[next_ahead
] = dist_ahead
;
504 if (0 == next_ahead
) {
505 // No points (ahead of the i point) are found within MAX_DISTANCE
506 // Just use the next point as target towards which create a vector of length delta
507 vector
.set(x
[i
] - r
.x(j
-1), y
[i
] - r
.y(j
-1), z
[i
] - r
.z(j
-1));
508 dist1
= vector
.length();
509 vector
.setLength(delta
);
513 // shallow clone: shared Point3d instances (but no shared lists)
514 new_source
.add(new ArrayList
<Point3d
>(this.source
.get(i
))); // the next point is 'i'
517 if (null != dep
) r
.setDeps(j
, dep
, new int[]{i
}, new double[]{1.0}, 1);
519 //System.out.println("j: " + j + " (ZERO) " + vector.computeLength() + " " + vector.length());
521 //correct for point overtaking the not-close-enough point ahead in terms of 'delta_p' as it is represented in MAX_DISTANCE, but overtaken by the 'delta' used for subsampling:
522 if (dist1
<= delta
) {
523 //look for a point ahead that is over distance delta from the previous j, so that it will lay ahead of the current j
524 for (u
=i
; u
<this.length
; u
++) {
525 dist2
= Math
.sqrt(Math
.pow(x
[u
] - r
.x(j
-1), 2)
526 + Math
.pow(y
[u
] - r
.y(j
-1), 2)
527 + Math
.pow(z
[u
] - r
.z(j
-1), 2));
536 // Compose a point ahead out of the found ones.
538 // First, adjust weights for the points ahead
539 w
[0] = distances
[0] / MAX_DISTANCE
;
540 double largest
= w
[0];
541 for (u
=1; u
<next_ahead
; u
++) {
542 w
[u
] = 1 - (distances
[u
] / MAX_DISTANCE
);
543 if (w
[u
] > largest
) {
547 // normalize weights: divide by largest
549 for (u
=0; u
<next_ahead
; u
++) {
550 w
[u
] = w
[u
] / largest
;
553 // correct error. The closest point gets the extra
557 recalculate(w
, next_ahead
, sum
);
559 // Now, make a vector for each point with the corresponding weight
562 final ArrayList
<Point3d
> ap
= with_source ?
new ArrayList
<Point3d
>() : null;
564 for (u
=0; u
<next_ahead
; u
++) {
566 if (iu
>= this.length
) iu
-= this.length
;
567 ve
[u
].set(x
[iu
] - r
.x(j
-1), y
[iu
] - r
.y(j
-1), z
[iu
] - r
.z(j
-1));
568 ve
[u
].setLength(w
[u
] * delta
);
569 vector
.add(ve
[u
], u
== next_ahead
-1); // compute the length only on the last iteration
571 ap
.addAll(this.source
.get(iu
));
575 if (with_source
) new_source
.add(ap
);
577 // correct potential errors
578 if (Math
.abs(vector
.length() - delta
) > 0.00000001) {
579 vector
.setLength(delta
);
583 if (null != dep
) r
.setDeps(j
, dep
, ahead
, w
, next_ahead
);
585 //System.out.println("j: " + j + " (" + next_ahead + ") " + vector.computeLength() + " " + vector.length());
588 // find the first point that is right ahead of the newly added point
589 // so: loop through points that lay within MAX_DISTANCE, and find the first one that is right past delta.
591 for (k
=0; k
<next_ahead
; k
++) {
592 if (distances
[k
] > delta
) {
597 // correct for the case of unseen point (because all MAX_POINTS ahead lay under delta):
600 i
= ahead
[next_ahead
-1] +1; //the one after the last.
601 if (i
>= this.length
) {
602 if (isClosed()) i
= i
- this.length
; // this.length is the length of the x,y,z, the original points
603 else i
= this.length
-1;
609 //advance index in the new points
613 } catch (final Exception e
) {
615 System
.out
.println("Some data: x,y,z .length = " + x
.length
+ "," + y
.length
+ "," + z
.length
616 + "\nj=" + j
+ ", i=" + i
+ ", prev_i=" + prev_i
621 dist_ahead
= r
.distance(j
-1, x
[this.length
-1], y
[this.length
-1], z
[this.length
-1]);
623 //System.out.println("delta: " + delta + "\nlast point: " + x[x.length-1] + ", " + y[y.length-1] + ", " + z[z.length-1]);
624 //System.out.println("last resampled point: x,y,z " + r.x(j-1) + ", " + r.y(j-1) + ", " + r.z(j-1));
625 //System.out.println("distance: " + dist_ahead);
627 // see whether the subsampling terminated too early, and fill with a line of points.
628 final int last_i
= isClosed() ?
0 : this.length
-1;
629 if (dist_ahead
> delta
*1.2) {
630 //TODO//System.out.println("resampling terminated too early. Why?");
631 while (dist_ahead
> delta
*1.2) {
632 // make a vector from the last resampled point to the last point
633 vector
.set(x
[last_i
] - r
.x(j
-1), y
[last_i
] - r
.y(j
-1), z
[last_i
] - r
.z(j
-1));
634 // resize it to length delta
635 vector
.setLength(delta
);
637 if (with_source
) new_source
.add(new ArrayList
<Point3d
>(this.source
.get(last_i
)));
639 dist_ahead
= r
.distance(j
-1, x
[last_i
], y
[last_i
], z
[last_i
]);
643 r
.put(this, j
); // j acts as length of resampled points and vectors
644 // Above, 'r' sets the length to j as well.
646 // vector at zero is left as 0,0 which makes no sense. Should be the last point that has no vector, or has it only in the event that the list of points is declared as closed: a vector to the first point. Doesn't really matter though, as long as it's clear: as of right now, the first point has no vector unless the path is closed, in which case it contains the vector from the last-to-first.
648 if (with_source
) this.source
= new_source
;
650 //System.out.println("resampling with_source: " + with_source + " n_sources = " + n_sources);
653 //if (with_source && j != new_source.size()) System.out.println("WARNING: len: " + j + ", sources length: " + new_source.size());
654 //if (with_source && n_sources > 1) System.out.println("n_sources=" + n_sources + " lengths: " + j + ", " + new_source.size());
657 /** Returns a list of lists of Point3d, one list of lists for each point in this VectorString3D; may be null. */
658 public ArrayList
<ArrayList
<Point3d
>> getSource() { return this.source
; }
659 /** Returns the number of VectorString3D that have contributed to this VectorString3D, via merging with createInterpolated(...). */
660 public int getNSources() { return n_sources
; }
662 /** Reorder the arrays so that the index zero becomes new_zero -the arrays are circular. */
664 public void reorder(final int new_zero
) {
667 double[] tmp
= new double[this.length
];
671 for (i
=0, j
=new_zero
; j
<length
; i
++, j
++) { tmp
[i
] = src
[j
]; }
672 for (j
=0; j
<new_zero
; i
++, j
++) { tmp
[i
] = src
[j
]; }
677 for (i
=0, j
=new_zero
; j
<length
; i
++, j
++) { tmp
[i
] = src
[j
]; }
678 for (j
=0; j
<new_zero
; i
++, j
++) { tmp
[i
] = src
[j
]; }
683 for (i
=0, j
=new_zero
; j
<length
; i
++, j
++) { tmp
[i
] = src
[j
]; }
684 for (j
=0; j
<new_zero
; i
++, j
++) { tmp
[i
] = src
[j
]; }
689 for (i
=0, j
=new_zero
; j
<length
; i
++, j
++) { tmp
[i
] = src
[j
]; }
690 for (j
=0; j
<new_zero
; i
++, j
++) { tmp
[i
] = src
[j
]; }
695 for (i
=0, j
=new_zero
; j
<length
; i
++, j
++) { tmp
[i
] = src
[j
]; }
696 for (j
=0; j
<new_zero
; i
++, j
++) { tmp
[i
] = src
[j
]; }
701 for (i
=0, j
=new_zero
; j
<length
; i
++, j
++) { tmp
[i
] = src
[j
]; }
702 for (j
=0; j
<new_zero
; i
++, j
++) { tmp
[i
] = src
[j
]; }
707 /** Subtracts vs2 vector j to this vector i and returns its length, without changing any data. */
709 public double getDiffVectorLength(final int i
, final int j
, final VectorString vsb
) {
710 final VectorString3D vs
= (VectorString3D
)vsb
;
711 if (null == rvx
|| null == rvy
|| null == rvz
) {
712 // use absolute vectors
713 final double dx
= vx
[i
] - vs
.vx
[j
];
714 final double dy
= vy
[i
] - vs
.vy
[j
];
715 final double dz
= vz
[i
] - vs
.vz
[j
];
716 return Math
.sqrt(dx
*dx
+ dy
*dy
+ dz
*dz
);
718 // use relative vectors
719 final double dx
= rvx
[i
] - vs
.rvx
[j
];
720 final double dy
= rvy
[i
] - vs
.rvy
[j
];
721 final double dz
= rvz
[i
] - vs
.rvz
[j
];
722 return Math
.sqrt(dx
*dx
+ dy
*dy
+ dz
*dz
);
727 public Object
clone() {
729 final VectorString3D vs
= new VectorString3D(Util
.copy(x
, length
), Util
.copy(y
, length
), Util
.copy(z
, length
), isClosed());
731 if (null != vx
) vs
.vx
= Util
.copy(vx
, length
);
732 if (null != vy
) vs
.vy
= Util
.copy(vy
, length
);
733 if (null != vz
) vs
.vz
= Util
.copy(vz
, length
);
734 if (null != rvx
) vs
.rvx
= Util
.copy(rvx
, length
);
735 if (null != rvy
) vs
.rvy
= Util
.copy(rvy
, length
);
736 if (null != rvz
) vs
.rvz
= Util
.copy(rvz
, length
);
737 if (null != source
) {
738 // shallow clone the source points
739 vs
.source
= new ArrayList
<ArrayList
<Point3d
>>();
740 for (final ArrayList
<Point3d
> ap
: this.source
) {
741 vs
.source
.add(new ArrayList
<Point3d
>(ap
));
745 vs
.cal
= null == this.cal ?
null : this.cal
.copy();
747 } catch (final Exception e
) {
753 private ArrayList
<ArrayList
<Point3d
>> cloneSource(final int first
, final int last
) {
754 if (null == source
) return null;
755 final ArrayList
<ArrayList
<Point3d
>> s
= new ArrayList
<ArrayList
<Point3d
>>();
757 for (final ArrayList
<Point3d
> ap
: source
) { // looping with get(i) would be paint-bucket problem
758 if (i
< first
) continue;
760 s
.add(new ArrayList
<Point3d
>(ap
));
766 public VectorString3D
makeReversedCopy() {
768 final VectorString3D vs
= new VectorString3D(Util
.copy(x
, length
), Util
.copy(y
, length
), Util
.copy(z
, length
), isClosed());
769 vs
.source
= cloneSource(0, length
-1);
771 if (delta
!= 0 && null != vx
) {
772 vs
.delta
= this.delta
;
779 } catch (final Exception e
) {
785 /** Create the relative vectors, that is, the differences of one vector to the next. In this way, vectors represent changes in the path and are independent of the actual path orientation in space. */
786 public void relative() {
787 // create vectors if not there yet
788 if (null == vx
|| null == vy
|| null == vz
) {
789 resample(getAverageDelta());
791 rvx
= new double[length
];
792 rvy
= new double[length
];
793 rvz
= new double[length
];
796 // TODO 2: also, it should test both orientations and select the best
798 // TODO: there is a mistake. Both vectors, i and i-1, have to be rotated so that vector i becomes aligned with whatever axis, as long as all vectors get aligned to the same. In this way then one truly stores differences only. An easy approach is to store the angle difference, since then it will be a scalar value independent of the orientation of the vectors. As long as the angle is represented with a vector, fine.
799 // Another alternative is, if both vectors are rotated, then the first is always on the axis and the second has the "difference" information already as is.
800 // So for a vector c1,c2,c3 of length L, I want it as L,0,0 (for example) and then the second vector, as rotated, is the one to use as relative.
801 // So: v[i-1], vL, and v[i]; then v[i-1] + W = vL, so W = vL - v[i-1], and then the diff vector is v[i] + W
803 //double wx, wy, wz; // the W
804 //double vLx; // vLy = vLz = 0
807 if (isClosed()) { // last to first
808 rvx
[0] = vx
[0] + delta
- vx
[length
-1];
809 rvy
[0] = vy
[0] - vy
[length
-1];
810 rvz
[0] = vz
[0] - vz
[length
-1];
811 } // else, as open curve the first vector remains 0,0,0
815 final Vector vL
= new Vector();
816 final Vector vA
= new Vector();
817 final Vector vQ
= new Vector();
818 final Vector vQQ
= new Vector();
819 final Vector vQ2
= new Vector();
820 final double[][] m1
= new double[3][3];
821 final double[][] m2
= new double[3][3];
823 for (int i
=1; i
<length
; i
++) {
824 // So: Johannes Schindelin said: vector Y is the rotated, X is the original
825 // Y = A + R * (X - A) where R is a rotation matrix (A is the displacement vector, 0,0,0 in this case)
826 // so the axis of rotation is the cross product of {L,0,0} and i-1
827 // (a1; a2; a3) x (b1; b2; b3) = (a2b3 - a3b2; a3b1 - a1b3; a1b2 - a2b1)
828 //Vector axis = new Vector(0 - 0, 0 - delta * vz[i-1], delta * vy[i-1] - 0);
830 vL
.normalize(); // this line can be reduced to new Vector(1,0,0);
831 vA
.set(vx
[i
-1], vy
[i
-1], vz
[i
-1]);
833 vQ
.setCrossProduct(vA
, vL
);
834 //vQ.normalize(); // no need: the cross product of two normalized vectors is also a normalized vector
835 vQQ
.setCrossProduct(vQ
, vL
);
836 //vQQ.normalize(); // no need
838 // the matrices expect the vectors as columns, not as rows! ARGH! Solved.
840 // now, vQ,vL,vQQ define an orthogonal system
841 // (vQ'; vL'; vQ' x vL')
845 final Matrix mat1
= new Matrix(m1
);
847 // (vQ'; vA'; vQ' x vA')^T // Johannes wrote vB to mean vA. For me vB is the second vector
850 vQ2
.setCrossProduct(vQ
, vA
);
851 //vQ2.normalize(); // no need
853 final Matrix mat2
= new Matrix(m2
).transpose();
855 final Matrix R
= mat1
.times(mat2
);
856 // the difference vector as a one-dimensional matrix
857 final Matrix vB
= new Matrix(new double[]{vx
[i
] - vx
[i
-1], vy
[i
] - vy
[i
-1], vz
[i
] - vz
[i
-1]}, 1);
858 final Matrix vB_rot
= R
.transpose().times(vB
.transpose()); // 3x3 times 3x1, hence the transposing of the 1x3 vector so that the inner lengths are the same
859 final double[][] arr
= vB_rot
.getArray();
863 //System.out.println("rv{x,y,z}[" + i + "]= " + rvx[i] + ", " + rvy[i] + ", " + rvz[i]);
867 /** Expand or shrink the points in this 3D path so that the average point interdistance becomes delta.
869 * This method is intended to enhance the comparison of two paths in 3D space which may be similar but differ greatly in dimensions; for example, secondary lineage tracts in 3rd instar Drosophila larvae versus adult.
871 public void equalize(final double target_delta
) {
872 if (length
< 2) return;
873 final double scale
= target_delta
/ getAverageDelta();
874 for (int i
=0; i
<length
; i
++) {
881 static public VectorString3D
createRandom(final int length
, final double delta
, final boolean closed
) throws Exception
{
882 System
.out
.println("Creating random with length " + length
+ " and delta " + delta
);
883 final double[] x
= new double[length
];
884 final double[] y
= new double[length
];
885 final double[] z
= new double[length
];
886 final Random rand
= new Random(69997); // fixed seed, so that when length is equal the generated sequence is also equal
887 final Vector v
= new Vector();
888 for (int i
=0; i
<length
; i
++) {
889 v
.set(rand
.nextDouble()*2 -1, rand
.nextDouble()*2 -1, rand
.nextDouble()*2 -1); // random values in the range [-1,1]
893 final VectorString3D vs
= new VectorString3D(x
, y
, z
, closed
);
895 vs
.vx
= new double[length
];
896 vs
.vy
= new double[length
];
897 vs
.vz
= new double[length
];
898 for (int i
=1; i
<length
; i
++) {
899 vs
.vx
[i
] = vs
.x
[i
] - vs
.x
[i
-1];
900 vs
.vy
[i
] = vs
.y
[i
] - vs
.y
[i
-1];
901 vs
.vz
[i
] = vs
.z
[i
] - vs
.z
[i
-1];
904 vs
.vx
[0] = vs
.x
[0] - vs
.x
[length
-1];
905 vs
.vy
[0] = vs
.y
[0] - vs
.y
[length
-1];
906 vs
.vz
[0] = vs
.z
[0] - vs
.z
[length
-1];
911 /** Scale to match cal.pixelWidth, cal.pixelHeight and computed depth. If cal is null, returns immediately. Will make all vectors null, so you must call resample(delta) again after calibrating. So it brings all values to cal.units, such as microns.
912 * Beware: if calibrated, then the Calibration has been applied to the points, but the callibration's pixelWidth and pixelHeight remain the same, not set to 1 and 1 respectively. So they can be used to uncalibrate, or to read out the units.
914 public void calibrate(final Calibration cal
) {
915 if (null != this.cal
) {
916 System
.out
.println("WARNING calibrating VectorString3D more than one time!");
918 if (null == cal
) return;
920 final int sign
= cal
.pixelDepth
< 0 ?
-1 : 1;
921 for (int i
=0; i
<x
.length
; i
++) {
922 x
[i
] *= cal
.pixelWidth
;
923 y
[i
] *= cal
.pixelHeight
; // should be the same as pixelWidth
924 z
[i
] *= cal
.pixelWidth
* sign
; // not pixelDepth, see day notes 20080227
925 // it's pixelWidth because that is what is used to generate the pixel Z coordinates of the layers, multiplying by pixelDepth. So, layer Z coords are: pixelDepth / pixelWidth (i.e. microns divided by microns/px gives px)
929 rvx
= rvy
= rvz
= null;
933 /** Sets but does NOT apply the given calibration. */
934 public void setCalibration(final Calibration cal
) {
938 public boolean isCalibrated() {
939 return null != this.cal
;
941 public Calibration
getCalibrationCopy() {
942 return null == this.cal ?
null : this.cal
.copy();
945 /** Invert the order of points. Will clear all vector arrays if any! */
947 public void reverse() {
948 tags ^
= REVERSED
; // may be re-reversed
949 // reverse point arrays
953 // eliminate vector data
955 if (null != vx
|| null != vy
|| null != vz
) {
958 if (null != rvx
|| null != rvy
|| null != rvz
) {
959 rvx
= rvy
= rvz
= null;
961 if (null != source
) Collections
.reverse(source
);
964 public boolean isReversed() {
965 return 0 != (this.tags
& REVERSED
);
968 static public VectorString3D
createInterpolated(final Editions ed
, final double alpha
) throws Exception
{
969 final VectorString3D vs1
= (VectorString3D
)ed
.getVS1();
970 final VectorString3D vs2
= (VectorString3D
)ed
.getVS2();
971 return vs1
.createInterpolated(vs2
, ed
, alpha
);
974 /** Create an interpolated VectorString3D between this and the given one, with the proper weight 'alpha' which must be 0 <= weight <= 1 (otherwise returns null) */
975 public VectorString3D
createInterpolated(final VectorString3D other
, final Editions ed
, final double alpha
) throws Exception
{
978 System
.out
.println("THIS METHOD IS BROKEN IN SOME UNKNOWN HORRIBLE WAY.\nFor now, use VectorString3D.createInterpolatedPoints(...) method, which is known to work -- even if conceptually not fully accurate.");
982 // check deltas: must be equal
983 if (Math
.abs(this.delta
- other
.delta
) > 0.00000001) {
984 throw new Exception("deltas are not the same: this.delta=" + this.delta
+ " other.delta=" + other
.delta
);
986 // check nonsense weight
987 if (alpha
< 0 || alpha
> 1) return null;
988 // if 0 or 1, the limits, the returned VectorString3D will be equal to this one or to the other, but relative to this one in position.
990 // else, make a proper intermediate curve
991 double[] vx1
= this.vx
;
992 double[] vy1
= this.vy
;
993 double[] vz1
= this.vz
;
994 double[] vx2
= other
.vx
;
995 double[] vy2
= other
.vy
;
996 double[] vz2
= other
.vz
;
997 boolean relative
= false;
998 if (null != this.rvx
&& null != other
.rvx
) {
1009 final int[][] editions
= ed
.getEditions();
1010 final int n_editions
= ed
.length(); // == Math.max(this.length, other.length)
1011 final double[] px
= new double[n_editions
];
1012 final double[] py
= new double[n_editions
];
1013 final double[] pz
= new double[n_editions
];
1015 // first point: an average of both starting points
1016 px
[0] = (this.x
[0] * (1-alpha
) + other
.x
[0] * alpha
);
1017 py
[0] = (this.y
[0] * (1-alpha
) + other
.y
[0] * alpha
);
1018 pz
[0] = (this.z
[0] * (1-alpha
) + other
.z
[0] * alpha
);
1021 final int end
= n_editions
;
1022 if (Editions
.INSERTION
== editions
[0][0] || Editions
.DELETION
== editions
[0][0]) {
1026 final int n
= this.length();
1027 final int m
= other
.length();
1031 double vs_x
=0, vs_y
=0, vs_z
=0;
1032 final Vector v
= new Vector();
1033 final Vector v_newref
= new Vector();
1034 final Vector v_delta
= new Vector(this.delta
, 0, 0);
1035 final Vector v_i1
= new Vector();
1036 final double[] d
= new double[3];
1038 final boolean with_source
= (null != this.source
1039 && null != other
.source
);
1040 final ArrayList
<ArrayList
<Point3d
>> the_source
= with_source ?
1041 new ArrayList
<ArrayList
<Point3d
>>()
1045 for (int e
=start
; e
<end
; e
++) {
1048 // check for deletions and insertions at the lower-right edges of the matrix:
1050 if (i
== n
) i
= 0; // zero, so the starting vector is applied.
1058 // merge j, i without deep cloning
1059 final ArrayList
<Point3d
> ap
= new ArrayList
<Point3d
>();
1060 ap
.addAll(this.source
.get(i
));
1061 ap
.addAll(other
.source
.get(j
));
1065 // TODO: add dependents!
1069 switch (editions
[e
][0]) {
1070 case Editions
.INSERTION
:
1071 vs_x
= vx2
[j
] * alpha
;
1072 vs_y
= vy2
[j
] * alpha
;
1073 vs_z
= vz2
[j
] * alpha
;
1075 case Editions
.DELETION
:
1076 vs_x
= vx1
[i
] * (1.0 - alpha
);
1077 vs_y
= vy1
[i
] * (1.0 - alpha
);
1078 vs_z
= vz1
[i
] * (1.0 - alpha
);
1080 case Editions
.MUTATION
:
1081 vs_x
= vx1
[i
] * (1.0 - alpha
) + vx2
[j
] * alpha
;
1082 vs_y
= vy1
[i
] * (1.0 - alpha
) + vy2
[j
] * alpha
;
1083 vs_y
= vz1
[i
] * (1.0 - alpha
) + vz2
[j
] * alpha
;
1086 // using same vectors as last edition
1087 System
.out
.println("\nIgnoring unknown edition " + editions
[e
][0]);
1095 // must inverse rotate the vector, so that instead of the difference relative to (delta,0,0)
1096 // it becomes the difference relative to (vx1,vy1,vz1)-1, i.e. absolute again in relation to this VectorString3D
1097 v_newref
.set(vx1
[i
], vy1
[i
], vz1
[i
]);
1098 v_i1
.set(vx1
[i
-1], vy1
[i
-1], vz1
[i
-1]);
1099 v
.set(vs_x
, vs_y
, vs_z
);
1100 v
.changeRef(v_delta
, v_i1
, v_newref
);
1102 vs_x
= vx1
[i
-1] + d
[0]; // making the difference vector be an absolute vector relative to this (uf!)
1103 vs_y
= vy1
[i
-1] + d
[1];
1104 vs_z
= vz1
[i
-1] + d
[2];
1107 if (next
+1 == px
.length
) {
1108 System
.out
.println("breaking early");
1112 px
[next
+1] = px
[next
] + vs_x
;
1113 py
[next
+1] = py
[next
] + vs_y
;
1114 py
[next
+1] = py
[next
] + vs_z
;
1120 } catch (final Exception e
) {
1121 e
.printStackTrace();
1122 System
.out
.println("next: " + next
+ " length: " + px
.length
+ " i,j: " + i
+ ", " + j
);
1126 final VectorString3D vs_interp
= new VectorString3D(px
, py
, pz
, isClosed());
1127 vs_interp
.source
= the_source
;
1128 vs_interp
.n_sources
= this.n_sources
+ other
.n_sources
;
1132 /** Where axis is any of VectorString.X_AXIS, .Y_AXIS or .Z_AXIS,
1133 and the mirroring is done relative to the local 0,0 of this VectorString. */
1134 public void mirror(final int axis
) {
1135 final Transform3D t
= new Transform3D();
1137 case VectorString
.X_AXIS
:
1138 t
.setScale(new Vector3d(-1, 1, 1));
1141 case VectorString
.Y_AXIS
:
1142 t
.setScale(new Vector3d(1, -1, 1));
1145 case VectorString
.Z_AXIS
:
1146 t
.setScale(new Vector3d(1, 1, -1));
1152 final Point3d p
= new Point3d();
1153 transform(x
, y
, z
, t
, p
);
1154 if (null != this.vx
) transform(vx
, vy
, vz
, t
, p
);
1155 if (null != this.rvx
) transform(rvx
, rvy
, rvz
, t
, p
);
1158 private final void transform(final double[] x
, final double[] y
, final double[] z
, final Transform3D t
, final Point3d p
) {
1159 for (int i
=0; i
<this.length
; i
++) {
1160 p
.x
= x
[i
]; p
.y
= y
[i
]; p
.z
= z
[i
];
1162 x
[i
] = p
.x
; y
[i
] = p
.y
; z
[i
] = p
.z
;
1166 public boolean isMirroredX() { return 0 != (tags
& MIRROR_X
); }
1167 public boolean isMirroredY() { return 0 != (tags
& MIRROR_Y
); }
1168 public boolean isMirroredZ() { return 0 != (tags
& MIRROR_Z
); }
1169 public byte getTags() { return tags
; }
1171 /** The physical length of this sequence of points. */
1172 public double computeLength() {
1174 for (int i
=1; i
<length
; i
++) {
1175 len
+= Math
.sqrt(Math
.pow(x
[i
] - x
[i
-1], 2) + Math
.pow(y
[i
] - y
[i
-1], 2) + Math
.pow(z
[i
] - z
[i
-1], 2));
1180 /** Returns a normalized average vector, or null if not resampled. */
1182 private Vector3d makeAverageNormalizedVector() {
1183 if (null == vx || null == vy || null == vz) return null;
1184 final Vector3d v = new Vector3d();
1185 for (int i=0; i<length; i++) {
1195 /** The sum of all vectors, or what is the same: a vector from first to last points. */
1196 public Vector3d
sumVector() {
1197 return new Vector3d(x
[length
-1] - x
[0], y
[length
-1] - y
[0], z
[length
-1] - z
[0]);
1200 static public final double distance(final double x1
, final double y1
, final double z1
,
1201 final double x2
, final double y2
, final double z2
) {
1202 return Math
.sqrt(Math
.pow(x1
- x2
, 2)
1203 + Math
.pow(y1
- y2
, 2)
1204 + Math
.pow(z1
- z2
, 2));
1207 static public final double sqDistance(final double x1
, final double y1
, final double z1
,
1208 final double x2
, final double y2
, final double z2
) {
1209 return Math
.pow(x1
- x2
, 2)
1210 + Math
.pow(y1
- y2
, 2)
1211 + Math
.pow(z1
- z2
, 2);
1214 static public final double distance(final VectorString3D vs1
, final int i
, final VectorString3D vs2
, final int j
) {
1215 return distance(vs1
.x
[i
], vs1
.y
[i
], vs1
.z
[i
],
1216 vs2
.x
[j
], vs2
.y
[j
], vs2
.z
[j
]);
1219 /** Distance from point i in this to point j in vs2. */
1221 public double distance(final int i
, final VectorString vs
, final int j
) {
1222 final VectorString3D vs2
= (VectorString3D
)vs
;
1223 return distance(x
[i
], y
[i
], z
[i
],
1224 vs2
.x
[j
], vs2
.y
[j
], vs2
.z
[j
]);
1227 /** Distance from the point at i to p.*/
1228 public final double distance(final int i
, final Point3d p
) {
1229 return distance(p
.x
, p
.y
, p
.z
,
1233 public void translate(final Vector3d v
) {
1234 for (int i
=0; i
<length
; i
++) {
1239 // vx, vy, vz not affected by translations, of course.
1242 public void transform(final Transform3D t
) {
1243 final Point3d p
= new Point3d();
1244 if (null != x
) transform(t
, x
, y
, z
, length
, p
);
1245 if (null != vx
) transform(t
, vx
, vy
, vz
, length
, p
);
1246 if (null != rvx
) transform(t
, rvx
, rvy
, rvz
, length
, p
);
1249 static private void transform(final Transform3D t
, final double[] x
, final double[] y
, final double[] z
, final int length
, final Point3d p
) {
1250 for (int i
=0; i
<length
; i
++) {
1262 static private void transform(Transform3D trans, Transform3D rot, Vector3d v) {
1268 public Point3d
computeCenterOfMass() {
1269 final Point3d v
= new Point3d();
1270 for (int i
=0; i
<length
; i
++) {
1277 /** Create a new VectorString for the given range. If last < first, it will be created as reversed. */
1279 public VectorString
subVectorString(int first
, int last
) throws Exception
{
1280 final boolean reverse
= last
< first
;
1282 final int tmp
= first
;
1286 final int len
= last
- first
+ 1;
1287 final double[] x
= new double[len
];
1288 final double[] y
= new double[len
];
1289 final double[] z
= new double[len
];
1290 System
.arraycopy(this.x
, first
, x
, 0, len
);
1291 System
.arraycopy(this.y
, first
, y
, 0, len
);
1292 System
.arraycopy(this.z
, first
, z
, 0, len
);
1293 final VectorString3D vs
= new VectorString3D(x
, y
, z
, this.isClosed());
1294 vs
.source
= cloneSource(first
, last
);
1295 if (reverse
) vs
.reverse();
1296 vs
.cal
= null == this.cal ?
null : this.cal
.copy();
1297 vs
.tags
= this.tags
;
1298 vs
.delta
= this.delta
;
1300 if (null != this.vx
) {
1301 // this is resampled, so:
1302 vs
.delta
= this.delta
;
1304 vs
.vx
= new double[len
];
1305 vs
.vy
= new double[len
];
1306 vs
.vz
= new double[len
];
1307 for (int i
=1; i
<len
; i
++) {
1308 vs
.vx
[i
] = vs
.x
[i
] - vs
.x
[i
-1];
1309 vs
.vy
[i
] = vs
.y
[i
] - vs
.y
[i
-1];
1310 vs
.vz
[i
] = vs
.z
[i
] - vs
.z
[i
-1];
1312 if (null != this.rvx
) {
1320 /** Create a new VectorString3D which is the weighted average between the two VectorString3D that make the Editions.
1321 * @param alpha is the weight, between 0 and 1.
1323 static public VectorString3D
createInterpolatedPoints(final Editions ed
, final double alpha
) {
1324 return createInterpolatedPoints(ed
, alpha
, 0, ed
.editions
.length
-1);
1326 /** Create a new VectorString3D which is the weighted average between the two VectorString3D that make the Editions.
1327 * @param alpha is the weight, between 0 and 1.
1328 * @param first is the first index to consider for the interpolated VectorString3D.
1329 * @param last is the last index to consider for the interpolated VectorString3D.
1331 static public VectorString3D
createInterpolatedPoints(final Editions ed
, final double alpha
, final int first
, final int last
) {
1333 final VectorString3D vs1
= (VectorString3D
)ed
.vs1
;
1334 if (alpha
<= 0) return (VectorString3D
)vs1
.clone();
1335 final VectorString3D vs2
= (VectorString3D
)ed
.vs2
;
1336 if (alpha
>= 1) return (VectorString3D
)vs2
.clone();
1337 // else make weighted average
1338 final double[] x
= new double[last
- first
+ 1];
1339 final double[] y
= new double[x
.length
];
1340 final double[] z
= new double[x
.length
];
1341 final int len1
= vs1
.length();
1342 final int len2
= vs2
.length();
1344 final boolean with_source
= (null != vs1
.source
1345 && null != vs2
.source
);
1346 final ArrayList
<ArrayList
<Point3d
>> the_source
= with_source ?
1347 new ArrayList
<ArrayList
<Point3d
>>()
1350 for (int k
=first
, next
=0; k
<=last
; k
++, next
++) {
1351 final int[] e
= ed
.editions
[k
];
1352 int i
= e
[1]; if (i
>= len1
) i
= len1
-1; // patching error that I don't understand TODO
1353 int j
= e
[2]; if (j
>= len2
) j
= len2
-1;
1354 x
[next
] = vs1
.x
[i
] * alpha
+ vs2
.x
[j
] * (1 - alpha
);
1355 y
[next
] = vs1
.y
[i
] * alpha
+ vs2
.y
[j
] * (1 - alpha
);
1356 z
[next
] = vs1
.z
[i
] * alpha
+ vs2
.z
[j
] * (1 - alpha
);
1359 // merge i, j without deep cloning
1360 final ArrayList
<Point3d
> ap
= new ArrayList
<Point3d
>();
1361 ap
.addAll(vs1
.source
.get(i
));
1362 ap
.addAll(vs2
.source
.get(j
));
1368 System
.out
.println("createInterpolatedPoints: lengths " + ed
.editions
.length
+ ", " + the_source
.size() + " first,last: " + first
+ ", " + last
);
1371 final VectorString3D vs
= new VectorString3D(x
, y
, z
, ed
.vs1
.isClosed());
1372 vs
.source
= the_source
;
1373 vs
.n_sources
= vs1
.n_sources
+ vs2
.n_sources
;
1374 vs
.cal
= null == vs1
.cal ?
null : vs1
.cal
.copy();
1375 vs
.resample(vs1
.delta
, with_source
);
1379 } catch (final Exception e
) {
1380 e
.printStackTrace();
1385 /** Returns a new VectorString3D which is the result of the optimal chaining of this and the given VectorString.
1386 * The ordering of this VectorString3D is preserved; the other is thus appended at the end or prepended at te beginning, reversed as necessary.
1388 public VectorString3D
chain(final VectorString3D vs
) {
1389 if (this.isClosed() || vs
.isClosed()) {
1390 System
.out
.println("Can't chain closed VectorString3D instances.");
1393 // check both ends, find the two ends that are closest
1394 final double d1
= distance(x
[0], y
[0], z
[0],
1395 vs
.x
[0], vs
.y
[0], vs
.z
[0]);
1396 final double d2
= distance(x
[length
-1], y
[length
-1], z
[length
-1],
1397 vs
.x
[0], vs
.y
[0], vs
.z
[0]);
1398 final double d3
= distance(x
[0], y
[0], z
[0],
1399 vs
.x
[vs
.length
-1], vs
.y
[vs
.length
-1], vs
.z
[vs
.length
-1]);
1400 final double d4
= distance(x
[length
-1], y
[length
-1], z
[length
-1],
1401 vs
.x
[vs
.length
-1], vs
.y
[vs
.length
-1], vs
.z
[vs
.length
-1]);
1403 final double min
= Math
.min(d1
, Math
.min(d2
, Math
.min(d3
, d4
)));
1405 final VectorString3D vsr
= (VectorString3D
)vs
.clone();
1407 return concat(vsr
, this);
1408 } else if (d2
== min
) {
1409 return concat(this, vs
);
1410 } else if (d3
== min
) {
1411 return concat(vs
, this);
1412 } else { // if (d4 == min)
1413 final VectorString3D vsr
= (VectorString3D
)vs
.clone();
1415 return concat(this, vsr
);
1419 static private final VectorString3D
concat(final VectorString3D vs1
, final VectorString3D vs2
) {
1420 final int len
= vs1
.length
+ vs2
.length
;
1421 final double[] x
= Util
.copy(vs1
.x
, len
);
1422 final double[] y
= Util
.copy(vs1
.y
, len
);
1423 final double[] z
= Util
.copy(vs1
.z
, len
);
1424 System
.arraycopy(vs2
.x
, 0, x
, vs1
.length
, vs2
.length
);
1425 System
.arraycopy(vs2
.y
, 0, y
, vs1
.length
, vs2
.length
);
1426 System
.arraycopy(vs2
.z
, 0, z
, vs1
.length
, vs2
.length
);
1428 return new VectorString3D(x
, y
, z
, false);
1429 } catch (final Exception e
) {
1434 /** Makes a substring starting at @param first (inclusive) and finishing at @param last (non-inclusive, aka last-1). */
1435 public VectorString3D
substring(final int first
, final int last
) {
1436 if (first
< 0 || last
> length
) return null;
1437 final int len
= last
- first
; // no +1 because last is non-inclusive
1439 final VectorString3D vs
= new VectorString3D(Util
.copy(x
, first
, len
), Util
.copy(y
, first
, len
), Util
.copy(z
, first
, len
), false);
1441 if (null != vx
) vs
.vx
= Util
.copy(vx
, first
, len
);
1442 if (null != vy
) vs
.vy
= Util
.copy(vy
, first
, len
);
1443 if (null != vz
) vs
.vz
= Util
.copy(vz
, first
, len
);
1444 if (null != rvx
) vs
.rvx
= Util
.copy(rvx
, first
, len
);
1445 if (null != rvy
) vs
.rvy
= Util
.copy(rvy
, first
, len
);
1446 if (null != rvz
) vs
.rvz
= Util
.copy(rvz
, first
, len
);
1447 if (null != source
) {
1448 // shallow clone the source points
1449 vs
.source
= new ArrayList
<ArrayList
<Point3d
>>();
1450 for (final ArrayList
<Point3d
> ap
: this.source
) {
1451 vs
.source
.add(new ArrayList
<Point3d
>(ap
));
1454 vs
.tags
= this.tags
;
1455 vs
.cal
= null == this.cal ?
null : this.cal
.copy();
1457 } catch (final Exception e
) {
1458 e
.printStackTrace();
1464 public int getDimensions() { return 3; }
1466 static public final double getAverageVectorLength(final int[] i
, final VectorString3D
[] vs
) {
1468 for (int k
=vs
.length
; k
>-1; k
--) {
1469 final VectorString3D v
= vs
[k
]; // java cannot even optimize this .. pitiful
1471 len
+= Math
.sqrt(Math
.pow(v
.x
[j
], 2) + Math
.pow(v
.y
[j
], 2) + Math
.pow(v
.z
[j
], 2));
1473 return len
/ vs
.length
;
1476 /** Determine if any point of the given VectorString3D falls within a radius of any of the points in this VectorString3D. */
1477 public boolean isNear(final VectorString3D vs
, final double radius
) {
1478 final double sq_radius
= radius
* radius
;
1479 for (int k
=0; k
<this.length
; k
++) {
1480 for (int i
=0; i
<vs
.length
; i
++) {
1481 final double sqd
= sqDistance(x
[k
], y
[k
], z
[k
], vs
.x
[i
], vs
.y
[i
], vs
.z
[i
]);
1482 //Util.log("radius: " + sq_radius + " sqd: " + sqd);
1483 if (sqd
<= sq_radius
) {
1484 System
.out
.println("Found nearby " + vs
+ " at " + Math
.sqrt(sqd
));
1492 /** If null != source, compute the StdDev at each point in this VectorString3D, by comparing it with its associated source points. Returns null if there is no source.
1493 * What is meant for stdDev is the stdDev of the points from which any given point in this VectorString3D was generated, returned as a distance to use as radius around that point. */
1494 public double[] getStdDevAtEachPoint() {
1495 if (null == source
) return null;
1496 final double[] stdDev
= new double[length
];
1498 System
.out
.println("len x: " + length
+ " len source: " + source
.size());
1499 for (final ArrayList
<Point3d
> ap
: source
) {
1500 // 1 - Expected: the current position
1501 final Point3d expected
= new Point3d(x
[i
], y
[i
], z
[i
]);
1502 // 2 - Sum of squares of differences of the distances to the expected position
1504 for (final Point3d p
: ap
) sd
+= Math
.pow(p
.distance(expected
), 2);
1506 stdDev
[i
] = Math
.sqrt(sd
/ ap
.size());
1508 // Test separately for each dimension : it's the same
1513 for (Point3d p : ap) {
1514 sdx += Math.pow(p.x - expected.x, 2);
1515 sdy += Math.pow(p.y - expected.y, 2);
1516 sdz += Math.pow(p.z - expected.z, 2);
1518 sdx = Math.sqrt(sdx / ap.size());
1519 sdy = Math.sqrt(sdy / ap.size());
1520 sdz = Math.sqrt(sdz / ap.size());
1522 sd = stdDev[i]; // from above
1524 // stdDev as radial distance:
1525 stdDev[i] = Math.sqrt(sdx*sdx + sdy*sdy + sdz*sdz);
1527 System.out.println("check: sd = " + sd +
1528 " sd indep = " + stdDev[i]);