use of ini.trakem2.tree.ProjectThing in project TrakEM2 by trakem2.
the class Display method insertStack.
/**
* @param stack_patch is just a Patch of a series of Patch that make a stack of Patches.
*/
private boolean insertStack(final ProjectThing target_landmarks, final Project source, final ProjectThing source_landmarks, final Patch stack_patch) {
final List<Ball> l1 = new ArrayList<Ball>();
final List<Ball> l2 = new ArrayList<Ball>();
// source is the one that has the stack_patch
final Collection<ProjectThing> b1s = source_landmarks.findChildrenOfType("ball");
// target is this
final Collection<ProjectThing> b2s = target_landmarks.findChildrenOfType("ball");
final HashSet<String> seen = new HashSet<String>();
for (final ProjectThing b1 : b1s) {
final Ball ball1 = (Ball) b1.getObject();
if (null == ball1) {
Utils.log("ERROR: there's an empty 'ball' node in target project" + project.toString());
return false;
}
final String title1 = ball1.getTitle();
for (final ProjectThing b2 : b2s) {
final Ball ball2 = (Ball) b2.getObject();
if (null == ball2) {
Utils.log("ERROR: there's an empty 'ball' node in source project" + source.toString());
return false;
}
if (title1.equals(ball2.getTitle())) {
if (seen.contains(title1))
continue;
seen.add(title1);
l1.add(ball1);
l2.add(ball2);
}
}
}
if (l1.size() < 4) {
Utils.log("ERROR: found only " + l1.size() + " common landmarks: needs at least 4!");
return false;
}
// Extract coordinates of source project landmarks, in patch stack coordinate space
final List<double[]> c1 = new ArrayList<double[]>();
for (final Ball ball1 : l1) {
final Map<Layer, double[]> m = ball1.getRawBalls();
if (1 != m.size()) {
Utils.log("ERROR: ball object " + ball1 + " from target project " + project + " has " + m.size() + " balls instead of just 1.");
return false;
}
final Map.Entry<Layer, double[]> e = m.entrySet().iterator().next();
final Layer layer = e.getKey();
final double[] xyr = e.getValue();
final double[] fin = new double[] { xyr[0], xyr[1] };
final AffineTransform affine = ball1.getAffineTransformCopy();
try {
affine.preConcatenate(stack_patch.getAffineTransform().createInverse());
} catch (final Exception nite) {
IJError.print(nite);
return false;
}
final double[] fout = new double[2];
affine.transform(fin, 0, fout, 0, 1);
c1.add(new double[] { fout[0], fout[1], layer.getParent().indexOf(layer) });
}
// Extract coordinates of target (this) project landmarks, in calibrated world space
final List<double[]> c2 = new ArrayList<double[]>();
for (final Ball ball2 : l2) {
final double[][] b = ball2.getBalls();
if (1 != b.length) {
Utils.log("ERROR: ball object " + ball2 + " from source project " + source + " has " + b.length + " balls instead of just 1.");
return false;
}
final double[] fin = new double[] { b[0][0], b[0][1] };
final AffineTransform affine = ball2.getAffineTransformCopy();
final double[] fout = new double[2];
affine.transform(fin, 0, fout, 0, 1);
c2.add(new double[] { fout[0], fout[1], b[0][2] });
}
// Print landmarks:
Utils.log("Landmarks:");
for (Iterator<double[]> it1 = c1.iterator(), it2 = c2.iterator(); it1.hasNext(); ) {
Utils.log(Utils.toString(it1.next()) + " <--> " + Utils.toString(it2.next()));
}
// Create point matches
final List<PointMatch> pm = new ArrayList<PointMatch>();
for (Iterator<double[]> it1 = c1.iterator(), it2 = c2.iterator(); it1.hasNext(); ) {
pm.add(new mpicbg.models.PointMatch(new mpicbg.models.Point(it1.next()), new mpicbg.models.Point(it2.next())));
}
// Estimate AffineModel3D
final AffineModel3D aff3d = new AffineModel3D();
try {
aff3d.fit(pm);
} catch (final Exception e) {
IJError.print(e);
return false;
}
// Create and add the Stack
final String path = stack_patch.getImageFilePath();
final Stack st = new Stack(project, new File(path).getName(), 0, 0, getLayerSet().getLayers().get(0), path);
st.setInvertibleCoordinateTransform(aff3d);
getLayerSet().add(st);
return true;
}
use of ini.trakem2.tree.ProjectThing in project TrakEM2 by trakem2.
the class Display3D method show.
/**
* Scan the {@link ProjectThing} children and assign the renderable ones to an existing {@link Display3D} for their {@link LayerSet}, or open a new one. If {@code true == wait && -1 != resample}, then the method returns only when the mesh/es have been added.
*/
public static Future<Vector<Future<Content>>> show(final ProjectThing pt, final boolean wait, final int resample) {
if (null == pt)
return null;
final Future<Vector<Future<Content>>> fu = launchers.submit(new Callable<Vector<Future<Content>>>() {
@Override
public Vector<Future<Content>> call() {
// Scan the given ProjectThing for 3D-viewable items
// So: find arealist, pipe, ball, and profile_list types
final HashSet<ProjectThing> hs = pt.findBasicTypeChildren();
if (null == hs || 0 == hs.size()) {
Utils.logAll("Node " + pt + " does not contain any 3D-displayable children");
return null;
}
// Remove profile if it lives under a profile_list
for (final Iterator<ProjectThing> it = hs.iterator(); it.hasNext(); ) {
final ProjectThing pt = it.next();
if (null != pt.getObject() && pt.getObject().getClass() == Profile.class && pt.getParent().getType().equals("profile_list")) {
it.remove();
}
}
setWaitingCursor();
// Start new scheduler to publish/add meshes to the 3D Viewer every 5 seconds and when done.
final Hashtable<Display3D, Vector<Content>> contents = new Hashtable<Display3D, Vector<Content>>();
final ScheduledExecutorService updater = Executors.newScheduledThreadPool(1);
final AtomicInteger counter = new AtomicInteger();
updater.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
// Obtain a copy of the contents queue
final HashMap<Display3D, Vector<Content>> m = new HashMap<Display3D, Vector<Content>>();
synchronized (contents) {
m.putAll(contents);
contents.clear();
}
if (m.isEmpty())
return;
// Add all to the corresponding Display3D
for (final Map.Entry<Display3D, Vector<Content>> e : m.entrySet()) {
e.getKey().universe.addContentLater(e.getValue());
counter.getAndAdd(e.getValue().size());
}
Utils.showStatus(new StringBuilder("Rendered ").append(counter.get()).append('/').append(hs.size()).toString());
}
}, 100, 4000, TimeUnit.MILLISECONDS);
// A list of all generated Content objects
final Vector<Future<Content>> list = new Vector<Future<Content>>();
for (final Iterator<ProjectThing> it = hs.iterator(); it.hasNext(); ) {
// obtain the Displayable object under the node
final ProjectThing child = it.next();
final Object obc = child.getObject();
final Displayable displ = obc.getClass().equals(String.class) ? null : (Displayable) obc;
if (null != displ) {
if (displ.getClass().equals(Profile.class)) {
// handled by profile_list Thing
continue;
}
if (!displ.isVisible()) {
Utils.log("Skipping non-visible node " + displ);
continue;
}
}
// obtain the containing LayerSet
final Display3D d3d;
if (null != displ)
d3d = Display3D.get(displ.getLayerSet());
else if (child.getType().equals("profile_list")) {
final ArrayList<ProjectThing> al_children = child.getChildren();
if (null == al_children || 0 == al_children.size())
continue;
// else, get the first Profile and get its LayerSet
d3d = Display3D.get(((Displayable) ((ProjectThing) al_children.get(0)).getObject()).getLayerSet());
} else {
Utils.log("Don't know what to do with node " + child);
d3d = null;
}
if (null == d3d) {
Utils.log("Could not get a proper 3D display for node " + displ);
// java3D not installed most likely
return null;
}
boolean already;
synchronized (d3d.ht_pt_meshes) {
already = d3d.ht_pt_meshes.containsKey(child);
}
if (already) {
if (child.getObject() instanceof ZDisplayable) {
Utils.log("Updating 3D view of " + child.getObject());
} else {
Utils.log("Updating 3D view of " + child);
}
}
list.add(d3d.executors.submit(new Callable<Content>() {
@Override
public Content call() {
Content c = null;
try {
c = d3d.createMesh(child, displ, resample).call();
Vector<Content> vc;
synchronized (contents) {
vc = contents.get(d3d);
if (null == vc)
vc = new Vector<Content>();
contents.put(d3d, vc);
}
vc.add(c);
} catch (final Exception e) {
IJError.print(e);
}
return c;
}
}));
// If it's the last one:
if (!it.hasNext()) {
// Add the concluding task, that waits on all and shuts down the scheduler
d3d.executors.submit(new Runnable() {
@Override
public void run() {
// Wait until all are done
for (final Future<Content> c : list) {
try {
c.get();
} catch (final Throwable t) {
IJError.print(t);
}
}
try {
// Shutdown scheduler and execute remaining tasks
for (final Runnable r : updater.shutdownNow()) {
r.run();
}
} catch (final Throwable e) {
IJError.print(e);
}
// Reset cursor
doneWaiting();
Utils.showStatus(new StringBuilder("Done rendering ").append(counter.get()).append('/').append(hs.size()).toString());
}
});
}
}
return list;
}
});
if (wait && -1 != resample) {
try {
fu.get();
} catch (final Throwable t) {
IJError.print(t);
}
}
return fu;
}
use of ini.trakem2.tree.ProjectThing in project TrakEM2 by trakem2.
the class Display3D method removeFrom3D.
/**
* Remove all basic type children contained in {@code pt} and its children, recursively.
*
* @param pt
*/
public static void removeFrom3D(final ProjectThing pt) {
final HashSet<ProjectThing> hs = pt.findBasicTypeChildren();
if (null == hs || 0 == hs.size()) {
Utils.logAll("Nothing to remove from 3D.");
return;
}
// Ignore Profile instances ("profile_list" takes care of them)
for (final ProjectThing child : hs) {
if (child.getByType().equals("profile"))
continue;
// Find the LayerSet
LayerSet lset = null;
if (child.getType().equals("profile_list")) {
if (!child.hasChildren())
continue;
for (final ProjectThing p : child.getChildren()) {
if (null != p.getObject() && p.getObject() instanceof Profile) {
lset = ((Displayable) p.getObject()).getLayerSet();
break;
}
}
if (null == lset)
continue;
} else if (child.getType().equals("profile")) {
// Taken care of by "profile list"
continue;
} else {
final Displayable d = (Displayable) child.getObject();
if (null == d) {
Utils.log("Null object for ProjectThing " + child);
continue;
}
lset = d.getLayerSet();
}
if (null == lset) {
Utils.log("No LayerSet found for " + child);
continue;
}
final Display3D d3d = getDisplay(lset);
if (null == d3d) {
Utils.log("No Display 3D found for " + child);
// no Display3D window open
continue;
}
final String oldTitle = d3d.ht_pt_meshes.remove(child);
if (null == oldTitle) {
Utils.log("Could not find a title for " + child);
continue;
}
Utils.log("Removed from 3D view: " + oldTitle);
d3d.getUniverse().removeContent(oldTitle);
}
}
use of ini.trakem2.tree.ProjectThing in project TrakEM2 by trakem2.
the class ImageJCommandListener method commandExecuting.
// I know, I could create a hashtable and then map methods of this class to each command key ... this is just easier, and performance-wise nobody cares
// Or even a hastable with String command keys and then a number as value, and use a gigantic switch block. So much pain to write. WHAT I REALLY WANT is a switch that takes a String and is fast because it has its own hash setup.
public String commandExecuting(final String command) {
// Utils.log2("Command: " + command);
// 1 - check source
ImagePlus current = WindowManager.getCurrentImage();
// not a trakem2 display: continue happily
if (!(current instanceof FakeImagePlus))
return command;
// 2 - identify project
final FakeImagePlus fimp = (FakeImagePlus) current;
final Display display = fimp.getDisplay();
final LayerSet layer_set = display.getLayer().getParent();
final Project project = display.getProject();
final ProjectTree ptree = project.getProjectTree();
final Displayable active = display.getActive();
final Selection selection = display.getSelection();
// FILE menu
if (command.equals("Save")) {
project.save();
return null;
} else // EDIT menu
if (command.equals("Undo")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Cut")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Copy")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Copy to System")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Paste")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Clear")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Clear Outside")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Fill")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Draw")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Invert")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else // EDIT - SELECTION menu
if (command.equals("Select All")) {
if (ProjectToolbar.SELECT == Toolbar.getToolId()) {
selection.selectAll();
return null;
}
return command;
} else if (command.equals("Select None")) {
if (ProjectToolbar.SELECT == Toolbar.getToolId()) {
display.select(null);
return null;
}
return command;
} else if (command.equals("Restore Selection")) {
if (ProjectToolbar.SELECT == Toolbar.getToolId()) {
selection.restore();
return null;
}
return command;
} else // IMAGE - TYPE menu
if (command.equals("8-bit")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("16-bit")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("32-bit")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("8-bit Color")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("RGB Color")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("RGB Stack") || command.equals("HSB Stack")) {
Utils.showMessage("Can't convert to " + command);
return null;
} else // IMAGE - ADJUST menu
if (command.equals("Brightness/Contrast...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Window/Level...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Color Balance...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Threshold...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Size...")) {
if (null != active)
selection.specify();
return null;
} else if (command.equals("Canvas Size...")) {
display.resizeCanvas();
return null;
} else // IMAGE menu
if (command.equals("Show Info...")) {
// TODO perhaps it should show only for images ...
if (null == active) {
ptree.showInfo(project.getRootProjectThing());
} else {
ProjectThing pt = project.findProjectThing(active);
if (null != pt)
ptree.showInfo(pt);
}
return null;
} else // IMAGE - COLOR menu
if (in(command, new String[] { "RGB Split", "RGB Merge...", "Stack to RGB", "Make Composite" })) {
notAvailable(command);
return null;
} else if (command.equals("Show LUT")) {
return setTempCurrentImage(command, active);
} else if (command.equals("Edit LUT...")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("Average Color")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("RGB to CIELAB")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else if (command.equals("RGB to Luminance")) {
// TODO forward to the active image, if any
niy(command);
return null;
} else // IMAGE STACK menu
if (in(command, new String[] { "Add Slice", "Delete Slice" })) {
Utils.showMessage("Go to the Layer Tree and right-click to add/delete a layer.");
return null;
} else if (command.equals("Next Slice [>]")) {
display.nextLayer(IJ.shiftKeyDown() ? Event.SHIFT_MASK : 0);
return null;
} else if (command.equals("Previous Slice [<]")) {
display.previousLayer(IJ.shiftKeyDown() ? Event.SHIFT_MASK : 0);
return null;
} else if (in(command, new String[] { "Set Slice", "Images to Stack", "Stack to Images", "Make Montage..." })) {
notAvailable(command);
return null;
} else if (command.equals("Reslice [/]...")) {
// TODO
niy(command);
return null;
} else if (command.equals("Z Project...")) {
// TODO
niy(command);
return null;
} else if (command.equals("3D Project...")) {
// TODO
niy(command);
return null;
} else if (command.equals("Plot Z-axis Profile")) {
// TODO
niy(command);
return null;
} else if (command.equals("Start Animation [\\]")) {
// TODO
niy(command);
return null;
} else if (command.equals("Stop Animation")) {
// TODO
niy(command);
return null;
} else // IMAGE menu again
if (command.equals("Crop")) {
notAvailable(command);
return null;
} else if (in(command, new String[] { "Translate...", "Scale..." })) {
if (null != active)
selection.specify();
return null;
} else if (command.equals("Duplicate...")) {
if (null != active && active.getClass().equals(Patch.class)) {
// TODO stacks?
// 2.5 security factor: for awt in non-1.6.0 machines
project.getLoader().releaseToFit((long) (project.getLoader().estimateImageFileSize((Patch) active, 0) * 2.5));
new ImagePlus(active.getTitle(), ((Patch) active).getImageProcessor().duplicate()).show();
}
return null;
} else if (command.equals("Rename...")) {
if (null != active) {
active.adjustProperties();
Display.updateSelection();
}
return null;
} else // IMAGE ROTATE menu
if (command.equals("Flip Horizontally")) {
selection.apply(2, new double[] { -1, 1 });
return null;
} else if (command.equals("Flip Vertically")) {
selection.apply(2, new double[] { 1, -1 });
return null;
} else if (command.equals("Rotate 90 Degrees Right")) {
selection.apply(1, new double[] { 90 });
return null;
} else if (command.equals("Rotate 90 Degrees Left")) {
selection.apply(1, new double[] { -90 });
return null;
} else if (command.equals("Arbitrarily...")) {
if (null != active)
selection.specify();
return null;
} else // IMAGE ZOOM menu
if (command.equals("To Selection")) {
Roi roi = fimp.getRoi();
if (null != roi) {
Rectangle b = roi.getBounds();
b.x -= b.width / 2;
b.y -= b.height / 2;
b.width *= 2;
b.height *= 2;
display.getCanvas().showCentered(b);
}
return null;
} else if (command.equals("View 100%")) {
// TODO
niy(command);
return null;
} else // ANALYZE menu
if (command.equals("Measure")) {
// Minimal measurement: area of closed ROIs, length of unclosed ROIs, calibrated.
Roi roi = fimp.getRoi();
if (null != roi) {
Calibration cal = fimp.getCalibration();
AffineTransform caff = new AffineTransform();
caff.scale(cal.pixelWidth, cal.pixelHeight);
if (M.isAreaROI(roi)) {
Area area = M.getArea(roi);
area = area.createTransformedArea(caff);
ResultsTable rt = Utils.createResultsTable("ROI area", new String[] { "area", "perimeter" });
rt.incrementCounter();
rt.addLabel("units", cal.getUnit());
rt.addValue(0, Math.abs(AreaCalculations.area(area.getPathIterator(null))));
rt.addValue(1, roi.getLength());
rt.show("ROI area");
} else {
ResultsTable rt = Utils.createResultsTable("ROI length", new String[] { "length" });
rt.incrementCounter();
rt.addLabel("units", cal.getUnit());
rt.addValue(0, roi.getLength());
rt.show("ROI length");
}
return null;
} else if (null != active) {
// Measure the active displayable
if (active.getClass() == Patch.class) {
// measure like 'm' would in ImageJ for an image
ImagePlus imp = ((Patch) active).getImagePlus();
imp.setCalibration(active.getLayer().getParent().getCalibrationCopy());
IJ.run(imp, "Measure", "");
} else {
// Call measure like ProjectThing does
ProjectThing pt = active.getProject().findProjectThing(active);
if (active instanceof Profile)
((ProjectThing) pt.getParent()).measure();
else
pt.measure();
}
return null;
}
Utils.log("Draw a ROI or select an object!");
return null;
} else if (in(command, new String[] { "Analyze Particles...", "Histogram", "Plot Profile", "Surface Plot...", "Color Inspector 3D", "3D Surface Plot", "Color Histogram" })) {
return setTempCurrentImage(command, active);
} else if (command.equals("Label")) {
notAvailable(command);
return null;
} else // PLUGINS menu
if (command.equals("Volume Viewer")) {
return runOnVirtualLayerSet(command, layer_set, display);
} else if (command.equals("3D Viewer")) {
// it's virtual and non-caching, will appear as a regular ImageJ stack
layer_set.createLayerStack(Displayable.class, ImagePlus.COLOR_RGB, display.getDisplayChannelAlphas()).getImagePlus().show();
return command;
} else // PROCESS menu and submenus
if (in(command, new String[] { "FFT", "Fast FFT (2D/3D)" })) {
return setTempCurrentImage(command, active);
} else if (in(command, new String[] { "Bandpass Filter...", "Custom Filter...", "FD Math...", "Swap Quadrants", "Convolve...", "Gaussian Blur...", "Median...", "Mean...", "Minimum...", "Maximum...", "Unsharp Mask...", "Variance...", "Show Circular Masks...", "Subtract Background..." })) {
return duplicate(command, active);
} else if (in(command, new String[] { "Smooth", "Sharpen", "Find Edges", "Enhance Contrast", "Add Noise", "Add Specified Noise...", "Salt and Pepper", "Despeckle", "Remove Outliers...", "North", "Northeast", "East", "Southeast", "South", "Southwest", "West", "Northwest", "Make Binary", "Convert to Mask", "Find Maxima...", "Erode", "Dilate", "Open ", "Close-", "Outline", "Fill Holes", "Skeletonize", "Distance Map", "Ultimate Points", "Watershed", "Add...", "Subtract...", "Multiply...", "Divide...", "AND...", "OR...", "XOR...", "Min...", "Max...", "Gamma...", "Log", "Exp", "Square", "Square Root", "Reciprocal", "NaN Background", "Abs" })) {
return duplicate(command, active);
}
/*else {
// continue happily
//Utils.log2("Skipping " + command);
}*/
// If it's part of "Save As", ignore it
Menu menu = Menus.getSaveAsMenu();
for (int i = menu.getItemCount() - 1; i > -1; i--) {
if (command.equals(menu.getItem(i).getActionCommand())) {
notAvailable(command);
return null;
}
}
// Give it back to ImageJ
return command;
}
use of ini.trakem2.tree.ProjectThing in project TrakEM2 by trakem2.
the class Profile method generateTriangles.
/**
* Takes a profile_list, scans for its Profile children, makes sublists of continuous profiles (if they happen to be branched), and then creates triangles for them using weighted vector strings.
*/
public static List<Point3f> generateTriangles(final ProjectThing pt, final double scale) {
if (!pt.getType().equals("profile_list")) {
Utils.log2("Profile: ignoring unhandable ProjectThing type.");
return null;
}
// should be sorted by Z already
final ArrayList<ProjectThing> al = pt.getChildren();
if (al.size() < 2) {
Utils.log("profile_list " + pt + " has less than two profiles: can't render in 3D.");
return null;
}
// collect all Profile
final HashSet<Profile> hs = new HashSet<Profile>();
for (final ProjectThing child : al) {
final Object ob = child.getObject();
if (ob instanceof Profile) {
hs.add((Profile) ob);
} else {
Utils.log2("Render: skipping non Profile class child");
}
}
// Create sublists of profiles, following the chain of links.
final Profile[] p = new Profile[hs.size()];
hs.toArray(p);
// find if at least one is visible
boolean hidden = true;
for (int i = 0; i < p.length; i++) {
if (p[i].visible) {
hidden = false;
break;
}
if (null == p[i] || 0 == p[i].n_points) {
Utils.log("Cannot generate triangle mesh: empty profile " + p[i] + (null != p[i] ? " at layer " + p[i].getLayer() : ""));
return null;
}
}
if (hidden)
return null;
// collect starts and ends
final HashSet<Profile> hs_bases = new HashSet<Profile>();
final HashSet<Profile> hs_done = new HashSet<Profile>();
final List<Point3f> triangles = new ArrayList<Point3f>();
do {
Profile base = null;
// choose among existing bases
if (hs_bases.size() > 0) {
base = hs_bases.iterator().next();
} else {
// find a new base, simply by taking the lowest Z or remaining profiles
double min_z = Double.MAX_VALUE;
for (int i = 0; i < p.length; i++) {
if (hs_done.contains(p[i]))
continue;
final double z = p[i].getLayer().getZ();
if (z < min_z) {
min_z = z;
base = p[i];
}
}
// add base
if (null != base)
hs_bases.add(base);
}
if (null == base) {
Utils.log2("No more bases.");
break;
}
// crawl list to get a sequence of profiles in increasing or decreasing Z order, but not mixed z trends
final ArrayList<Profile> al_profiles = new ArrayList<Profile>();
// Utils.log2("Calling accumulate for base " + base);
al_profiles.add(base);
final Profile last = accumulate(hs_done, al_profiles, base, 0);
// if the trend was not empty, add it
if (last != base) {
// count as done
hs_done.addAll(al_profiles);
// add new possible base (which may have only 2 links if it was from a broken Z trend)
hs_bases.add(last);
// create 3D object from base to base
final Profile[] profiles = new Profile[al_profiles.size()];
al_profiles.toArray(profiles);
final List<Point3f> tri = makeTriangles(profiles, scale);
if (null != tri)
triangles.addAll(tri);
} else {
// remove base
hs_bases.remove(base);
}
} while (0 != hs_bases.size());
return triangles;
}
Aggregations