Search in sources :

Example 21 with Point3f

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

the class Tree method getShortTitle.

@Override
public String getShortTitle() {
    final String title = getTitle();
    if (null != title && !getClass().getSimpleName().toLowerCase().equals(title.toLowerCase()))
        return title;
    if (null == root)
        return "Empty";
    final Point3f p = getOriginPoint(true);
    return new StringBuilder("Root: x=").append(p.x).append(", y=" + p.y).append(" z=").append(p.z).toString();
}
Also used : Point3f(org.scijava.vecmath.Point3f)

Example 22 with Point3f

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

the class Treeline method generateMesh.

/**
 * Testing for performance, 100 iterations:
 * A: 3307  (current, with clearing of table on the fly)
 * B: 4613  (without clearing table)
 * C: 4012  (without point caching)
 *
 * Although in short runs (10 iterations) A can get very bad:
 * (first run of 10)
 * A: 664
 * B: 611
 * C: 196
 * (second run of 10)
 * A: 286
 * B: 314
 * C: 513  <-- gets worse !?
 *
 * Differences are not so huge in any case.
 */
/*
	static final public void testMeshGenerationPerformance(int n_iterations) {
		// test 3D mesh generation

		Layer la = Display.getFrontLayer();
		java.util.Random rnd = new java.util.Random(67779);
		Node root = new RadiusNode(rnd.nextFloat(), rnd.nextFloat(), la);
		Node parent = root;
		for (int i=0; i<10000; i++) {
			Node child = new RadiusNode(rnd.nextFloat(), rnd.nextFloat(), la);
			parent.add(child, Node.MAX_EDGE_CONFIDENCE);
			if (0 == i % 100) {
				// add a branch of 100 nodes
				Node pa = parent;
				for (int k = 0; k<100; k++) {
					Node ch = new RadiusNode(rnd.nextFloat(), rnd.nextFloat(), la);
					pa.add(ch, Node.MAX_EDGE_CONFIDENCE);
					pa = ch;
				}
			}
			parent = child;
		}

		final AffineTransform at = new AffineTransform(1, 0, 0, 1, 67, 134);

		final ArrayList list = new ArrayList();

		final LinkedList<Node> todo = new LinkedList<Node>();

		final float scale = 0.345f;
		final Calibration cal = la.getParent().getCalibration();
		final float pixelWidthScaled = (float) cal.pixelWidth * scale;
		final float pixelHeightScaled = (float) cal.pixelHeight * scale;
		final int sign = cal.pixelDepth < 0 ? -1 : 1;
		final Map<Node,Point3f> points = new HashMap<Node,Point3f>();

		// A few performance tests are needed:
		// 1 - if the map caching of points helps or recomputing every time is cheaper than lookup
		// 2 - if removing no-longer-needed points from the map helps lookup or overall slows down

		long t0 = System.currentTimeMillis();
		for (int i=0; i<n_iterations; i++) {
			// A -- current method
			points.clear();
			todo.clear();
			todo.add(root);
			list.clear();
			final float[] fps = new float[2];

			boolean go = true;
			while (go) {
				final Node node = todo.removeFirst();
				// Add children to todo list if any
				if (null != node.children) {
					for (final Node nd : node.children) todo.add(nd);
				}
				go = !todo.isEmpty();
				// Get node's 3D coordinate
				Point3f p = points.get(node);
				if (null == p) {
					fps[0] = node.x;
					fps[1] = node.y;
					at.transform(fps, 0, fps, 0, 1);
					p = new Point3f(fps[0] * pixelWidthScaled,
							fps[1] * pixelHeightScaled,
							(float)node.la.getZ() * pixelWidthScaled * sign);
					points.put(node, p);
				}
				if (null != node.parent) {
					// Create a line to the parent
					list.add(points.get(node.parent));
					list.add(p);
					if (go && node.parent != todo.getFirst().parent) {
						// node.parent point no longer needed (last child just processed)
						points.remove(node.parent);
					}
				}
			}
		}
		System.out.println("A: " + (System.currentTimeMillis() - t0));


		t0 = System.currentTimeMillis();
		for (int i=0; i<n_iterations; i++) {

			points.clear();
			todo.clear();
			todo.add(root);
			list.clear();
			final float[] fps = new float[2];

			// Simpler method, not clearing no-longer-used nodes from map
			while (!todo.isEmpty()) {
				final Node node = todo.removeFirst();
				// Add children to todo list if any
				if (null != node.children) {
					for (final Node nd : node.children) todo.add(nd);
				}
				// Get node's 3D coordinate
				Point3f p = points.get(node);
				if (null == p) {
					fps[0] = node.x;
					fps[1] = node.y;
					at.transform(fps, 0, fps, 0, 1);
					p = new Point3f(fps[0] * pixelWidthScaled,
							fps[1] * pixelHeightScaled,
							(float)node.la.getZ() * pixelWidthScaled * sign);
					points.put(node, p);
				}
				if (null != node.parent) {
					// Create a line to the parent
					list.add(points.get(node.parent));
					list.add(p);
				}
			}
		}
		System.out.println("B: " + (System.currentTimeMillis() - t0));

		t0 = System.currentTimeMillis();
		for (int i=0; i<n_iterations; i++) {

			todo.clear();
			todo.add(root);
			list.clear();

			// Simplest method: no caching in a map
			final float[] fp = new float[4];
			while (!todo.isEmpty()) {
				final Node node = todo.removeFirst();
				// Add children to todo list if any
				if (null != node.children) {
					for (final Node nd : node.children) todo.add(nd);
				}
				if (null != node.parent) {
					// Create a line to the parent
					fp[0] = node.x;
					fp[1] = node.y;
					fp[2] = node.parent.x;
					fp[3] = node.parent.y;
					at.transform(fp, 0, fp, 0, 2);
					list.add(new Point3f(fp[2] * pixelWidthScaled,
							     fp[3] * pixelHeightScaled,
							     (float)node.parent.la.getZ() * pixelWidthScaled * sign));
					list.add(new Point3f(fp[0] * pixelWidthScaled,
							     fp[1] * pixelHeightScaled,
							     (float)node.la.getZ() * pixelWidthScaled * sign));
				}
			}
		}
		System.out.println("C: " + (System.currentTimeMillis() - t0));
	}
	*/
/**
 * Returns a list of two lists: the {@code List<Point3f>} and the corresponding {@code List<Color3f>}.
 */
public MeshData generateMesh(final double scale_, int parallels) {
    // Construct a mesh made of straight tubes for each edge, and balls of the same ending diameter on the nodes.
    // 
    // TODO:
    // With some cleverness, such meshes could be welded together by merging the nearest vertices on the ball
    // surfaces, or by cleaving the surface where the diameter of the tube cuts it.
    // A tougher problem is where tubes cut each other, but perhaps if the resulting mesh is still non-manifold, it's ok.
    final float scale = (float) scale_;
    if (parallels < 3)
        parallels = 3;
    // Simple ball-and-stick model
    // first test: just the nodes as icosahedrons with 1 subdivision
    final Calibration cal = layer_set.getCalibration();
    final float pixelWidthScaled = (float) cal.pixelWidth * scale;
    final float pixelHeightScaled = (float) cal.pixelHeight * scale;
    final int sign = cal.pixelDepth < 0 ? -1 : 1;
    final List<Point3f> ico = M.createIcosahedron(1, 1);
    final List<Point3f> ps = new ArrayList<Point3f>();
    // A plane made of as many edges as parallels, with radius 1
    // Perpendicular vector of the plane is 0,0,1
    final List<Point3f> plane = new ArrayList<Point3f>();
    final double inc_rads = (Math.PI * 2) / parallels;
    double angle = 0;
    for (int i = 0; i < parallels; i++) {
        plane.add(new Point3f((float) Math.cos(angle), (float) Math.sin(angle), 0));
        angle += inc_rads;
    }
    final Vector3f vplane = new Vector3f(0, 0, 1);
    final Transform3D t = new Transform3D();
    final AxisAngle4f aa = new AxisAngle4f();
    final List<Color3f> colors = new ArrayList<Color3f>();
    final Color3f cf = new Color3f(this.color);
    final HashMap<Color, Color3f> cached_colors = new HashMap<Color, Color3f>();
    cached_colors.put(this.color, cf);
    for (final Set<Node<Float>> nodes : node_layer_map.values()) {
        for (final Node<Float> nd : nodes) {
            Point2D.Double po = transformPoint(nd.x, nd.y);
            final float x = (float) po.x * pixelWidthScaled;
            final float y = (float) po.y * pixelHeightScaled;
            final float z = (float) nd.la.getZ() * pixelWidthScaled * sign;
            // TODO r is not transformed by the AffineTransform
            final float r = ((RadiusNode) nd).r * pixelWidthScaled;
            for (final Point3f vert : ico) {
                final Point3f v = new Point3f(vert);
                v.x = v.x * r + x;
                v.y = v.y * r + y;
                v.z = v.z * r + z;
                ps.add(v);
            }
            int n_verts = ico.size();
            // Check if a 3D volume representation is necessary for this segment
            if (null != nd.parent && (0 != nd.parent.getData() || 0 != nd.getData())) {
                po = null;
                // parent:
                final Point2D.Double pp = transformPoint(nd.parent.x, nd.parent.y);
                final float parx = (float) pp.x * pixelWidthScaled;
                final float pary = (float) pp.y * pixelWidthScaled;
                final float parz = (float) nd.parent.la.getZ() * pixelWidthScaled * sign;
                // TODO r is not transformed by the AffineTransform
                final float parr = ((RadiusNode) nd.parent).r * pixelWidthScaled;
                // the vector perpendicular to the plane is 0,0,1
                // the vector from parent to child is:
                final Vector3f vpc = new Vector3f(x - parx, y - pary, z - parz);
                if (x == parx && y == pary) {
                    aa.set(0, 0, 1, 0);
                } else {
                    final Vector3f cross = new Vector3f();
                    cross.cross(vpc, vplane);
                    // not needed?
                    cross.normalize();
                    aa.set(cross.x, cross.y, cross.z, -vplane.angle(vpc));
                }
                t.set(aa);
                final List<Point3f> parent_verts = transform(t, plane, parx, pary, parz, parr);
                final List<Point3f> child_verts = transform(t, plane, x, y, z, r);
                for (int i = 1; i < parallels; i++) {
                    addTriangles(ps, parent_verts, child_verts, i - 1, i);
                    n_verts += 6;
                }
                // faces from last to first:
                addTriangles(ps, parent_verts, child_verts, parallels - 1, 0);
                n_verts += 6;
            }
            // Colors for each segment:
            Color3f c;
            if (null == nd.color) {
                c = cf;
            } else {
                c = cached_colors.get(nd.color);
                if (null == c) {
                    c = new Color3f(nd.color);
                    cached_colors.put(nd.color, c);
                }
            }
            while (n_verts > 0) {
                n_verts--;
                colors.add(c);
            }
        }
    }
    return new MeshData(ps, colors);
}
Also used : HashMap(java.util.HashMap) Transform3D(org.scijava.java3d.Transform3D) Color3f(org.scijava.vecmath.Color3f) Color(java.awt.Color) ArrayList(java.util.ArrayList) Calibration(ij.measure.Calibration) Point(java.awt.Point) Point3f(org.scijava.vecmath.Point3f) Point2D(java.awt.geom.Point2D) Vector3f(org.scijava.vecmath.Vector3f) AxisAngle4f(org.scijava.vecmath.AxisAngle4f)

Example 23 with Point3f

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

the class Treeline method transform.

private static final List<Point3f> transform(final Transform3D t, final List<Point3f> plane, final float x, final float y, final float z, final float radius) {
    final List<Point3f> ps = new ArrayList<Point3f>(plane.size());
    for (final Point3f p2 : plane) {
        final Point3f p = new Point3f(p2);
        p.scale(radius);
        t.transform(p);
        p.x += x;
        p.y += y;
        p.z += z;
        ps.add(p);
    }
    return ps;
}
Also used : Point3f(org.scijava.vecmath.Point3f) ArrayList(java.util.ArrayList)

Example 24 with Point3f

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

the class Display3D method createMesh.

/**
 * Returns a function that returns a Content object.
 *  Does NOT add the Content to the universe; it merely creates it.
 */
public Callable<Content> createMesh(final ProjectThing pt, final Displayable displ, final int resample) {
    // OBSOLETE
    final double scale = 1.0;
    return new Callable<Content>() {

        @Override
        public Content call() {
            Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
            try {
                // the list 'triangles' is really a list of Point3f, which define a triangle every 3 consecutive points. (TODO most likely Bene Schmid got it wrong: I don't think there's any need to have the points duplicated if they overlap in space but belong to separate triangles.)
                final List<Point3f> triangles;
                // boolean no_culling_ = false;
                final Class<?> c;
                final boolean line_mesh;
                final int line_mesh_mode;
                if (null == displ) {
                    c = null;
                    line_mesh = false;
                    line_mesh_mode = Integer.MAX_VALUE;
                } else {
                    c = displ.getClass();
                    line_mesh = Tree.class.isAssignableFrom(c) || Polyline.class == c;
                    if (Tree.class.isAssignableFrom(c))
                        line_mesh_mode = CustomLineMesh.PAIRWISE;
                    else if (Polyline.class == c)
                        line_mesh_mode = CustomLineMesh.CONTINUOUS;
                    else
                        // disabled
                        line_mesh_mode = Integer.MAX_VALUE;
                }
                List<Point3f> extra_triangles = null;
                List<Color3f> triangle_colors = null, extra_triangle_colors = null;
                int rs = resample;
                if (displ instanceof AreaContainer) {
                    if (// will adjust this.resample, and return it (even if it's a default value)
                    -1 == resample)
                        // will adjust this.resample, and return it (even if it's a default value)
                        rs = Display3D.this.resample = adjustResampling();
                    else
                        rs = Display3D.this.resample;
                }
                if (AreaList.class == c) {
                    triangles = ((AreaList) displ).generateTriangles(scale, rs);
                // triangles = removeNonManifold(triangles);
                } else if (Ball.class == c) {
                    final double[][][] globe = Ball.generateGlobe(12, 12);
                    triangles = ((Ball) displ).generateTriangles(scale, globe);
                } else if (displ instanceof Line3D) {
                    // Pipe and Polyline
                    // adjustResampling();  // fails horribly, needs first to correct mesh-generation code
                    triangles = ((Line3D) displ).generateTriangles(scale, 12, 1);
                } else if (displ instanceof Tree<?>) {
                    // A 3D wire skeleton, using CustomLineMesh
                    final Tree.MeshData skeleton = ((Tree<?>) displ).generateSkeleton(scale, 12, 1);
                    triangles = skeleton.verts;
                    triangle_colors = skeleton.colors;
                    if (displ instanceof Treeline) {
                        final Tree.MeshData tube = ((Treeline) displ).generateMesh(scale, 12);
                        extra_triangles = tube.verts;
                        extra_triangle_colors = tube.colors;
                    } else if (displ instanceof AreaTree) {
                        final Tree.MeshData mesh = ((AreaTree) displ).generateMesh(scale, rs);
                        extra_triangles = mesh.verts;
                        extra_triangle_colors = mesh.colors;
                    }
                    // avoid issues with MultiMesh
                    if (null != extra_triangles && extra_triangles.isEmpty())
                        extra_triangles = null;
                } else if (Connector.class == c) {
                    final Tree.MeshData octopus = ((Connector) displ).generateMesh(scale, 12);
                    triangles = octopus.verts;
                    triangle_colors = octopus.colors;
                } else if (null == displ && pt.getType().equals("profile_list")) {
                    triangles = Profile.generateTriangles(pt, scale);
                // no_culling_ = true;
                } else {
                    Utils.log("Unrecognized type for 3D mesh generation: " + (null != displ ? displ.getClass() : null) + " : " + displ);
                    triangles = null;
                }
                // safety checks
                if (null == triangles) {
                    Utils.log("Some error ocurred: can't create triangles for " + displ);
                    return null;
                }
                if (0 == triangles.size()) {
                    Utils.log2("Skipping empty mesh for " + displ.getTitle());
                    return null;
                }
                if (!line_mesh && 0 != triangles.size() % 3) {
                    Utils.log2("Skipping non-multiple-of-3 vertices list generated for " + displ.getTitle());
                    return null;
                }
                final Color color;
                final float alpha;
                final String title;
                if (null != displ) {
                    color = displ.getColor();
                    alpha = displ.getAlpha();
                    title = makeTitle(displ);
                } else if (pt.getType().equals("profile_list")) {
                    // for profile_list: get from the first (what a kludge; there should be a ZDisplayable ProfileList object)
                    final Object obp = ((ProjectThing) pt.getChildren().get(0)).getObject();
                    if (null == obp)
                        return null;
                    final Displayable di = (Displayable) obp;
                    color = di.getColor();
                    alpha = di.getAlpha();
                    title = makeProfileListTitle(pt);
                } else {
                    title = pt.toString() + " #" + pt.getId();
                    color = null;
                    alpha = 1.0f;
                }
                // Why for all? Above no_culling_ is set to true or false, depending upon type. --> Because with transparencies it looks proper and better when no_culling is true.
                // for ALL
                final boolean no_culling = true;
                Content ct = null;
                try {
                    final Color3f c3 = new Color3f(color);
                    // If it exists, remove and add as new:
                    universe.removeContent(title);
                    final CustomMesh cm;
                    if (line_mesh) {
                        // ct = universe.createContent(new CustomLineMesh(triangles, line_mesh_mode, c3, 0), title);
                        cm = new CustomLineMesh(triangles, line_mesh_mode, c3, 0);
                    } else if (no_culling) {
                        // create a mesh with the same color and zero transparency (that is, full opacity)
                        final CustomTriangleMesh mesh = new CustomTriangleMesh(triangles, c3, 0);
                        // Set mesh properties for double-sided triangles
                        final PolygonAttributes pa = mesh.getAppearance().getPolygonAttributes();
                        pa.setCullFace(PolygonAttributes.CULL_NONE);
                        pa.setBackFaceNormalFlip(true);
                        mesh.setColor(c3);
                        // After setting properties, add to the viewer
                        // ct = universe.createContent(mesh, title);
                        cm = mesh;
                    } else {
                        // ct = universe.createContent(new CustomTriangleMesh(triangles, c3, 0), title);
                        cm = new CustomTriangleMesh(triangles, c3, 0);
                    }
                    if (null != triangle_colors)
                        cm.setColor(triangle_colors);
                    if (null == extra_triangles || 0 == extra_triangles.size()) {
                        ct = universe.createContent(cm, title);
                    } else {
                        final CustomTriangleMesh extra = new CustomTriangleMesh(extra_triangles, c3, 0);
                        if (null != extra_triangle_colors) {
                            // Set mesh properties for double-sided triangles
                            final PolygonAttributes pa = extra.getAppearance().getPolygonAttributes();
                            pa.setCullFace(PolygonAttributes.CULL_NONE);
                            pa.setBackFaceNormalFlip(true);
                            extra.setColor(extra_triangle_colors);
                        }
                        ct = universe.createContent(new CustomMultiMesh(Arrays.asList(new CustomMesh[] { cm, extra })), title);
                    }
                    // Set general content properties
                    ct.setTransparency(1f - alpha);
                    // Default is unlocked (editable) transformation; set it to locked:
                    ct.setLocked(true);
                    // register mesh title
                    synchronized (ht_pt_meshes) {
                        ht_pt_meshes.put(pt, ct.getName());
                    }
                } catch (final Throwable e) {
                    Utils.logAll("Mesh generation failed for \"" + title + "\"  from " + pt);
                    IJError.print(e);
                    e.printStackTrace();
                }
                Utils.log2(pt.toString() + " n points: " + triangles.size());
                return ct;
            } catch (final Exception e) {
                IJError.print(e);
                return null;
            }
        }
    };
}
Also used : Color3f(org.scijava.vecmath.Color3f) Callable(java.util.concurrent.Callable) CustomTriangleMesh(customnode.CustomTriangleMesh) Point3f(org.scijava.vecmath.Point3f) CustomMesh(customnode.CustomMesh) CustomMultiMesh(customnode.CustomMultiMesh) Color(java.awt.Color) PolygonAttributes(org.scijava.java3d.PolygonAttributes) CustomLineMesh(customnode.CustomLineMesh) Content(ij3d.Content)

Example 25 with Point3f

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

the class Pipe method generateTriangles.

/**
 * Accepts an arrays as that returned from methods generateJoints and makeTube: first dimension is the list of points, second dimension is the number of vertices defining the circular cross section of the tube, and third dimension is the x,y,z of each vertex.
 */
public static List<Point3f> generateTriangles(final double[][][] all_points, final double scale) {
    final int n = all_points.length;
    final int parallels = all_points[0].length - 1;
    final List<Point3f> list = new ArrayList<Point3f>();
    for (int i = 0; i < n - 1; i++) {
        // minus one since last is made with previous
        for (int j = 0; j < parallels; j++) {
            // there are 12+12 triangles for each joint //it's up to 12+1 because first point is repeated at the end
            // first triangle in the quad
            list.add(new Point3f((float) (all_points[i][j][0] * scale), (float) (all_points[i][j][1] * scale), (float) (all_points[i][j][2] * scale)));
            list.add(new Point3f((float) (all_points[i][j + 1][0] * scale), (float) (all_points[i][j + 1][1] * scale), (float) (all_points[i][j + 1][2] * scale)));
            list.add(new Point3f((float) (all_points[i + 1][j][0] * scale), (float) (all_points[i + 1][j][1] * scale), (float) (all_points[i + 1][j][2] * scale)));
            // second triangle in the quad
            list.add(new Point3f((float) (all_points[i + 1][j][0] * scale), (float) (all_points[i + 1][j][1] * scale), (float) (all_points[i + 1][j][2] * scale)));
            list.add(new Point3f((float) (all_points[i][j + 1][0] * scale), (float) (all_points[i][j + 1][1] * scale), (float) (all_points[i][j + 1][2] * scale)));
            list.add(new Point3f((float) (all_points[i + 1][j + 1][0] * scale), (float) (all_points[i + 1][j + 1][1] * scale), (float) (all_points[i + 1][j + 1][2] * scale)));
        }
    }
    return list;
}
Also used : Point3f(org.scijava.vecmath.Point3f) ArrayList(java.util.ArrayList)

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