Search in sources :

Example 1 with AxisAngle4f

use of org.scijava.vecmath.AxisAngle4f 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)

Aggregations

Calibration (ij.measure.Calibration)1 Color (java.awt.Color)1 Point (java.awt.Point)1 Point2D (java.awt.geom.Point2D)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 Transform3D (org.scijava.java3d.Transform3D)1 AxisAngle4f (org.scijava.vecmath.AxisAngle4f)1 Color3f (org.scijava.vecmath.Color3f)1 Point3f (org.scijava.vecmath.Point3f)1 Vector3f (org.scijava.vecmath.Vector3f)1