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);
}
Aggregations