1 package ini
.trakem2
.utils
;
3 import ij
.gui
.PolygonRoi
;
5 import ij
.gui
.ShapeRoi
;
6 import ij
.process
.FloatPolygon
;
7 import ini
.trakem2
.display
.VectorDataTransform
;
8 import ini
.trakem2
.persistence
.Loader
;
9 import ini
.trakem2
.vector
.VectorString2D
;
11 import java
.awt
.Color
;
12 import java
.awt
.Graphics2D
;
13 import java
.awt
.Polygon
;
14 import java
.awt
.Rectangle
;
15 import java
.awt
.Shape
;
16 import java
.awt
.geom
.AffineTransform
;
17 import java
.awt
.geom
.Area
;
18 import java
.awt
.geom
.GeneralPath
;
19 import java
.awt
.geom
.NoninvertibleTransformException
;
20 import java
.awt
.geom
.PathIterator
;
21 import java
.awt
.geom
.Point2D
;
22 import java
.awt
.image
.BufferedImage
;
23 import java
.awt
.image
.DataBufferByte
;
24 import java
.util
.ArrayList
;
25 import java
.util
.Collection
;
26 import java
.util
.List
;
28 import javax
.vecmath
.Point3f
;
29 import javax
.vecmath
.Tuple3d
;
30 import javax
.vecmath
.Vector3d
;
31 import javax
.vecmath
.Vector3f
;
33 /** TrakEM2's mathematician. */
34 public final class M
{
36 /*============== 3D =============*/
38 static public final double distancePointToSegment
39 (final double px
, final double py
, final double pz
,
40 final double lx1
, final double ly1
, final double lz1
,
41 final double lx2
, final double ly2
, final double lz2
)
43 return distancePointToSegment(new Vector3d(px
, py
, pz
),
44 new Vector3d(lx1
, ly1
, lz1
),
45 new Vector3d(lx2
, ly2
, lz2
));
47 static public final double distancePointToSegmentSq
48 (final double px
, final double py
, final double pz
,
49 final double lx1
, final double ly1
, final double lz1
,
50 final double lx2
, final double ly2
, final double lz2
)
52 return distancePointToSegmentSq(new Vector3d(px
, py
, pz
),
53 new Vector3d(lx1
, ly1
, lz1
),
54 new Vector3d(lx2
, ly2
, lz2
));
57 /** Minimum distance between point v0 and a line segment defined by points v1 and v2. */
58 static public final double distancePointToSegment(final Vector3d p
, final Vector3d v1
, final Vector3d v2
) {
59 final Vector3d v
= new Vector3d();
61 final Vector3d w
= new Vector3d();
64 final double c1
= w
.dot(v
);
65 if (c1
<= 0) return distance(p
, v1
);
67 final double c2
= v
.dot(v
);
68 if (c2
<= c1
) return distance(p
, v2
);
70 final double b
= c1
/ c2
;
72 final Vector3d pb
= new Vector3d(v
);
75 return distance(p
, pb
);
78 static public final double distancePointToSegmentSq(final Vector3d p
, final Vector3d v1
, final Vector3d v2
) {
79 final Vector3d v
= new Vector3d();
81 final Vector3d w
= new Vector3d();
84 final double c1
= w
.dot(v
);
85 if (c1
<= 0) return distanceSq(p
, v1
);
87 final double c2
= v
.dot(v
);
88 if (c2
<= c1
) return distanceSq(p
, v2
);
90 final double b
= c1
/ c2
;
92 final Vector3d pb
= new Vector3d(v
);
95 return distanceSq(p
, pb
);
99 static public final double distance(final Tuple3d t1
, final Tuple3d t2
) {
100 return Math
.sqrt( Math
.pow(t1
.x
- t2
.x
, 2)
101 + Math
.pow(t1
.y
- t2
.y
, 2)
102 + Math
.pow(t1
.z
- t2
.z
, 2) );
105 static public final double distanceSq(final Tuple3d t1
, final Tuple3d t2
) {
106 return Math
.pow(t1
.x
- t2
.x
, 2)
107 + Math
.pow(t1
.y
- t2
.y
, 2)
108 + Math
.pow(t1
.z
- t2
.z
, 2);
111 static public final double distance(final double x1
, final double y1
, final double z1
,
112 final double x2
, final double y2
, final double z2
) {
113 return Math
.sqrt(Math
.pow(x1
- x2
, 2)
114 + Math
.pow(y1
- y2
, 2)
115 + Math
.pow(z1
- z2
, 2));
118 static public final double distance(final double x1
, final double y1
,
119 final double x2
, final double y2
) {
120 return Math
.sqrt(Math
.pow(x1
- x2
, 2)
121 + Math
.pow(y1
- y2
, 2));
124 static public final double distanceSq(final double x1
, final double y1
, final double z1
,
125 final double x2
, final double y2
, final double z2
) {
126 return Math
.pow(x1
- x2
, 2)
127 + Math
.pow(y1
- y2
, 2)
128 + Math
.pow(z1
- z2
, 2);
132 static public double distancePointToLine(final double px
, final double py
, final double pz
, final double lx1
, final double ly1
, final double lz1
, final double lx2
, final double ly2
, final double lz2
) {
133 final double segment_length
= new Vector3d(lx2
- lx1
, ly2
- ly1
, lz2
- lz1
).length();
134 if (0 == segment_length
) return 0;
135 final Vector3d cross
= new Vector3d();
136 cross
.cross(new Vector3d(px
- lx1
, py
- ly1
, pz
- lz1
),
137 new Vector3d(px
- lx2
, py
- ly2
, pz
- lz2
));
138 return cross
.length() / segment_length
;
142 /*============== 2D =============*/
144 /** For a point px,py return its distance to a line defined by points lx1,ly1 and lx2,ly2. Formula as in http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html at 2008-11-11 20:19 Zurich time.*/
145 static public double distancePointToLine(final double px
, final double py
, final double lx1
, final double ly1
, final double lx2
, final double ly2
) {
146 return Math
.abs( (lx2
- lx1
) * (ly1
- py
) - (lx1
- px
) * (ly2
- ly1
) )
147 / Math
.sqrt( Math
.pow(lx2
- lx1
, 2) + Math
.pow(ly2
- ly1
, 2) );
151 /** For a point px,py return its distance to a line SEGMENT defined by points lx1,ly1 and lx2,ly2. */
152 static public double distancePointToSegment(final double px
, final double py
, final double lx1
, final double ly1
, final double lx2
, final double ly2
) {
153 double xu
= px
- lx1
;
154 double yu
= py
- ly1
;
155 double xv
= lx2
- lx1
;
156 double yv
= ly2
- ly1
;
157 if (xu
* xv
+ yu
* yv
< 0) return Math
.sqrt( Math
.pow(px
- lx1
, 2) + Math
.pow(py
- ly1
, 2) );
163 if (xu
* xv
+ yu
* yv
< 0) return Math
.sqrt( Math
.pow(px
- lx2
, 2) + Math
.pow(py
- ly2
, 2) );
165 return Math
.abs( (px
* (ly1
- ly2
) + py
* (lx2
- lx1
) + (lx1
* ly2
- lx2
* ly1
) ) / Math
.sqrt( Math
.pow(lx2
- lx1
, 2) + Math
.pow(ly2
- ly1
, 2) ) );
168 static public final boolean equals(final double a
, final double b
) {
169 return Math
.abs(a
- b
) < Utils
.FL_ERROR
;
172 static public final Point2D
.Double
transform(final AffineTransform affine
, final double x
, final double y
) {
173 final Point2D
.Double pSrc
= new Point2D
.Double(x
, y
);
174 if (affine
.isIdentity()) return pSrc
;
175 final Point2D
.Double pDst
= new Point2D
.Double();
176 affine
.transform(pSrc
, pDst
);
180 static public final Point2D
.Double
inverseTransform(final AffineTransform affine
, final double x
, final double y
) {
181 final Point2D
.Double pSrc
= new Point2D
.Double(x
, y
);
182 if (affine
.isIdentity()) return pSrc
;
183 final Point2D
.Double pDst
= new Point2D
.Double();
185 affine
.createInverse().transform(pSrc
, pDst
);
186 } catch (NoninvertibleTransformException nite
) {
192 // from utilities.c in my CurveMorphing C module ... from C! Java is a low level language with the disadvantages of the high level languages ...
193 /** Returns the angle in radians of the given polar coordinates, correcting the Math.atan2 output.
194 * Adjusting so that 0 is 3 o'clock, PI+PI/2 is 12 o'clock, PI is 9 o'clock, and PI/2 is 6 o'clock (why atan2 doesn't output angles this way? I remember I had the same problem for Pipe.java in the A_3D_editing plugin) */
195 static public final double getAngle(final double x
, final double y
) {
197 double a
= Math
.atan2(x
, y
);
198 // fix too large angles (beats me why are they ever generated)
199 if (a
> 2 * Math
.PI
) {
202 // fix atan2 output scheme to match my mental scheme
203 if (a
>= 0.0 && a
<= Math
.PI
/2) {
205 } else if (a
< 0 && a
>= -Math
.PI
) {
207 } else if (a
> Math
.PI
/2 && a
<= Math
.PI
) {
208 a
= Math
.PI
+ Math
.PI
+ Math
.PI
/2 - a
;
214 /*============== Geometry =============*/
216 static public final boolean isEmpty(final Area area
) {
217 final Rectangle b
= area
.getBounds();
218 return 0 == b
.width
|| 0 == b
.height
;
221 /** Test whether the areas intersect each other. */
222 static public final boolean intersects(final Area a1
, final Area a2
) {
223 final Area b
= new Area(a1
);
225 final java
.awt
.Rectangle r
= b
.getBounds();
226 return 0 != r
.width
&& 0 != r
.height
;
229 static public final Area
getArea(final Roi roi
) {
230 if (null == roi
) return null;
231 if (roi
instanceof ShapeRoi
) return getArea((ShapeRoi
)roi
);
232 return getArea(new ShapeRoi(roi
));
234 static public final Area
getArea(final ShapeRoi sroi
) {
235 if (null == sroi
) return null;
236 AffineTransform at
= new AffineTransform();
237 Rectangle bounds
= sroi
.getBounds();
238 at
.translate(bounds
.x
, bounds
.y
);
239 Area area
= new Area(sroi
.getShape());
244 /** Returns the approximated area of the given Area object; Loader can be null; if not, it's used to secure memory space. */
245 static public final double measureArea(Area area
, final Loader loader
) {
248 Rectangle bounds
= area
.getBounds();
250 if (bounds
.width
> 2048 || bounds
.height
> 2048) { // TODO value 2048 should be reconsidered as a project property
251 scale
= 2048.0 / bounds
.width
;
254 Utils
.log("Can't measure: area too large, out of scale range for approximation.");
257 AffineTransform at
= new AffineTransform();
258 at
.translate(-bounds
.x
, -bounds
.y
);
259 at
.scale(scale
, scale
);
260 area
= area
.createTransformedArea(at
);
261 bounds
= area
.getBounds();
262 if (0 == bounds
.width
|| 0 == bounds
.height
) {
263 Utils
.log("Can't measure: area too large, approximates zero.");
266 if (null != loader
) loader
.releaseToFit(bounds
.width
* bounds
.height
* 3);
267 BufferedImage bi
= new BufferedImage(bounds
.width
, bounds
.height
, BufferedImage
.TYPE_BYTE_INDEXED
);
268 Graphics2D g
= bi
.createGraphics();
269 g
.setColor(Color
.white
);
271 final byte[] pixels
= ((DataBufferByte
)bi
.getRaster().getDataBuffer()).getData(); // buffer.getData();
272 for (int i
=pixels
.length
-1; i
>-1; i
--) {
273 //if (255 == (pixels[i]&0xff)) sum++;
274 if (0 != pixels
[i
]) sum
++;
278 if (1 != scale
) sum
= sum
/ (scale
* scale
);
279 } catch (Throwable e
) {
285 /** Compute the area of the triangle defined by 3 points in 3D space, returning half of the length of the vector resulting from the cross product of vectors p1p2 and p1p3. */
286 static public final double measureArea(final Point3f p1
, final Point3f p2
, final Point3f p3
) {
287 // Distance from p1 to line p2-p3, times length of line p2-p3, divided by 2:
288 return 0.5 * M
.distancePointToLine(p1
.x
, p1
.y
, p1
.z
,
294 /** Returns true if the roi is of closed shape type like an OvalRoi, ShapeRoi, a Roi rectangle, etc. */
295 static public final boolean isAreaROI(final Roi roi
) {
296 switch (roi
.getType()) {
306 @SuppressWarnings("null")
307 static public final Collection
<Polygon
> getPolygons(Area area
) {
308 final ArrayList
<Polygon
> pols
= new ArrayList
<Polygon
>();
311 final float[] coords
= new float[6];
312 for (PathIterator pit
= area
.getPathIterator(null); !pit
.isDone(); ) {
313 int seg_type
= pit
.currentSegment(coords
);
315 case PathIterator
.SEG_MOVETO
:
318 case PathIterator
.SEG_LINETO
:
319 pol
.addPoint((int)coords
[0], (int)coords
[1]);
321 case PathIterator
.SEG_CLOSE
:
325 Utils
.log2("WARNING: unhandled seg type.");
336 static public final Collection
<Polygon
> getPolygonsByRounding(final Area area
) {
337 final ArrayList
<Polygon
> pols
= new ArrayList
<Polygon
>();
338 Polygon pol
= new Polygon();
340 final float[] coords
= new float[6];
341 for (PathIterator pit
= area
.getPathIterator(null); !pit
.isDone(); ) {
342 int seg_type
= pit
.currentSegment(coords
);
344 case PathIterator
.SEG_MOVETO
:
345 case PathIterator
.SEG_LINETO
:
346 pol
.addPoint(Math
.round(coords
[0]), Math
.round(coords
[1]));
348 case PathIterator
.SEG_CLOSE
:
353 Utils
.log2("WARNING: unhandled seg type.");
364 /** Return a new Area resulting from applying @param ict to @param a;
365 * assumes areas consists of paths with moveTo, lineTo and close operations. */
366 static public final Area
transform(final mpicbg
.models
.CoordinateTransform ict
, final Area a
) {
367 final GeneralPath path
= new GeneralPath();
368 final float[] coords
= new float[6];
369 final float[] fp
= new float[2];
371 for (final PathIterator pit
= a
.getPathIterator(null); !pit
.isDone(); ) {
372 final int seg_type
= pit
.currentSegment(coords
);
375 ict
.applyInPlace(fp
);
377 case PathIterator
.SEG_MOVETO
:
378 path
.moveTo(fp
[0], fp
[1]);
380 case PathIterator
.SEG_LINETO
:
381 case PathIterator
.SEG_CLOSE
:
382 path
.lineTo(fp
[0], fp
[1]);
385 Utils
.log2("WARNING: unhandled seg type.");
393 return new Area(path
);
396 /** Apply in place the @param ict to the Area @param a, but only for the part that intersects the roi. */
397 static final public void apply(final mpicbg
.models
.CoordinateTransform ict
, final Area roi
, final Area a
) {
398 final Area intersection
= new Area(a
);
399 intersection
.intersect(roi
);
400 a
.subtract(intersection
);
401 a
.add(M
.transform(ict
, intersection
));
404 static final public void apply(final VectorDataTransform vdt
, final Area a
) {
405 apply(vdt
, a
, false);
408 /** Parts of @param a not intersected by any of @param vdt rois will be left untouched if @param remove_outside is false. */
409 static final public void apply(final VectorDataTransform vdt
, final Area a
, final boolean remove_outside
) {
410 final Area b
= new Area();
411 for (final VectorDataTransform
.ROITransform rt
: vdt
.transforms
) {
412 // Cut the intersecting part from a:
413 final Area intersection
= new Area(a
);
414 intersection
.intersect(rt
.roi
);
415 a
.subtract(intersection
);
416 // .. and add it to b, transformed:
417 b
.add(M
.transform(rt
.ct
, intersection
));
421 if (remove_outside
) {
422 // Clear areas not affected any ROITransform
423 Utils
.log("WARNING: parts of an area in layer " + vdt
.layer
+ "\n did not intersect any transformation target\n and where removed.");
425 } else Utils
.log("WARNING: parts of an area in layer " + vdt
.layer
+ "\n remain untransformed.");
428 // Add b (the transformed parts) to what remains of a
432 /** Detect if a point is not in the area, but lays inside one of its path, which is returned as a Polygon. Otherwise returns null. The given x,y must be already in the Area's coordinate system. */
433 static public final Polygon
findPath(final Area area
, final int x
, final int y
) {
434 Polygon pol
= new Polygon();
435 for (PathIterator pit
= area
.getPathIterator(null); !pit
.isDone(); ) {
436 float[] coords
= new float[6];
437 int seg_type
= pit
.currentSegment(coords
);
439 case PathIterator
.SEG_MOVETO
:
440 case PathIterator
.SEG_LINETO
:
441 pol
.addPoint((int)coords
[0], (int)coords
[1]);
443 case PathIterator
.SEG_CLOSE
:
444 if (pol
.contains(x
, y
)) return pol
;
449 Utils
.log2("WARNING: unhandled seg type.");
460 /** Converts all points in @param area to ints by casting. */
461 static public final Area
areaInInts(final Area area
) {
462 final Area a
= new Area();
463 for (final Polygon pol
: M
.getPolygons(area
)) {
464 a
.exclusiveOr(new Area(pol
));
469 /** Converts all points in @param area to ints by rounding. */
470 static public final Area
areaInIntsByRounding(final Area area
) {
471 final Area a
= new Area();
472 for (final Polygon pol
: M
.getPolygonsByRounding(area
)) {
473 a
.add(new Area(pol
));
478 /* ================================================= */
480 public static void quicksort(float[] data
, Object
[] sortAlso
) throws IllegalArgumentException
{
481 if (data
.length
!= sortAlso
.length
) {
482 throw new IllegalArgumentException("data and sortAlso arrays don't have the same length.");
484 quicksort(data
, sortAlso
, 0, data
.length
-1);
487 /** Adapted from Stephan Preibisch's mpi.fruitfly.math.General homonimous method. */
488 public static void quicksort(final float[] data
, final Object
[] sortAlso
,
489 final int left
, final int right
) {
490 if (data
.length
< 2) return;
491 int i
= left
, j
= right
;
492 float x
= data
[(left
+ right
) / 2];
494 while (data
[i
] < x
) i
++;
495 while (x
< data
[j
]) j
--;
497 float temp
= data
[i
];
501 Object temp2
= sortAlso
[i
];
502 sortAlso
[i
] = sortAlso
[j
];
509 if (left
< j
) quicksort(data
, sortAlso
, left
, j
);
510 if (i
< right
) quicksort(data
, sortAlso
, i
, right
);
513 static private Polygon arrowhead
= null;
515 /** Create an arrowhead at the end of the line segment defined by x1,y1 and x2,y2. */
516 static public final Shape
createArrowhead(final double x1
, final double y1
, final double x2
, final double y2
) {
517 return createArrowhead(x1
, y1
, x2
, y2
, 1.0);
519 static public final Shape
createArrowhead(final double x1
, final double y1
, final double x2
, final double y2
, final double magnification
) {
520 if (null == arrowhead
) arrowhead
= new Polygon(new int[]{-14, -13, 0, -13, -14, -9}, new int[]{-5, -5, 0, 5, 5, 0}, 6);
521 final AffineTransform aff
= new AffineTransform();
522 aff
.translate(x2
, y2
);
523 aff
.rotate(M
.getAngle(x2
- x1
, y2
- y1
));
524 if (magnification
< 1.0) aff
.scale(magnification
, magnification
);
525 return aff
.createTransformedShape(arrowhead
);
528 static final private float phi
= (1 + (float)Math
.sqrt(5)) / 2;
529 static final private float[][] icosahedron
= { { phi
, 1, 0 },
541 static final private int[][] icosfaces
= { { 0, 8, 4 },
562 /** Returns a "3D Viewer"-ready list mesh, centered at 0,0,0 and with radius as the radius of the enclosing sphere. */
563 static public final List
<Point3f
> createIcosahedron(int subdivisions
, float radius
) {
564 List
<Point3f
> ps
= new ArrayList
<Point3f
>();
565 for (int i
=0; i
<icosfaces
.length
; i
++) {
566 for (int k
=0; k
<3; k
++) {
567 ps
.add(new Point3f(icosahedron
[icosfaces
[i
][k
]]));
570 while (subdivisions
-- > 0) {
571 final List
<Point3f
> sub
= new ArrayList
<Point3f
>();
572 // Take three consecutive points, which define a face, and create 4 faces out of them.
573 for (int i
=0; i
<ps
.size(); i
+=3) {
574 Point3f p0
= ps
.get(i
);
575 Point3f p1
= ps
.get(i
+1);
576 Point3f p2
= ps
.get(i
+2);
578 Point3f p01
= new Point3f((p0
.x
+ p1
.x
)/2, (p0
.y
+ p1
.y
)/2, (p0
.z
+ p1
.z
)/2);
579 Point3f p02
= new Point3f((p0
.x
+ p2
.x
)/2, (p0
.y
+ p2
.y
)/2, (p0
.z
+ p2
.z
)/2);
580 Point3f p12
= new Point3f((p1
.x
+ p2
.x
)/2, (p1
.y
+ p2
.y
)/2, (p1
.z
+ p2
.z
)/2);
586 sub
.add(new Point3f(p01
)); // as copies
590 sub
.add(new Point3f(p12
));
592 sub
.add(new Point3f(p02
));
594 sub
.add(new Point3f(p01
));
595 sub
.add(new Point3f(p12
));
596 sub
.add(new Point3f(p02
));
601 // Project all vertices to the surface of a sphere of radius 1
602 final Vector3f v
= new Vector3f();
603 for (final Point3f p
: ps
) {
613 /** Reuses the @param fp to apply in place. */
614 static public final void apply(final mpicbg
.models
.CoordinateTransform ict
, final double[][] p
, final int i
, final float[] fp
) {
615 fp
[0] = (float)p
[0][i
];
616 fp
[1] = (float)p
[1][i
];
617 ict
.applyInPlace(fp
);
622 /** The @param ict is expected to transform the data as if this data was expressed in world coordinates,
623 * so this method returns a transformation list that prepends the transform from local to world, then the @param ict, then from world to local. */
624 static public final mpicbg
.models
.CoordinateTransform
wrap(final AffineTransform to_world
, final mpicbg
.models
.CoordinateTransform ict
, final AffineTransform to_local
) throws Exception
{
625 final mpicbg
.models
.CoordinateTransformList
<mpicbg
.models
.CoordinateTransform
> chain
626 = new mpicbg
.models
.CoordinateTransformList
<mpicbg
.models
.CoordinateTransform
>(); // bravo!
627 // 1 - Prepend to world
628 final mpicbg
.models
.AffineModel2D toworld
= new mpicbg
.models
.AffineModel2D();
629 toworld
.set(to_world
);
631 // 2 - Perform the transform in world coordinates
634 final mpicbg
.models
.AffineModel2D tolocal
= new mpicbg
.models
.AffineModel2D();
635 tolocal
.set(to_local
);
640 /** Returns the shortest possible String representation of a float number, according to the desired decimal @param precision. */
641 static public final String
shortest(final float f
, final float precision
) {
642 return Math
.abs(f
- (int)f
) < precision ?
643 Integer
.toString((int)f
)
647 /** Appends to @param sb the shortest possible String representation of a float number, according to the desired decimal @param precision. */
648 static public final void appendShortest(final float f
, final float precision
, final StringBuilder sb
) {
649 if (Math
.abs(f
- (int)f
) < precision
) {
656 /** @returns the point of intersection of the two segments a and b, or null if they don't intersect. */
657 public static final float[] computeSegmentsIntersection(
658 float ax0
, float ay0
, float ax1
, float ay1
,
659 float bx0
, float by0
, float bx1
, float by1
) {
660 float d
= (ax0
- ax1
)*(by0
- by1
) - (ay0
- ay1
) * (bx0
- bx1
);
661 if (0 == d
) return null;
662 float xi
= ((bx0
- bx1
)*(ax0
*ay1
- ay0
*ax1
) - (ax0
- ax1
)*(bx0
*by1
- by0
*bx1
)) / d
;
663 float yi
= ((by0
- by1
)*(ax0
*ay1
- ay0
*ax1
) - (ay0
- ay1
)*(bx0
*by1
- by0
*bx1
)) / d
;
664 if (xi
< Math
.min(ax0
, ax1
) || xi
> Math
.max(ax0
, ax1
)) return null;
665 if (xi
< Math
.min(bx0
, bx1
) || xi
> Math
.max(bx0
, bx1
)) return null;
666 if (yi
< Math
.min(ay0
, ay1
) || yi
> Math
.max(ay0
, ay1
)) return null;
667 if (yi
< Math
.min(by0
, by1
) || yi
> Math
.max(by0
, by1
)) return null;
668 return new float[]{xi
, yi
};
671 /** Returns an array of two Area objects, or of 1 if the @param proi doesn't intersect @param a. */
672 public static final Area
[] splitArea(final Area a
, final PolygonRoi proi
, final Rectangle world
) {
673 if (!a
.getBounds().intersects(proi
.getBounds())) return new Area
[]{a
};
675 int[] x
= proi
.getXCoordinates(),
676 y
= proi
.getYCoordinates();
677 final Rectangle rb
= proi
.getBounds();
678 final int len
= proi
.getNCoordinates();
679 int x0
= x
[0] + rb
.x
;
680 int y0
= y
[0] + rb
.y
;
681 int xN
= x
[len
-1] + rb
.x
;
682 int yN
= y
[len
-1] + rb
.y
;
684 // corners, clock-wise:
685 int[][] corners
= new int[][]{new int[]{world
.x
, world
.y
}, // top left
686 new int[]{world
.x
+ world
.width
, world
.y
}, // top right
687 new int[]{world
.x
+ world
.width
, world
.y
+ world
.height
}, // bottom right
688 new int[]{world
.x
, world
.y
+ world
.height
}}; // bottom left
689 // Which corner is closest to x0,y0, and which to xN,yN
690 double min_dist0
= Double
.MAX_VALUE
;
693 double min_distN
= Double
.MAX_VALUE
;
695 for (int[] corner
: corners
) {
696 double d
= distance(x0
, y0
, corner
[0], corner
[1]);
701 d
= distance(xN
, yN
, corner
[0], corner
[1]);
708 // Create new Area 'b' with which to intersect Area 'a':
713 // 0 1 2 3: when there difference is 2, there is a point in between
714 if (2 != Math
.abs(min_iN
- min_i0
)) {
718 // -4 and -1 will be the drifted corners
720 xb
[l
-4] = corners
[min_iN
][0];
721 yb
[l
-4] = corners
[min_iN
][1];
722 xb
[l
-3] = corners
[min_iN
][0];
723 yb
[l
-3] = corners
[min_iN
][1];
724 xb
[l
-2] = corners
[min_i0
][0];
725 yb
[l
-2] = corners
[min_i0
][1];
726 xb
[l
-1] = corners
[min_i0
][0];
727 yb
[l
-1] = corners
[min_i0
][1];
732 // -5 and -1 will be the drifted corners
734 xb
[l
-5] = corners
[min_iN
][0];
735 yb
[l
-5] = corners
[min_iN
][1];
736 xb
[l
-4] = corners
[min_iN
][0];
737 yb
[l
-4] = corners
[min_iN
][1];
738 xb
[l
-3] = corners
[(min_iN
+ min_i0
) / 2][0];
739 yb
[l
-3] = corners
[(min_iN
+ min_i0
) / 2][1];
740 xb
[l
-2] = corners
[min_i0
][0];
741 yb
[l
-2] = corners
[min_i0
][1];
742 xb
[l
-1] = corners
[min_i0
][0];
743 yb
[l
-1] = corners
[min_i0
][1];
745 // Enter polyline points, corrected for proi bounds
746 for (int k
=0; k
<len
; k
++) {
750 // Drift corners to closest point on the sides
752 int dx
= Math
.abs(xb
[l
+i1
] - (x
[len
-1] + rb
.x
)),
753 dy
= Math
.abs(yb
[l
+i1
] - (y
[len
-1] + rb
.y
));
754 if (dy
< dx
) xb
[l
+i1
] = x
[len
-1] + rb
.x
;
755 else yb
[l
+i1
] = y
[len
-1] + rb
.y
;
757 dx
= Math
.abs(xb
[l
+i2
] - (x
[0] + rb
.x
));
758 dy
= Math
.abs(yb
[l
+i2
] - (y
[0] + rb
.y
));
759 if (dy
< dx
) xb
[l
+i2
] = x
[0] + rb
.x
;
760 else yb
[l
+i2
] = y
[0] + rb
.y
;
762 Area b
= new Area(new Polygon(xb
, yb
, xb
.length
));
763 Area c
= new Area(world
);
766 return new Area
[]{b
, c
};
769 public static final VectorString2D
asVectorString2D(final Polygon pol
, final double z
) throws Exception
{
770 double[] x
= new double[pol
.npoints
];
771 double[] y
= new double[pol
.npoints
];
772 for (int i
=0; i
<x
.length
; i
++) {
773 x
[i
] = pol
.xpoints
[i
];
774 y
[i
] = pol
.ypoints
[i
];
776 return new VectorString2D(x
, y
, z
, true);
779 public static final double volumeOfTruncatedCone(final double r1
, final double r2
, final double height
) {
788 public static final double lateralAreaOfTruncatedCone(final double r1
, final double r2
, final double height
) {
789 return Math
.PI
* (r1
+ r2
) * Math
.sqrt(Math
.pow(r2
- r1
, 2) + height
* height
);
792 /** The lateral area plus the two circles. */
793 public static final double totalAreaOfTruncatedCone(final double r1
, final double r2
, final double height
) {
794 return lateralAreaOfTruncatedCone(r1
, r2
, height
)
795 + Math
.PI
* (r1
* r1
+ r2
* r2
);
798 public static final void convolveGaussianSigma1(final float[] in
, final float[] out
, final CircularSequence seq
) {
799 // Weights for sigma = 1 and kernel 5x1, normalized so they add up to 1.
800 final float w2
= 0.05448869f
,
803 // Handle boundary case
804 if (out
.length
< 5) {
805 for (int i
=0; i
<out
.length
; ++i
) {
806 out
[i
] = in
[seq
.setPosition(i
-2)] * w2
807 + in
[seq
.setPosition(i
-1)] * w1
808 + in
[seq
.setPosition(i
)] * w0
809 + in
[seq
.setPosition(i
+1)] * w1
810 + in
[seq
.setPosition(i
+2)] * w2
;
814 // Optimized general case: use the CircularSequence only at the ends
815 for (int i
=0; i
<2; ++i
) {
816 out
[i
] = in
[seq
.setPosition(i
-2)] * w2
817 + in
[seq
.setPosition(i
-1)] * w1
818 + in
[seq
.setPosition(i
)] * w0
819 + in
[seq
.setPosition(i
+1)] * w1
820 + in
[seq
.setPosition(i
+2)] * w2
;
822 final int cut
= out
.length
-2;
823 for (int i
=2; i
<cut
; ++i
) {
824 out
[i
] = in
[i
-2] * w2
830 for (int i
=cut
; i
<out
.length
; ++i
) {
831 out
[i
] = in
[seq
.setPosition(i
-2)] * w2
832 + in
[seq
.setPosition(i
-1)] * w1
833 + in
[seq
.setPosition(i
)] * w0
834 + in
[seq
.setPosition(i
+1)] * w1
835 + in
[seq
.setPosition(i
+2)] * w2
;
839 /** Copied from ImageJ's ij.gui.PolygonRoi.getInterpolatedPolygon, by Wayne Rasband and collaborators.
840 * The reason I copied this method is that creating a new PolygonRoi just to get an interpolated polygon
841 * processes the float[] arrays of the coordinates, subtracting the minimum x,y. Not only it is an extra
842 * operation but it is also in place, altering data arrays. Fortunately FloatPolygon does not touch the arrays. */
843 final static public FloatPolygon
createInterpolatedPolygon(
844 final FloatPolygon p
,
845 final double interval
,
846 final boolean isLine
) {
847 double length
= p
.getLength(isLine
);
848 int npoints2
= (int)((length
*1.2)/interval
);
849 float[] xpoints2
= new float[npoints2
];
850 float[] ypoints2
= new float[npoints2
];
851 xpoints2
[0] = p
.xpoints
[0];
852 ypoints2
[0] = p
.ypoints
[0];
855 double distance
=0.0, distance2
=0.0, dx
=0.0, dy
=0.0, xinc
, yinc
;
856 double x
, y
, lastx
, lasty
, x1
, y1
, x2
=p
.xpoints
[0], y2
=p
.ypoints
[0];
857 int npoints
= p
.npoints
;
858 if (!isLine
) npoints
++;
859 for (int i
=1; i
<npoints
; i
++) {
871 distance
= Math
.sqrt(dx
*dx
+dy
*dy
);
872 xinc
= dx
*inc
/distance
;
873 yinc
= dy
*inc
/distance
;
874 lastx
=xpoints2
[n
-1]; lasty
=ypoints2
[n
-1];
875 //n2 = (int)(dx/xinc);
876 n2
= (int)(distance
/inc
);
877 if (npoints
==2) n2
++;
881 distance2
= Math
.sqrt(dx
*dx
+dy
*dy
);
882 //IJ.log(i+" "+IJ.d2s(xinc,5)+" "+IJ.d2s(yinc,5)+" "+IJ.d2s(distance,2)+" "+IJ.d2s(distance2,2)+" "+IJ.d2s(x,2)+" "+IJ.d2s(y,2)+" "+IJ.d2s(lastx,2)+" "+IJ.d2s(lasty,2)+" "+n+" "+n2);
883 if (distance2
>=interval
-inc
/2.0 && n
<xpoints2
.length
-1) {
884 xpoints2
[n
] = (float)x
;
885 ypoints2
[n
] = (float)y
;
886 //IJ.log("--- "+IJ.d2s(x,2)+" "+IJ.d2s(y,2)+" "+n);
894 return new FloatPolygon(xpoints2
, ypoints2
, n
);