use of org.scijava.vecmath.Color3f 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);
}
use of org.scijava.vecmath.Color3f in project TrakEM2 by trakem2.
the class Display3DGUI method randomizeColors.
public static final void randomizeColors(final Image3DUniverse univ) {
final ArrayList<Content> cs = new ArrayList<Content>(getOrderedContents(univ));
for (int i = 0; i < cs.size(); ++i) {
if (i < colors.length) {
cs.get(i).setColor(new Color3f(colors[i]));
} else {
cs.get(i).setColor(new Color3f((float) Math.random(), (float) Math.random(), (float) Math.random()));
}
}
// Update the color bars if something is selected:
final Content content = univ.getSelected();
if (null != content)
univ.fireContentChanged(content);
}
use of org.scijava.vecmath.Color3f in project TrakEM2 by trakem2.
the class Display3DGUI method newPanelColors.
private static final JPanel newPanelColors(final Image3DUniverse univ) {
final JPanel p = new JPanel();
p.setBackground(Color.white);
final GridBagLayout gb = new GridBagLayout();
final GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.NORTHWEST;
c.fill = GridBagConstraints.HORIZONTAL;
p.setLayout(gb);
final String[] labels = new String[] { "Red", "Green", "Blue" };
final JScrollBar[] sliders = new JScrollBar[3];
final JTextField[] typers = new JTextField[3];
for (int i = 0; i < 3; ++i) {
final JScrollBar slider = new JScrollBar(JScrollBar.HORIZONTAL, 255, 1, 0, 256);
sliders[i] = slider;
final JTextField typer = new IntegerField(255, 3);
typers[i] = typer;
final int k = i;
slider.addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(final AdjustmentEvent e) {
final Content content = univ.getSelected();
if (null == content) {
Utils.log("Nothing selected!");
return;
}
Color3f color = content.getColor();
// default to yellow
if (null == color)
color = new Color3f(1, 1, 0);
final float[] co = new float[3];
color.get(co);
co[k] = e.getValue() / 255.0f;
content.setColor(new Color3f(co));
typer.setText(Integer.toString(e.getValue()));
}
});
typer.addKeyListener(new SliderTyperLink(univ, slider, typer));
final JLabel l = new JLabel(labels[i]);
// Layout
c.gridx = 0;
c.gridy = i;
gb.setConstraints(l, c);
p.add(l);
c.gridx = 1;
c.weightx = 1;
c.fill = GridBagConstraints.HORIZONTAL;
gb.setConstraints(slider, c);
p.add(slider);
c.gridx = 2;
c.weightx = 0;
c.fill = GridBagConstraints.NONE;
gb.setConstraints(typer, c);
p.add(typer);
}
// Alpha slider
c.gridx = 0;
c.gridy += 1;
final JLabel aL = new JLabel("Alpha:");
gb.setConstraints(aL, c);
p.add(aL);
c.gridx = 1;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
final JScrollBar alphaSlider = new JScrollBar(JScrollBar.HORIZONTAL, 255, 1, 0, 256);
gb.setConstraints(alphaSlider, c);
p.add(alphaSlider);
c.gridx = 2;
c.fill = GridBagConstraints.NONE;
c.weightx = 0;
final JTextField alphaTyper = new IntegerField(255, 3);
gb.setConstraints(alphaTyper, c);
p.add(alphaTyper);
alphaSlider.addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(final AdjustmentEvent e) {
final Content content = univ.getSelected();
if (null == content) {
Utils.log("Nothing selected!");
return;
}
final float alpha = e.getValue() / 255.0f;
content.setTransparency(1 - alpha);
alphaTyper.setText(Integer.toString(e.getValue()));
}
});
alphaTyper.addKeyListener(new SliderTyperLink(univ, alphaSlider, alphaTyper));
// Button to colorize randomly
c.gridx = 0;
c.gridy += 1;
c.gridwidth = 3;
c.weightx = 1;
c.insets = new Insets(15, 4, 4, 4);
final JButton r = new JButton("Assign random colors to all");
r.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
randomizeColors(univ);
}
});
gb.setConstraints(r, c);
p.add(r);
addTitledLineBorder(p, "Colors");
univ.addUniverseListener(new UniverseListener() {
@Override
public void universeClosed() {
}
@Override
public void transformationUpdated(final View arg0) {
}
@Override
public void transformationStarted(final View arg0) {
}
@Override
public void transformationFinished(final View arg0) {
}
@Override
public void contentSelected(final Content arg0) {
if (null == arg0) {
return;
}
Color3f color = arg0.getColor();
// default to yellow
if (null == color)
color = new Color3f(1, 1, 0);
final float[] co = new float[3];
color.get(co);
for (int i = 0; i < 3; ++i) {
// Disallow the slider from firing an event when its value is adjusted
sliders[i].setValueIsAdjusting(true);
final int val = (int) (co[i] * 255);
typers[i].setText(Integer.toString(val));
sliders[i].setValue(val);
}
// After all are set, re-enable, which triggers events (the color will be set three times...)
for (int i = 0; i < 3; ++i) {
sliders[i].setValueIsAdjusting(false);
}
// Alpha slider:
alphaSlider.setValueIsAdjusting(true);
final int alpha = (int) ((1 - arg0.getTransparency()) * 255);
alphaTyper.setText(Integer.toString(alpha));
alphaSlider.setValue(alpha);
alphaSlider.setValueIsAdjusting(false);
}
@Override
public void contentRemoved(final Content arg0) {
}
@Override
public void contentChanged(final Content arg0) {
if (arg0 == univ.getSelected()) {
contentSelected(arg0);
}
}
@Override
public void contentAdded(final Content arg0) {
}
@Override
public void canvasResized() {
}
});
return p;
}
use of org.scijava.vecmath.Color3f in project TrakEM2 by trakem2.
the class Display3D method addFatPoint.
/**
* Expects uncalibrated wx,wy,wz, (i.e. pixel values), to be calibrated by @param ls calibration.
*/
public static final Future<Content> addFatPoint(final String title, final LayerSet ls, final double wx, final double wy, final double wz, final double wr, final Color color) {
final Display3D d3d = Display3D.get(ls);
d3d.universe.removeContent(title);
final Content ct = d3d.universe.createContent(new CustomTriangleMesh(d3d.createFatPoint(wx, wy, wz, wr, ls.getCalibrationCopy()), new Color3f(color), 0), title);
ct.setLocked(true);
return d3d.addContent(ct);
}
use of org.scijava.vecmath.Color3f 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;
}
}
};
}
Aggregations