Search in sources :

Example 51 with Point3f

use of org.scijava.vecmath.Point3f in project TrakEM2 by trakem2.

the class Ball method generateTriangles.

/**
 * Put all balls as a single 'mesh'; the returned list contains all faces as three consecutive Point3f. The mesh is also translated by x,y,z of this Displayable.
 */
public List<Point3f> generateTriangles(final double scale, final double[][][] globe) {
    try {
        Class.forName("org.scijava.vecmath.Point3f");
    } catch (final ClassNotFoundException cnfe) {
        Utils.log("Java3D is not installed.");
        return null;
    }
    final Calibration cal = layer_set.getCalibrationCopy();
    // modify the globe to fit each ball's radius and x,y,z position
    final ArrayList<Point3f> list = new ArrayList<Point3f>();
    // transform points
    // local pointers, since they may be transformed
    double[][] p = this.p;
    double[] p_width = this.p_width;
    if (!this.at.isIdentity()) {
        final Object[] ob = getTransformedData();
        p = (double[][]) ob[0];
        p_width = (double[]) ob[1];
    }
    final int sign = cal.pixelDepth < 0 ? -1 : 1;
    // for each ball
    for (int i = 0; i < n_points; i++) {
        // create local globe for the ball, and translate it to z,y,z
        final double[][][] ball = new double[globe.length][globe[0].length][3];
        for (int z = 0; z < ball.length; z++) {
            for (int k = 0; k < ball[0].length; k++) {
                // the line below says: to each globe point, multiply it by the radius of the particular ball, then translate to the ball location, then translate to this Displayable's location, then scale to the Display3D scale.
                ball[z][k][0] = (globe[z][k][0] * p_width[i] + p[0][i]) * scale * cal.pixelWidth;
                ball[z][k][1] = (globe[z][k][1] * p_width[i] + p[1][i]) * scale * cal.pixelHeight;
                // not pixelDepth, see day notes 20080227. Because pixelDepth is in microns/px, not in px/microns, and the z coord here is taken from the z of the layer, which is in pixels.
                ball[z][k][2] = (globe[z][k][2] * p_width[i] + layer_set.getLayer(p_layer[i]).getZ()) * scale * cal.pixelWidth * sign;
            }
        }
        // create triangular faces and add them to the list
        for (int z = 0; z < ball.length - 1; z++) {
            // the parallels
            for (int k = 0; k < ball[0].length - 1; k++) {
                // meridian points
                // half quadrant (a triangle)
                list.add(new Point3f((float) ball[z][k][0], (float) ball[z][k][1], (float) ball[z][k][2]));
                list.add(new Point3f((float) ball[z + 1][k + 1][0], (float) ball[z + 1][k + 1][1], (float) ball[z + 1][k + 1][2]));
                list.add(new Point3f((float) ball[z + 1][k][0], (float) ball[z + 1][k][1], (float) ball[z + 1][k][2]));
                // the other half quadrant
                list.add(new Point3f((float) ball[z][k][0], (float) ball[z][k][1], (float) ball[z][k][2]));
                list.add(new Point3f((float) ball[z][k + 1][0], (float) ball[z][k + 1][1], (float) ball[z][k + 1][2]));
                list.add(new Point3f((float) ball[z + 1][k + 1][0], (float) ball[z + 1][k + 1][1], (float) ball[z + 1][k + 1][2]));
            }
        // the Point3f could be initialized through reflection, by getting the Construntor from the Class and calling new Instance(new Object[]{new Double(x), new Double(y), new Double(z)), so it would compile even in the absence of java3d
        }
    }
    return list;
}
Also used : Point3f(org.scijava.vecmath.Point3f) ArrayList(java.util.ArrayList) Calibration(ij.measure.Calibration)

Example 52 with Point3f

use of org.scijava.vecmath.Point3f in project TrakEM2 by trakem2.

the class Display3D method createFatPoint.

/**
 * Creates a calibrated sphere to represent a point at LayerSet pixel coordinates wx, wy, wz, with radius wr.
 */
public List<Point3f> createFatPoint(final double wx, final double wy, final double wz, final double wr, final Calibration cal) {
    final double[][][] globe = Ball.generateGlobe(12, 12);
    final int sign = cal.pixelDepth < 0 ? -1 : 1;
    for (int z = 0; z < globe.length; z++) {
        for (int k = 0; k < globe[0].length; k++) {
            globe[z][k][0] = (globe[z][k][0] * wr + wx) * scale * cal.pixelWidth;
            globe[z][k][1] = (globe[z][k][1] * wr + wy) * scale * cal.pixelHeight;
            // not pixelDepth, see day notes 20080227. Because pixelDepth is in microns/px, not in px/microns, and the z coord here is taken from the z of the layer, which is in pixels.
            globe[z][k][2] = (globe[z][k][2] * wr + wz) * scale * cal.pixelWidth * sign;
        }
    }
    final ArrayList<Point3f> list = new ArrayList<Point3f>();
    // create triangular faces and add them to the list
    for (int z = 0; z < globe.length - 1; z++) {
        // the parallels
        for (int k = 0; k < globe[0].length - 1; k++) {
            // meridian points
            // half quadrant (a triangle)
            list.add(new Point3f((float) globe[z][k][0], (float) globe[z][k][1], (float) globe[z][k][2]));
            list.add(new Point3f((float) globe[z + 1][k + 1][0], (float) globe[z + 1][k + 1][1], (float) globe[z + 1][k + 1][2]));
            list.add(new Point3f((float) globe[z + 1][k][0], (float) globe[z + 1][k][1], (float) globe[z + 1][k][2]));
            // the other half quadrant
            list.add(new Point3f((float) globe[z][k][0], (float) globe[z][k][1], (float) globe[z][k][2]));
            list.add(new Point3f((float) globe[z][k + 1][0], (float) globe[z][k + 1][1], (float) globe[z][k + 1][2]));
            list.add(new Point3f((float) globe[z + 1][k + 1][0], (float) globe[z + 1][k + 1][1], (float) globe[z + 1][k + 1][2]));
        }
    }
    return list;
}
Also used : Point3f(org.scijava.vecmath.Point3f) ArrayList(java.util.ArrayList)

Example 53 with Point3f

use of org.scijava.vecmath.Point3f in project TrakEM2 by trakem2.

the class M method createIcosahedron.

/**
 * Returns a "3D Viewer"-ready list mesh, centered at 0,0,0 and with radius as the radius of the enclosing sphere.
 */
public static final List<Point3f> createIcosahedron(int subdivisions, final float radius) {
    List<Point3f> ps = new ArrayList<Point3f>();
    for (int i = 0; i < icosfaces.length; i++) {
        for (int k = 0; k < 3; k++) {
            ps.add(new Point3f(icosahedron[icosfaces[i][k]]));
        }
    }
    while (subdivisions-- > 0) {
        final List<Point3f> sub = new ArrayList<Point3f>();
        // Take three consecutive points, which define a face, and create 4 faces out of them.
        for (int i = 0; i < ps.size(); i += 3) {
            final Point3f p0 = ps.get(i);
            final Point3f p1 = ps.get(i + 1);
            final Point3f p2 = ps.get(i + 2);
            final Point3f p01 = new Point3f((p0.x + p1.x) / 2, (p0.y + p1.y) / 2, (p0.z + p1.z) / 2);
            final Point3f p02 = new Point3f((p0.x + p2.x) / 2, (p0.y + p2.y) / 2, (p0.z + p2.z) / 2);
            final Point3f p12 = new Point3f((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2);
            // lower left:
            sub.add(p0);
            sub.add(p01);
            sub.add(p02);
            // upper:
            // as copies
            sub.add(new Point3f(p01));
            sub.add(p1);
            sub.add(p12);
            // lower right:
            sub.add(new Point3f(p12));
            sub.add(p2);
            sub.add(new Point3f(p02));
            // center:
            sub.add(new Point3f(p01));
            sub.add(new Point3f(p12));
            sub.add(new Point3f(p02));
        }
        ps = sub;
    }
    // Project all vertices to the surface of a sphere of radius 1
    final Vector3f v = new Vector3f();
    for (final Point3f p : ps) {
        v.set(p);
        v.normalize();
        v.scale(radius);
        p.set(v);
    }
    return ps;
}
Also used : Point3f(org.scijava.vecmath.Point3f) Vector3f(org.scijava.vecmath.Vector3f) ArrayList(java.util.ArrayList)

Example 54 with Point3f

use of org.scijava.vecmath.Point3f in project TrakEM2 by trakem2.

the class AreaUtils method fix3DPoints.

/**
 * @param list The original points
 * @param output The accumulated list of modified points to construct a mesh from
 * @param verts The array of vertices, each index is filled if the point has been processed already.
 * @param la_Z The Layer to process points for.
 * @param la_thickness the thickness of that layer
 * @param layer_index The stack slice index corresponding to the Layer @param la.
 */
private static final void fix3DPoints(final List<Point3f> list, final TreeMap<Integer, Point3f> output, final Point3f[] verts, final double la_z, final double la_thickness, final int layer_index, final float dx, final float dy, final float rsw, final float rsh, final double sz, final int n_slices) {
    int fixed = 0;
    // Find all pixels that belong to the layer, and transform them back:
    for (int i = 0; i < verts.length; i++) {
        // already processed! The unprocessed Z is merely coincident with a processed Z.
        if (null != verts[i])
            continue;
        final Point3f p = list.get(i);
        final int pz = (int) (p.z + 0.05f);
        // final int pz = (int)(p.z + (0.5f * Math.signum(p.z)));
        if (pz >= layer_index && pz < layer_index + n_slices) {
            // correct pixel position:
            // -- The 'rsw','rsh' scales back to LayerSet coords
            // -- The 'dx','dy' translates back to this AreaList bounding box
            p.x = p.x * rsw + dx;
            p.y = p.y * rsh + dy;
            // The Z is more complicated: the Z of the layer, scaled relative to the layer thickness
            // using pixelWidth, not pixelDepth!
            p.z = (float) ((la_z + la_thickness * (p.z - layer_index)) * sz);
            verts[i] = p;
            output.put(i, p);
            fixed++;
        }
    }
// Utils.log("fix between " + layer_index + " and " + (layer_index + n_slices) + " (" + fixed + ")");
}
Also used : Point3f(org.scijava.vecmath.Point3f)

Example 55 with Point3f

use of org.scijava.vecmath.Point3f in project TrakEM2 by trakem2.

the class AreaUtils method generateTriangles.

/**
 * Expects areas in local coordinates to the Displayable @param d.
 *  @param d
 *  @param scale The scaling of the entire universe, to limit the overall box
 *  @param resample_ The optimization parameter for marching cubes (i.e. a value of 2 will scale down to half, then apply marching cubes, then scale up by 2 the vertices coordinates).
 *  @param areas
 *  @return The List of triangles involved, specified as three consecutive vertices. A list of Point3f vertices.
 */
public static List<Point3f> generateTriangles(final Displayable d, final double scale, final int resample_, final Map<Layer, Area> areas) {
    // in the LayerSet, layers are ordered by Z already.
    try {
        int n = areas.size();
        if (0 == n)
            return null;
        final int resample;
        if (resample_ <= 0) {
            resample = 1;
            Utils.log2("Fixing zero or negative resampling value to 1.");
        } else
            resample = resample_;
        final LayerSet layer_set = d.getLayerSet();
        final AffineTransform aff = d.getAffineTransformCopy();
        final Rectangle r = d.getBoundingBox(null);
        // remove translation from a copy of the Displayable's AffineTransform
        final AffineTransform at_translate = new AffineTransform();
        at_translate.translate(-r.x, -r.y);
        aff.preConcatenate(at_translate);
        // incorporate resampling scaling into the transform
        final AffineTransform atK = new AffineTransform();
        // Utils.log("resample: " + resample + "  scale: " + scale);
        // 'scale' is there to limit gigantic universes
        final double K = (1.0 / resample) * scale;
        atK.scale(K, K);
        aff.preConcatenate(atK);
        final Calibration cal = layer_set.getCalibrationCopy();
        // Find first layer, compute depth, and fill in the depth vs area map
        Layer first_layer = null, last_layer = null;
        final int w = (int) Math.ceil(r.width * K);
        final int h = (int) Math.ceil(r.height * K);
        int depth = 0;
        final Map<Integer, Area> ma = new HashMap<Integer, Area>();
        for (final Layer la : layer_set.getLayers()) {
            // layers sorted by Z ASC
            final Area area = areas.get(la);
            if (null != area) {
                ma.put(depth, area);
                if (null == first_layer) {
                    first_layer = la;
                }
                // Utils.log("area at depth " + depth + " for layer " + la);
                depth++;
                n--;
            } else if (0 != depth) {
                // Utils.log("Empty area at depth " + depth);
                // an empty layer
                depth++;
            }
            if (0 == n) {
                last_layer = la;
                // no more areas to paint
                break;
            }
        }
        if (0 == depth) {
            Utils.log("ERROR could not find any areas for " + d);
            return null;
        }
        if (0 != n) {
            Utils.log("WARNING could not find all areas for " + d);
        }
        // No zero-padding: Marching Cubes now can handle edges
        final ShapeList<ByteType> shapeList = new ShapeListCached<ByteType>(new int[] { w, h, depth }, new ByteType(), 32);
        final Image<ByteType> shapeListImage = new Image<ByteType>(shapeList, shapeList.getBackground(), "ShapeListContainer");
        // 255 or -1 don't work !? So, giving the highest value (127) that is both a byte and an int.
        final ByteType intensity = new ByteType((byte) 127);
        for (final Map.Entry<Integer, Area> e : ma.entrySet()) {
            Area a = e.getValue();
            if (!aff.isIdentity()) {
                a = M.areaInIntsByRounding(a.createTransformedArea(aff));
            }
            shapeList.addShape(a, intensity, new int[] { e.getKey() });
        }
        // debug:
        // ImagePlus imp = ImageJFunctions.displayAsVirtualStack(shapeListImage);
        // imp.getProcessor().setMinAndMax( 0, 255 );
        // imp.show();
        // Utils.log2("Using imglib Shape List Image Container");
        // Now marching cubes
        // origins at 0,0,0: uncalibrated
        final List<Point3f> list = new MCTriangulator().getTriangles(shapeListImage, 1, new float[3]);
        // The list of triangles has coordinates:
        // - in x,y: in pixels, scaled by K = (1 / resample) * scale,
        // translated by r.x, r.y (the top-left coordinate of this AreaList bounding box)
        // - in z: in stack slice indices
        // So all x,y,z must be corrected in x,y and z of the proper layer
        // final double offset = first_layer.getZ();
        final int i_first_layer = layer_set.indexOf(first_layer);
        // The x,y translation to correct each point by:
        final float dx = (float) (r.x * scale * cal.pixelWidth);
        final float dy = (float) (r.y * scale * cal.pixelHeight);
        // Correct x,y by resampling and calibration, but not scale
        // scale is already in the pixel coordinates
        final float rsw = (float) (resample * cal.pixelWidth);
        final float rsh = (float) (resample * cal.pixelHeight);
        // no resampling in Z. and Uses pixelWidth, not pixelDepth.
        final double sz = scale * cal.pixelWidth;
        // debug:
        /*
			// which p.z types exist?
			final TreeSet<Float> ts = new TreeSet<Float>();
			for (final Iterator it = list.iterator(); it.hasNext(); ) {
				ts.add(((Point3f)it.next()).z);
			}
			for (final Float pz : ts) Utils.log2("A z: " + pz);
			*/
        // debug: How many different Z?
        /*
			HashSet<Float> zs = new HashSet<Float>();
			for (Point3f p : list) {
				zs.add(p.z);
			}
			ArrayList<Float> a = new ArrayList<Float>(zs);
			java.util.Collections.sort(a);
			for (Float f : a) {
				Utils.log("f: " + f);
			}
			*/
        // Utils.log2("Number of slices: " + imp.getNSlices());
        // Fix all points:
        // Read from list, modify and put into verts
        // and don't modify it if the verts already has it (it's just coincident)
        final Point3f[] verts = new Point3f[list.size()];
        // Utils.log("number of verts: " + verts.length + " mod 3: " + (verts.length % 3));
        final TreeMap<Integer, Point3f> output = new TreeMap<Integer, Point3f>();
        // The first section generates vertices at -1 and 0
        // The last section generates them at last_section_index and last_section_index +1
        // Capture from -1 to 0
        fix3DPoints(list, output, verts, first_layer.getZ(), 0, -1, dx, dy, rsw, rsh, sz, 1);
        int slice_index = 0;
        for (final Layer la : layer_set.getLayers().subList(i_first_layer, i_first_layer + depth)) {
            // If layer is empty, continue
            /* // YEAH don't! At least the immediate next layer would have points, like the extra Z level after last layer, to account for the thickness of the layer!
				if (empty_layers.contains(la)) {
					slice_index++;
					continue;
				}
				*/
            fix3DPoints(list, output, verts, la.getZ(), la.getThickness(), slice_index, dx, dy, rsw, rsh, sz, 1);
            slice_index++;
        }
        // Do the last layer again. The last layer has two Z planes in which it has pixels:
        try {
            // Capture from last_section_index to last_section_index+1, inclusive
            fix3DPoints(list, output, verts, last_layer.getZ() + last_layer.getThickness(), 0, slice_index, dx, dy, rsw, rsh, sz, 2);
        } catch (final Exception ee) {
            IJError.print(ee);
        }
        // Handle potential errors:
        if (0 != list.size() - output.size()) {
            Utils.log2("Unprocessed/unused points: " + (list.size() - output.size()));
            for (int i = 0; i < verts.length; i++) {
                if (null == verts[i]) {
                    final Point3f p = (Point3f) list.get(i);
                    Utils.log2("verts[" + i + "] = " + p.x + ", " + p.y + ", " + p.z + "  p.z as int: " + ((int) (p.z + 0.05f)));
                }
            }
            return new ArrayList<Point3f>(output.values());
        } else {
            return java.util.Arrays.asList(verts);
        }
    } catch (final Exception e) {
        e.printStackTrace();
    }
    return null;
}
Also used : HashMap(java.util.HashMap) Rectangle(java.awt.Rectangle) ArrayList(java.util.ArrayList) ShapeListCached(mpicbg.imglib.container.shapelist.ShapeListCached) ByteType(mpicbg.imglib.type.numeric.integer.ByteType) Image(mpicbg.imglib.image.Image) Point3f(org.scijava.vecmath.Point3f) LayerSet(ini.trakem2.display.LayerSet) Calibration(ij.measure.Calibration) TreeMap(java.util.TreeMap) Layer(ini.trakem2.display.Layer) ExecutionException(java.util.concurrent.ExecutionException) Area(java.awt.geom.Area) AffineTransform(java.awt.geom.AffineTransform) HashMap(java.util.HashMap) Map(java.util.Map) TreeMap(java.util.TreeMap)

Aggregations

Point3f (org.scijava.vecmath.Point3f)58 ArrayList (java.util.ArrayList)20 Calibration (ij.measure.Calibration)6 HashMap (java.util.HashMap)5 Color3f (org.scijava.vecmath.Color3f)5 Point (java.awt.Point)4 Color (java.awt.Color)3 Area (java.awt.geom.Area)3 Map (java.util.Map)3 TreeMap (java.util.TreeMap)3 Point3d (com.github.quickhull3d.Point3d)2 QuickHull3D (com.github.quickhull3d.QuickHull3D)2 PolygonRoi (ij.gui.PolygonRoi)2 Rectangle (java.awt.Rectangle)2 AffineTransform (java.awt.geom.AffineTransform)2 HashSet (java.util.HashSet)2 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)2 Vector3f (org.scijava.vecmath.Vector3f)2 CustomLineMesh (customnode.CustomLineMesh)1 CustomMesh (customnode.CustomMesh)1