From e20a780de75e951855bdc1431b4688ce345d6a25 Mon Sep 17 00:00:00 2001 From: aherbert Date: Mon, 11 Jun 2018 17:26:25 +0100 Subject: [PATCH 01/15] Disable sync when closing a window --- src/main/java/ij3d/Image3DUniverse.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ij3d/Image3DUniverse.java b/src/main/java/ij3d/Image3DUniverse.java index 570b32f..fbc8838 100644 --- a/src/main/java/ij3d/Image3DUniverse.java +++ b/src/main/java/ij3d/Image3DUniverse.java @@ -333,6 +333,7 @@ public boolean isFullScreen() { */ @Override public void cleanup() { + sync(false); timeline.pause(); removeAllContents(); contents.clear(); From 36b9d0d7d3b5e1059e621fdf9ee204c482d481cb Mon Sep 17 00:00:00 2001 From: aherbert Date: Mon, 11 Jun 2018 17:31:34 +0100 Subject: [PATCH 02/15] Do not call removeUniverse(u) in universeClosed() --- src/main/java/ij3d/UniverseSynchronizer.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/ij3d/UniverseSynchronizer.java b/src/main/java/ij3d/UniverseSynchronizer.java index 3c1d55a..cbb89f5 100644 --- a/src/main/java/ij3d/UniverseSynchronizer.java +++ b/src/main/java/ij3d/UniverseSynchronizer.java @@ -47,7 +47,13 @@ public void transformationUpdated(final View view) { @Override public void universeClosed() { - removeUniverse(u); + //removeUniverse(u); + // Do not call removeUniverse(u) + // as this will remove this adapter from the list of universe listeners + // (i.e. u.removeUniverseListener(l)) + // so modifying the current list of listeners on which universeClosed() + // is currently being invoked by the closing universe. + universes.remove(u); } }; u.addUniverseListener(l); From b9676e7f15d016b3769027aac31723dfe5a96e4b Mon Sep 17 00:00:00 2001 From: aherbert Date: Mon, 11 Jun 2018 17:35:15 +0100 Subject: [PATCH 03/15] Use instanceof when writing CustomMesh This allows sub-classes of the CustomMesh to be exported --- src/main/java/customnode/WavefrontExporter.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/customnode/WavefrontExporter.java b/src/main/java/customnode/WavefrontExporter.java index 317f5d1..ad52a84 100644 --- a/src/main/java/customnode/WavefrontExporter.java +++ b/src/main/java/customnode/WavefrontExporter.java @@ -160,13 +160,13 @@ public static void save(final Map meshes, objWriter.write(mat.name); objWriter.write('\n'); // print faces - if (cmesh.getClass() == CustomTriangleMesh.class) writeTriangleFaces( - index, objWriter, name); - else if (cmesh.getClass() == CustomQuadMesh.class) writeQuadFaces(index, + if (cmesh instanceof CustomTriangleMesh) + writeTriangleFaces(index, objWriter, name); + else if (cmesh instanceof CustomQuadMesh) writeQuadFaces(index, objWriter, name); - else if (cmesh.getClass() == CustomPointMesh.class) writePointFaces( + else if (cmesh instanceof CustomPointMesh) writePointFaces( index, objWriter, name); - else if (cmesh.getClass() == CustomLineMesh.class) { + else if (cmesh instanceof CustomLineMesh) { final CustomLineMesh clm = (CustomLineMesh) cmesh; switch (clm.getMode()) { case CustomLineMesh.PAIRWISE: From c781fe6b4a460739613e351032e23434a364e0de Mon Sep 17 00:00:00 2001 From: aherbert Date: Mon, 11 Jun 2018 17:43:51 +0100 Subject: [PATCH 04/15] Removed duplicate setting of a flag --- src/main/java/customnode/STLLoader.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/customnode/STLLoader.java b/src/main/java/customnode/STLLoader.java index eebc7e2..4735606 100644 --- a/src/main/java/customnode/STLLoader.java +++ b/src/main/java/customnode/STLLoader.java @@ -209,7 +209,6 @@ private CustomMesh createCustomMesh() { cm = new CustomTriangleMesh(vertices); cm.loadedFromName = name; cm.changed = false; - cm.changed = false; return cm; } From 93a6cefe279d1756369af837e4eaaac8a6047a07 Mon Sep 17 00:00:00 2001 From: aherbert Date: Mon, 11 Jun 2018 17:50:10 +0100 Subject: [PATCH 05/15] Avoid NullPointerException --- src/main/java/ij3d/shortcuts/ShortCuts.java | 62 ++++++++++++++------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/src/main/java/ij3d/shortcuts/ShortCuts.java b/src/main/java/ij3d/shortcuts/ShortCuts.java index b8f48d6..bce9ff6 100644 --- a/src/main/java/ij3d/shortcuts/ShortCuts.java +++ b/src/main/java/ij3d/shortcuts/ShortCuts.java @@ -33,13 +33,15 @@ import ij3d.UniverseSettings; -public class ShortCuts { +public class ShortCuts +{ private final List commands; private final HashMap items; private final HashMap shortcuts; - public ShortCuts(final JMenuBar menubar) { + public ShortCuts(final JMenuBar menubar) + { commands = new ArrayList(); items = new HashMap(); shortcuts = UniverseSettings.shortcuts; @@ -47,60 +49,80 @@ public ShortCuts(final JMenuBar menubar) { for (int i = 0; i < menubar.getMenuCount(); i++) scan(menubar.getMenu(i), ""); - for (final String command : commands) { + for (final String command : commands) + { final String shortcut = shortcuts.get(command); - if (shortcut != null) setShortCut(command, shortcut); + if (shortcut != null) + setShortCut(command, shortcut); } } - public void save() { + public void save() + { UniverseSettings.save(); } - public void reload() { + public void reload() + { UniverseSettings.load(); } - public Iterable getCommands() { + public Iterable getCommands() + { return commands; } - public String getShortCut(final String command) { + public String getShortCut(final String command) + { return shortcuts.get(command); } - public void setShortCut(final String command, final String shortcut) { - if (shortcut.trim().length() == 0) { + public void setShortCut(final String command, final String shortcut) + { + if (shortcut.trim().length() == 0) + { clearShortCut(command); return; } shortcuts.put(command, shortcut); final KeyStroke stroke = KeyStroke.getKeyStroke(shortcut); - items.get(command).setAccelerator(stroke); + JMenuItem o = items.get(command); + if (o != null) + o.setAccelerator(stroke); } - public void clearShortCut(final String command) { - items.get(command).setAccelerator(null); + public void clearShortCut(final String command) + { + JMenuItem o = items.get(command); + if (o != null) + o.setAccelerator(null); shortcuts.remove(command); } - public int getNumberOfCommands() { + public int getNumberOfCommands() + { return commands.size(); } - public String getCommand(final int i) { + public String getCommand(final int i) + { return commands.get(i); } - private void scan(final JMenu menu, String prefix) { + private void scan(final JMenu menu, String prefix) + { prefix += menu.getText() + " > "; - for (int i = 0; i < menu.getItemCount(); i++) { + for (int i = 0; i < menu.getItemCount(); i++) + { final JMenuItem mi = menu.getItem(i); - if (mi == null) continue; - if (mi instanceof JMenu) { + if (mi == null) + continue; + if (mi instanceof JMenu) + { scan((JMenu) mi, prefix); } - else { + else + { final String c = prefix + mi.getText(); commands.add(c); items.put(c, mi); From 20de3a39d91d78b9666bc95060989d3d77020072 Mon Sep 17 00:00:00 2001 From: aherbert Date: Mon, 11 Jun 2018 17:53:16 +0100 Subject: [PATCH 06/15] Check correct HashMap for existing timepoint --- src/main/java/ij3d/Content.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ij3d/Content.java b/src/main/java/ij3d/Content.java index 0b8d10a..6d16615 100644 --- a/src/main/java/ij3d/Content.java +++ b/src/main/java/ij3d/Content.java @@ -122,7 +122,7 @@ public Content(final String name, public void addInstant(final ContentInstant ci) { final int timepoint = ci.timepoint; contents.put(timepoint, ci); - if (!contents.containsKey(timepoint)) { + if (!timepointToSwitchIndex.containsKey(timepoint)) { timepointToSwitchIndex.put(timepoint, contentSwitch.numChildren()); contentSwitch.addChild(ci); } From 8640e35aa0c3fff15401eb8009f9eaa219fa68a0 Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 12 Jun 2018 14:21:54 +0100 Subject: [PATCH 07/15] Fixed the reset rotation view methods The methods previously ignored the default orientation of y-downwards. Thus they could not be used to reset the view to match the default view returned by reset view command. --- src/main/java/ij3d/Image3DUniverse.java | 82 ++++++++++++++----------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/src/main/java/ij3d/Image3DUniverse.java b/src/main/java/ij3d/Image3DUniverse.java index fbc8838..f0ccf9e 100644 --- a/src/main/java/ij3d/Image3DUniverse.java +++ b/src/main/java/ij3d/Image3DUniverse.java @@ -648,8 +648,8 @@ public void recalculateGlobalMinMax() { final Point3d min = new Point3d(); final Point3d max = new Point3d(); - final Iterator it = contents(); - Content c = (Content) it.next(); + final Iterator it = contents(); + Content c = it.next(); c.getMin(min); c.getMax(max); globalMin.set(min); @@ -1425,7 +1425,7 @@ public void removeContent(final String name) { * universe. */ @Override - public Iterator contents() { + public Iterator contents() { return contents.values().iterator(); } @@ -1435,7 +1435,7 @@ public Iterator contents() { * * @return */ - public Collection getContents() { + public Collection getContents() { if (contents == null) return null; return contents.values(); } @@ -1496,7 +1496,7 @@ public void loadView(final String file) throws IOException { public void resetView() { fireTransformationStarted(); - // rotate so that y shows downwards + // rotate so that y shows downwards, z-axis away from the viewer final Transform3D t = new Transform3D(); final AxisAngle4d aa = new AxisAngle4d(1, 0, 0, Math.PI); t.set(aa); @@ -1522,72 +1522,84 @@ public void resetView() { * @param angle The angle in radians. */ public void rotateUniverse(final Vector3d axis, final double angle) { + fireTransformationStarted(); viewTransformer.rotate(axis, angle); + fireTransformationUpdated(); + fireTransformationFinished(); } /** - * Rotate the univere so that the user looks in the negative direction of the - * z-axis. + * Resets the rotation transform. This is used to provide specific view orientations. + * + * @param aa2 the new rotation transform (applied to the reset rotation) */ - public void rotateToNegativeXY() { + private void resetRotationTransform(AxisAngle4d aa2) { fireTransformationStarted(); - getRotationTG().setTransform(new Transform3D()); + // rotate so that y shows downwards, z-axis away from the viewer + // (i.e. reset the rotation). This is equivalent to rotateToPositiveXY. + final Transform3D t = new Transform3D(); + final AxisAngle4d aa = new AxisAngle4d(1, 0, 0, Math.PI); + t.set(aa); + if (aa2 != null) + { + final Transform3D t2 = new Transform3D(); + t2.set(aa2); + t.mul(t2); + } + getRotationTG().setTransform(t); + // TODO - The bounding box and coordinate system are not refreshed. + fireTransformationUpdated(); fireTransformationFinished(); } - + + // The methods below rotate the default view to look along a given axis + /** - * Rotate the univere so that the user looks in the positive direction of the + * Rotate the universe so that the user looks in the negative direction of the * z-axis. */ + public void rotateToNegativeXY() { + resetRotationTransform(new AxisAngle4d(0, 1, 0, Math.PI)); + } + + /** + * Rotate the universe so that the user looks in the positive direction of the + * z-axis. This is the default view. + */ public void rotateToPositiveXY() { - fireTransformationStarted(); - getRotationTG().setTransform(new Transform3D()); - waitForNextFrame(); - rotateUniverse(new Vector3d(0, 1, 0), Math.PI); + resetRotationTransform(null); } /** - * Rotate the univere so that the user looks in the negative direction of the + * Rotate the universe so that the user looks in the negative direction of the * y-axis. */ public void rotateToNegativeXZ() { - fireTransformationStarted(); - getRotationTG().setTransform(new Transform3D()); - waitForNextFrame(); - rotateUniverse(new Vector3d(1, 0, 0), Math.PI / 2); + resetRotationTransform(new AxisAngle4d(1, 0, 0, Math.PI / 2)); } /** - * Rotate the univere so that the user looks in the positive direction of the + * Rotate the universe so that the user looks in the positive direction of the * y-axis. */ public void rotateToPositiveXZ() { - fireTransformationStarted(); - getRotationTG().setTransform(new Transform3D()); - waitForNextFrame(); - rotateUniverse(new Vector3d(0, 1, 0), -Math.PI / 2); + resetRotationTransform(new AxisAngle4d(1, 0, 0, -Math.PI / 2)); } /** - * Rotate the univere so that the user looks in the negative direction of the + * Rotate the universe so that the user looks in the negative direction of the * x-axis. */ public void rotateToNegativeYZ() { - fireTransformationStarted(); - getRotationTG().setTransform(new Transform3D()); - waitForNextFrame(); - rotateUniverse(new Vector3d(0, 1, 0), Math.PI / 2); + resetRotationTransform(new AxisAngle4d(0, 1, 0, Math.PI / 2)); } /** - * Rotate the univere so that the user looks in the positive direction of the + * Rotate the universe so that the user looks in the positive direction of the * x-axis. */ public void rotateToPositiveYZ() { - fireTransformationStarted(); - getRotationTG().setTransform(new Transform3D()); - waitForNextFrame(); - rotateUniverse(new Vector3d(1, 0, 0), -Math.PI / 2); + resetRotationTransform(new AxisAngle4d(0, 1, 0, -Math.PI / 2)); } /** From e4b35b79bbd22e70bb3b04e65ed19b128ccdadd2 Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 12 Jun 2018 14:22:28 +0100 Subject: [PATCH 08/15] Added keyboard bindings for rotation around z-axis --- src/main/java/ij3d/behaviors/InteractiveBehavior.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/ij3d/behaviors/InteractiveBehavior.java b/src/main/java/ij3d/behaviors/InteractiveBehavior.java index 319caef..dfe3042 100644 --- a/src/main/java/ij3d/behaviors/InteractiveBehavior.java +++ b/src/main/java/ij3d/behaviors/InteractiveBehavior.java @@ -323,7 +323,12 @@ else if (c != null && c.getType() == ContentConstants.ORTHO && axis != -1) case KeyEvent.VK_PAGE_DOWN: viewTransformer.zoom(-1); return; - + case KeyEvent.VK_COMMA: + viewTransformer.rotateZ(TWO_RAD); + return; + case KeyEvent.VK_PERIOD: + viewTransformer.rotateZ(-TWO_RAD); + return; } } // If we arrive here, the event was not handled. From 05b36db59a1f79be0bb5afbad337b87164c2dea9 Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 12 Jun 2018 14:40:51 +0100 Subject: [PATCH 09/15] Use OpenDialog.getDefaultDirectory() when opening This will reset to the same directory as that used for save since the SaveDialog is called with the default name and the directory is left to be the default directory. --- src/main/java/ij3d/Executer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ij3d/Executer.java b/src/main/java/ij3d/Executer.java index 4cec5e1..8209c72 100644 --- a/src/main/java/ij3d/Executer.java +++ b/src/main/java/ij3d/Executer.java @@ -359,7 +359,7 @@ public void saveAsU3D() { } public void loadView() { - final OpenDialog sd = new OpenDialog("Open view...", "", ".view"); + final OpenDialog sd = new OpenDialog("Open view...", OpenDialog.getDefaultDirectory(), ".view"); final String dir = sd.getDirectory(); final String name = sd.getFileName(); if (dir == null || name == null) return; @@ -386,7 +386,7 @@ public void saveView() { public void loadSession() { final OpenDialog sd = - new OpenDialog("Open session...", "session", ".scene"); + new OpenDialog("Open session...", OpenDialog.getDefaultDirectory(), ".scene"); final String dir = sd.getDirectory(); final String name = sd.getFileName(); if (dir == null || name == null) return; From 5438d7d5f07c10aca07219cd88ba069169b2613f Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 12 Jun 2018 14:51:13 +0100 Subject: [PATCH 10/15] Remove redundant call to show pop-up Showing the pop-up on mouse press and release is redundant. Just do it on mouse released. --- src/main/java/ij3d/Image3DUniverse.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/ij3d/Image3DUniverse.java b/src/main/java/ij3d/Image3DUniverse.java index f0ccf9e..cf6b0e5 100644 --- a/src/main/java/ij3d/Image3DUniverse.java +++ b/src/main/java/ij3d/Image3DUniverse.java @@ -210,11 +210,16 @@ public void mouseClicked(final MouseEvent e) { @Override public void mousePressed(final MouseEvent e) { - contextmenu.showPopup(e); + // Remove redundant call to show pop-up. + // Pop-up is shown on mouse release allowing the mouse + // to be moved over content and the menu will be + // for the correct selected content + //contextmenu.showPopup(e); } @Override public void mouseReleased(final MouseEvent e) { + if (e.isConsumed()) return; contextmenu.showPopup(e); } }); From 30e7d047a3b42c94d517dba258412d4d4499a468 Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 12 Jun 2018 14:55:52 +0100 Subject: [PATCH 11/15] Added a getter for the ContextMenu --- src/main/java/ij3d/Image3DUniverse.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/ij3d/Image3DUniverse.java b/src/main/java/ij3d/Image3DUniverse.java index cf6b0e5..3093591 100644 --- a/src/main/java/ij3d/Image3DUniverse.java +++ b/src/main/java/ij3d/Image3DUniverse.java @@ -129,6 +129,7 @@ public class Image3DUniverse extends DefaultAnimatableUniverse { /** A reference to the universe's shortcuts */ private ShortCuts shortcuts; + /** A reference to the universe's context menu */ private ContextMenu contextmenu; /** @@ -450,6 +451,13 @@ public ShortCuts getShortcuts() { return shortcuts; } + /** + * Returns a reference to the universe's context menu. + */ + public ContextMenu getContextmenu() { + return contextmenu; + } + /** * Returns a reference to the PointListDialog used by this universe */ From b879b6974e2e935ad4131efba8f9b7135e954e1c Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 12 Jun 2018 15:13:10 +0100 Subject: [PATCH 12/15] Update the front-back clipping when synchronised windows are transformed --- src/main/java/ij3d/UniverseSynchronizer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ij3d/UniverseSynchronizer.java b/src/main/java/ij3d/UniverseSynchronizer.java index cbb89f5..8af235a 100644 --- a/src/main/java/ij3d/UniverseSynchronizer.java +++ b/src/main/java/ij3d/UniverseSynchronizer.java @@ -81,6 +81,7 @@ private static final void setGlobalTransform(final Image3DUniverse u, final int num = group.getNumTransforms(); for (int i = 0; i < num; i++) group.getTransformGroup(i).setTransform(transform.transforms[i]); + u.getViewPlatformTransformer().updateFrontBackClip(); u.fireTransformationUpdated(); } From 0ad0069043ae7072fe60c0bd5b87a16cf69606ff Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 12 Jun 2018 15:50:11 +0100 Subject: [PATCH 13/15] Detect accelerators in menu items if not in the current settings --- src/main/java/ij3d/shortcuts/ShortCuts.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/ij3d/shortcuts/ShortCuts.java b/src/main/java/ij3d/shortcuts/ShortCuts.java index bce9ff6..a6ec667 100644 --- a/src/main/java/ij3d/shortcuts/ShortCuts.java +++ b/src/main/java/ij3d/shortcuts/ShortCuts.java @@ -126,6 +126,17 @@ private void scan(final JMenu menu, String prefix) final String c = prefix + mi.getText(); commands.add(c); items.put(c, mi); + // Detect accelerators in menu items if not in the current settings. + // This is relevant when the standard menubar has been changed with + // extra items. + if (!shortcuts.containsKey(c)) + { + KeyStroke ks = mi.getAccelerator(); + if (ks != null) + { + shortcuts.put(c, ks.toString()); + } + } } } } From 8c96fb29bff1e2b6e286b4e3f449ce5e7c5d0742 Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 12 Jun 2018 16:11:11 +0100 Subject: [PATCH 14/15] Added method to refresh the shortcuts from the menubar. This is used when the menubar has been updated. --- src/main/java/ij3d/Image3DUniverse.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/ij3d/Image3DUniverse.java b/src/main/java/ij3d/Image3DUniverse.java index 3093591..fdaecdf 100644 --- a/src/main/java/ij3d/Image3DUniverse.java +++ b/src/main/java/ij3d/Image3DUniverse.java @@ -450,6 +450,16 @@ public Executer getExecuter() { public ShortCuts getShortcuts() { return shortcuts; } + + /** + * Refresh the shortcuts using the menubar. This should be called if the standard + * menubar for the ImageJ3DWindow has been modified. + * + * @see #getMenuBar() + */ + public void refreshShortcuts() { + shortcuts = new ShortCuts(menubar); + } /** * Returns a reference to the universe's context menu. From ec32c1abf5b0bc48bfdd22cac6211a3f48bc234f Mon Sep 17 00:00:00 2001 From: aherbert Date: Tue, 12 Jun 2018 17:33:29 +0100 Subject: [PATCH 15/15] Added Junit test for BoundingBox The BoundingBox could throw an exception when the content added to an image had identical min and max coordinates, e.g. a single point. This has required updating the POM to bring in the JOGL runtime for the test. This can be removed if the test is deemed trivial. --- pom.xml | 25 ++++++++++++++++++- src/main/java/ij3d/shapes/BoundingBox.java | 10 +++++++- .../java/ij3d/shapes/CoordinateSystem.java | 4 +++ .../java/ij3d/shapes/BoundingBoxTest.java | 18 +++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/test/java/ij3d/shapes/BoundingBoxTest.java diff --git a/pom.xml b/pom.xml index d78f323..91f8c3b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -97,6 +98,11 @@ https://imagej.net/User:Saalfeld axtimwalde + + Alex Herbert + https://imagej.net/User:Aherbert + aherbert + @@ -169,5 +175,22 @@ org.scijava vecmath + + + + junit + junit + test + + + org.jogamp.gluegen + gluegen-rt-main + test + + + org.jogamp.jogl + jogl-all-main + test + diff --git a/src/main/java/ij3d/shapes/BoundingBox.java b/src/main/java/ij3d/shapes/BoundingBox.java index 689f85e..ffa233d 100644 --- a/src/main/java/ij3d/shapes/BoundingBox.java +++ b/src/main/java/ij3d/shapes/BoundingBox.java @@ -89,6 +89,9 @@ public BoundingBox(final Point3f minp, final Point3f maxp, final Color3f color) final float ly = max.y - min.y; final float lz = max.z - min.z; final float max = Math.max(lx, Math.max(ly, lz)); + if (max <= 0) + // No bounding box + return; float min = Math.min(lx, Math.min(ly, lz)); if (min == 0 || max / min > 100) min = max; double tmp = 0.00001f; @@ -198,12 +201,17 @@ private Appearance createAppearance(final Color3f color) { private Geometry makeLine(final Point3f start, final Point3f end, final Color3f color, final float tickDistance, final float first, - final float tickSize, final boolean noTicks) + final float tickSize, boolean noTicks) { final float lineLength = start.distance(end); final int nTicks = (int) Math.floor((lineLength - first) / tickDistance) + 1; + // Avoid negative array size. This occurs when the (lineLength - first) + // is negative. + if (nTicks < 1) + noTicks = true; + final int n = noTicks ? 2 : nTicks * 6 + 2; final Point3f[] coords = new Point3f[n]; diff --git a/src/main/java/ij3d/shapes/CoordinateSystem.java b/src/main/java/ij3d/shapes/CoordinateSystem.java index fdbcf4c..bd4502b 100644 --- a/src/main/java/ij3d/shapes/CoordinateSystem.java +++ b/src/main/java/ij3d/shapes/CoordinateSystem.java @@ -49,6 +49,10 @@ public CoordinateSystem(final float length, final Color3f color) { this.length = length; this.color = color; setCapability(BranchGroup.ALLOW_DETACH); + + // No coordinate system + if (length <= 0) + return; final Shape3D lines = new Shape3D(); lines.setGeometry(createGeometry()); diff --git a/src/test/java/ij3d/shapes/BoundingBoxTest.java b/src/test/java/ij3d/shapes/BoundingBoxTest.java new file mode 100644 index 0000000..facf9c8 --- /dev/null +++ b/src/test/java/ij3d/shapes/BoundingBoxTest.java @@ -0,0 +1,18 @@ +package ij3d.shapes; + +import org.junit.Test; +import org.scijava.vecmath.Point3d; + +public class BoundingBoxTest +{ + @Test + public void canCreateWithNoRange() + { + // This used to cause a negative array size exception. + // It is relevant when adding content that is a single + // coordinate, e.g. a CustomPointMesh with 1 point. + double d = Double.parseDouble("-25.632442551701516"); + Point3d min = new Point3d(d, d, d); + new BoundingBox(min, min); + } +} \ No newline at end of file