diff --git a/.classpath b/.classpath index f7ad53eba4..725899b55b 100644 --- a/.classpath +++ b/.classpath @@ -1,367 +1,372 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore index 21ed2319f0..8ed7b69bbe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ /bin/ /export/ /null/ -classes \ No newline at end of file +classes +*.csv +*.pdf +*.png +/output/ diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java index bcca3ceb42..c082dfe85a 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VOI/VOILatticeManagerInterface.java @@ -23,6 +23,8 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.io.File; +import java.util.ArrayList; +import java.util.List; import java.util.Vector; import javax.swing.ButtonGroup; @@ -33,6 +35,8 @@ import javax.swing.JPanel; import javax.swing.JTextField; +import org.janelia.mipav.plugins.worm.untwisting.AccurateModeListener; + import WildMagic.LibFoundation.Distance.DistanceVector3Segment3; import WildMagic.LibFoundation.Mathematics.Segment3f; import WildMagic.LibFoundation.Mathematics.Vector3f; @@ -1092,39 +1096,78 @@ private void setVoxelSize() defaultVoxelSize = new JTextField( "" + VoxelSize ); defaultVoxelSize.addActionListener(this); JPanel panel = new JPanel( new GridLayout(1, 3) ); - panel.add( new JLabel( "Current Voxel Size" ) ); - panel.add( defaultVoxelSize ); - panel.add( new JLabel("um") ); + panel.add(new JLabel("Current Voxel Size")); + panel.add(defaultVoxelSize); + panel.add(new JLabel("um")); updateVoxelSize = new JDialog(); updateVoxelSize.getContentPane().setLayout(new BorderLayout()); - updateVoxelSize.setModalityType( JDialog.ModalityType.APPLICATION_MODAL); - updateVoxelSize.getContentPane().add( panel, BorderLayout.NORTH ); - updateVoxelSize.getContentPane().add( OK, BorderLayout.SOUTH ); + updateVoxelSize.setModalityType(JDialog.ModalityType.APPLICATION_MODAL); + updateVoxelSize.getContentPane().add(panel, BorderLayout.NORTH); + updateVoxelSize.getContentPane().add(OK, BorderLayout.SOUTH); updateVoxelSize.pack(); updateVoxelSize.setResizable(false); MipavUtil.centerOnScreen(updateVoxelSize); updateVoxelSize.setVisible(true); } - - public void mouseReleased(MouseEvent e) { + + // Handle event when mouse button is released + public void mouseReleased(MouseEvent e) { movingPickedPoint = false; - if(editingCrossSections) { + if (editingCrossSections) { latticeModel.showLattice(true); } - } + } + // Handle key press events if the key is pressed public void keyPressed(KeyEvent e) { isShiftSelected = e.isShiftDown(); } - + + // Maintains state for accurate mode and listeners for its changes + private boolean accurateMode = true; + // List of listeners that will be notified when accurate mode changes + // TODO: might want to change the array into set + private List listeners = new ArrayList<>(); + + // Check if the accurate mode is currently enabled + public boolean isAccurateMode() { + return accurateMode; + } + + // Toggle the accurate mode state and notify all listeners + public void toggleAccurateMode() { + accurateMode = !accurateMode; + setAccurateMode(accurateMode); + } + + // Set the accurate mode and notify listeners about the mode change + public void setAccurateMode(boolean accurateMode) { + this.accurateMode = accurateMode; + for (AccurateModeListener listener : listeners) { + listener.accurateModeChanged(accurateMode); + } + } + + // Add a new listener to be notified about accurate mode changes + public void addAccurateModeListener(AccurateModeListener listener) { + listeners.add(listener); + } + + // Handle key release events and perform actions based on the key released public void keyReleased(KeyEvent e) { isShiftSelected = e.isShiftDown(); movingPickedPoint = false; - if(editingCrossSections) { - switch(e.getKeyChar()) { + + // Specific actions based on key codes, e.g., toggle accurate mode on 'M' key + if (e.getKeyCode() == KeyEvent.VK_M) { + toggleAccurateMode(); + } + + if (editingCrossSections) { + switch (e.getKeyChar()) { case '+': latticeModel.decreaseCrossSectionSamples(); break; @@ -1155,7 +1198,7 @@ public void keyReleased(KeyEvent e) { case 'R': latticeModel.showLattice(false); latticeModel.resetCrossSections(); - if(editingCrossSections) { + if (editingCrossSections) { latticeModel.showLattice(true); } break; @@ -1168,46 +1211,40 @@ public void keyReleased(KeyEvent e) { } } } - - public static VOIBase findNearestAnnotation( final VOI annotations, final Vector3f startPt, final Vector3f endPt, final Vector3f pt ) { + public static VOIBase findNearestAnnotation(final VOI annotations, final Vector3f startPt, final Vector3f endPt, + final Vector3f pt) { int pickedAnnotation = -1; float minDist = Float.MAX_VALUE; - for ( int i = 0; i < annotations.getCurves().size(); i++ ) - { + for (int i = 0; i < annotations.getCurves().size(); i++) { final Vector3f annotationPt = annotations.getCurves().elementAt(i).elementAt(0); final float distance = pt.distance(annotationPt); - if ( distance < minDist ) - { + if (distance < minDist) { minDist = distance; - if ( minDist <= 12 ) - { + if (minDist <= 12) { pickedAnnotation = i; } } } // System.err.println("findNearestAnnotation " + minDist + " " + pickedAnnotation ); - if ( (pickedAnnotation == -1) && (startPt != null) && (endPt != null) ) - { + if ((pickedAnnotation == -1) && (startPt != null) && (endPt != null)) { minDist = Float.MAX_VALUE; // look at the vector under the mouse and see which lattice point is closest... final Segment3f mouseVector = new Segment3f(startPt, endPt); - for ( int i = 0; i < annotations.getCurves().size(); i++ ) - { - DistanceVector3Segment3 dist = new DistanceVector3Segment3(annotations.getCurves().elementAt(i).elementAt(0), mouseVector); + for (int i = 0; i < annotations.getCurves().size(); i++) { + DistanceVector3Segment3 dist = new DistanceVector3Segment3( + annotations.getCurves().elementAt(i).elementAt(0), mouseVector); float distance = dist.Get(); - // System.err.println( i + " " + distance ); - if ( distance < minDist ) - { + // System.err.println( i + " " + distance ); + if (distance < minDist) { minDist = distance; - if ( minDist <= 12 ) - { + if (minDist <= 12) { pickedAnnotation = i; } } } } - if ( pickedAnnotation != -1 ) { + if (pickedAnnotation != -1) { return annotations.getCurves().elementAt(pickedAnnotation); } return null; diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java index d5da71688a..bc3ce1cbf9 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/VolumeTriPlanarRender.java @@ -53,7 +53,11 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.Vector; import com.jogamp.opengl.GLAutoDrawable; @@ -61,6 +65,10 @@ import com.jogamp.opengl.awt.GLCanvas; import javax.swing.KeyStroke; +import org.janelia.mipav.plugins.worm.untwisting.AccurateModeListener; +import org.janelia.mipav.plugins.worm.untwisting.PlotDataUpdateListener; +import org.janelia.mipav.plugins.worm.untwisting.PlugInDialogVolumeRenderDualJanelia; + import WildMagic.LibFoundation.Mathematics.ColorRGBA; import WildMagic.LibFoundation.Mathematics.Mathf; import WildMagic.LibFoundation.Mathematics.Matrix3f; @@ -844,8 +852,15 @@ else if ( m_bErase ) m_bMouseDrag = false; } - private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) + + + private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPtAccurate) { + // Lists to store values and points for plot updates or further processing + List plotAccurateValues = new ArrayList<>(); + List plotValues = new ArrayList<>(); + List points = new ArrayList<>(); + m_kPicker.Execute(m_kVolumeRayCast.GetScene(),kPos,kDir,0.0f, Float.MAX_VALUE); @@ -857,6 +872,8 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) Vector3f pickedPoints[] = new Vector3f[m_kPicker.Records.size()]; float distances[] = new float[m_kPicker.Records.size()]; + long time = System.currentTimeMillis(); + for ( int i = 0; i < m_kPicker.Records.size(); i++ ) { PickRecord kPickPoint = m_kPicker.Records.elementAt(i); @@ -879,7 +896,9 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) firstIntersectionPoint.copy(pickedPoints[0]); secondIntersectionPoint.copy(pickedPoints[1]); + float maxValueAccurate = -Float.MAX_VALUE; float maxValue = -Float.MAX_VALUE; + Vector3f maxPt = new Vector3f(); Vector3f p0 = new Vector3f(firstIntersectionPoint); Vector3f p1 = new Vector3f(secondIntersectionPoint); @@ -931,62 +950,88 @@ private void PickVolume3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) continue; } } - float value = 0; - int sampleX = Math.round(p0.X); - int sampleY = Math.round(p0.Y); - int sampleZ = Math.round(p0.Z); - // loop over imageA, imageB, hyperstack, get value, apply color & opacity for 'value' - // take the maximum value: - if ( hyperstack != null ) { - for ( int h = 0; h < hyperstack.length; h++ ) { - if ( (hyperstack[h] != null) && (hyperstack[h].GetImage() != null) ) - { - if ( m_kVolumeRayCast.getImageOn(h) ) - { - value = Math.max(value, hyperstack[h].GetTransferedValue(sampleX, sampleY, sampleZ)); + + + // Two modes can be switched from the accurate mode to the 3-color mode(not working currently) by press key "M" or "m" + if (m_kVOIInterface.isAccurateMode()) { + // Old Mipav but more accurate click + float valueAccurate = 0; + valueAccurate = m_kVolumeImageA.GetImage().getFloatTriLinearBounds(p0.X, p0.Y, p0.Z); + if ((m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) { + float valueB = m_kVolumeImageB.GetImage().getFloatTriLinearBounds(p0.X, p0.Y, p0.Z); + float blend = getABBlend(); + valueAccurate = (blend * valueAccurate + (1 - blend) * valueB); + } + + if (valueAccurate > maxValueAccurate) { + maxValueAccurate = valueAccurate; + maxPtAccurate.copy(p0); + } + plotAccurateValues.add(valueAccurate);// add value to the list for plotting + + } else { + //this is not working, made for more than 3 colors. -Diyi Chen May 1, 2024 + float value = 0; + int sampleX = Math.round(p0.X); + int sampleY = Math.round(p0.Y); + int sampleZ = Math.round(p0.Z); + // loop over imageA, imageB, hyperstack, get value, apply color & opacity for + // 'value' + // take the maximum value: + if (hyperstack != null) { + for (int h = 0; h < hyperstack.length; h++) { + if ((hyperstack[h] != null) && (hyperstack[h].GetImage() != null)) { + if (m_kVolumeRayCast.getImageOn(h)) { + value = Math.max(value, + hyperstack[h].GetTransferedValue(sampleX, sampleY, sampleZ)); + } } } + } else if ((m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) { + float valueA = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); + float valueB = m_kVolumeImageB.GetTransferedValue(sampleX, sampleY, sampleZ); + float blend = getABBlend(); + value = (blend * valueA + (1 - blend) * valueB); + } else { + // Only one imageA: + value = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); } + if (value > maxValue) { + maxValue = value; + maxPt.copy(p0); + } + plotValues.add(value);// add value to the list for plotting } - else if ( (m_kVolumeImageB != null) && (m_kVolumeImageB.GetImage() != null)) { - float valueA = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); - float valueB = m_kVolumeImageB.GetTransferedValue(sampleX, sampleY, sampleZ); - float blend = getABBlend(); - value = (blend * valueA + (1 - blend) * valueB); - } - else - { - // Only one imageA: - value = m_kVolumeImageA.GetTransferedValue(sampleX, sampleY, sampleZ); - } - if ( value > maxValue ) - { - maxValue = value; - maxPt.copy(p0); - } + Vector3f p2 = new Vector3f(); + p2.copy(p0); + points.add(p2); } - if ( maxValue != -Float.MAX_VALUE ) - { + if (!m_kVOIInterface.isAccurateMode()) { + maxValueAccurate = maxValue; + maxPtAccurate.copy(maxPt); + } + + if (maxValueAccurate != -Float.MAX_VALUE) { boolean picked = false; // System.err.println( "mouse drag? " + m_bMouseDrag ); - if ( !m_bMouseDrag ) { + if (!m_bMouseDrag) { // select or create a new marker: - picked = select3DMarker( firstIntersectionPoint, secondIntersectionPoint, maxPt, rightMousePressed, altPressed ); - } - else if ( m_bMouseDrag ) { + picked = select3DMarker(firstIntersectionPoint, secondIntersectionPoint, maxPtAccurate, + rightMousePressed, altPressed); + } else if (m_bMouseDrag) { // modify currently selected, if exists - picked = modify3DMarker( firstIntersectionPoint, secondIntersectionPoint, maxPt ); + picked = modify3DMarker(firstIntersectionPoint, secondIntersectionPoint, maxPtAccurate); } - if ( !picked ) + if (!picked) { // add a new picked point: short id = (short) m_kVolumeImageA.GetImage().getVOIs().getUniqueID(); int colorID = 0; VOI newTextVOI = new VOI((short) colorID, "annotation3d_" + id, VOI.ANNOTATION, -1.0f); VOIText textVOI = new VOIText( ); - textVOI.add( maxPt ); - textVOI.add( maxPt ); + textVOI.add( maxPtAccurate ); + textVOI.add( maxPtAccurate ); // Transformation world = m_kVolumeRayCast.getMesh().World; // Vector3f dir = world.InvertVector(m_spkCamera.GetRVector()); // dir.scale(5); @@ -1017,16 +1062,38 @@ else if ( !doAutomaticLabels() ) m_kVOIInterface.updateDisplay(); } } - } + } + if (m_kVOIInterface.isAccurateMode()) { + notifyPlotListeners(points, plotAccurateValues, "Selection Chart"); + } else { + notifyPlotListeners(points, plotValues, "3-Color Chart"); + } + } + + // Create a Set to hold PlotListener instances + private Set listeners = new HashSet<>(); + + // update all listeners with new plot values and title. + public void notifyPlotListeners(List points, List values, String title) { + for (PlotDataUpdateListener listener : listeners) { + listener.updatePlotPanel(points, values, title); + } + } + + // add a new PlotListener + public void addPlotListener(PlotDataUpdateListener listener) { + listeners.add(listener); } - private boolean PickSlice3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) - { + // remove an existing PlotListener + public void removePlotListener(PlotDataUpdateListener listener) { + listeners.remove(listener); + } + + private boolean PickSlice3D(Vector3f kPos, Vector3f kDir, Vector3f maxPt) { // pick on the slices - m_kPicker.Execute(m_kSlices.GetScene(),kPos,kDir,0.0f, - Float.MAX_VALUE); - if (m_kPicker.Records.size() > 0) - { + m_kPicker.Execute(m_kSlices.GetScene(), kPos, kDir, 0.0f, Float.MAX_VALUE); + if (m_kPicker.Records.size() > 0) { PickRecord kPickPoint = m_kPicker.GetClosestNonnegative(); TriMesh kMesh = (TriMesh)kPickPoint.Intersected; int iPlane = m_kSlices.whichPlane(kMesh); diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java index 0076dc92d0..930f70d45d 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/LatticeModel.java @@ -82,7 +82,7 @@ public static void checkParentDir( String parentDir ) } else if (parentFileDir.exists() && !parentFileDir.isDirectory()) { // do nothing } else { // voiFileDir does not exist // System.err.println( "LatticeModel:checkParentDir" + parentDir); - parentFileDir.mkdir(); + parentFileDir.mkdirs(); } } @@ -374,7 +374,7 @@ public static void saveAllVOIsTo(final String voiDir, final ModelImage image) { voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveAllVOIsTo " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } final int nVOI = VOIs.size(); @@ -445,10 +445,8 @@ public static void saveAnnotationsAsCSV(final String dir, final String fileName, // check files, create new directories and delete any existing files: final File fileDir = new File(dir); - if (fileDir.exists() && fileDir.isDirectory()) {} - else if (fileDir.exists() && !fileDir.isDirectory()) { // voiFileDir.delete(); - } else { // voiFileDir does not exist - fileDir.mkdir(); + if (!fileDir.exists() && !fileDir.mkdirs()) { // voiFileDir does not exist + System.err.println("Failed to create directory in svaeAnnotationAsCSV: " + fileDir); } File file = new File(fileDir + File.separator + fileName); @@ -2815,8 +2813,8 @@ private void saveMeshContoursCSV() { String dir = sharedOutputDir + File.separator + "model_contours" + File.separator; File fileDir = new File(dir); - if ( !fileDir.exists() ) { // voiFileDir does not exist - fileDir.mkdir(); + if (!fileDir.exists() && !fileDir.mkdirs()) { // voiFileDir does not exist + System.err.println("Failed to create directory in saveMeshContourCSV: " + fileDir); } File file = new File(dir + File.separator + "wormContours.csv"); @@ -3075,10 +3073,8 @@ public ModelImage segmentLattice(final ModelImage image, boolean saveContourImag final File lrFile = new File(voiDir + list[i]); lrFile.delete(); } - } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); - } else { // voiFileDir does not exist - // System.err.println( "segmentLattice" + voiDir); - voiFileDir.mkdir(); + } else if (!voiFileDir.exists() && !voiFileDir.mkdirs()) { // voiFileDir does not exist + System.err.println("Failed to create directory in segmentLattice: " + voiFileDir); } System.err.println( "Segment Lattice " + paddingFactor ); @@ -3734,17 +3730,17 @@ public void setImage(ModelImage image) outputDirectory = new String(imageA.getImageDirectory() + JDialogBase.makeImageName(imageA.getImageFileName(), "") + File.separator); boolean isStraight = image.getImageFileName().contains("_straight"); File file = new File(outputDirectory); - if ( !file.exists() && !isStraight ) file.mkdir(); + if ( !file.exists() && !isStraight ) file.mkdirs(); outputDirectory += JDialogBase.makeImageName(imageA.getImageFileName(), "_results") + File.separator; file = new File(outputDirectory); - if ( !file.exists() && !isStraight ) file.mkdir(); + if ( !file.exists() && !isStraight ) file.mkdirs(); } } public void setSharedDirectory( String dir ) { sharedOutputDir = new String(dir); File file = new File(sharedOutputDir + File.separator + "output_images" + File.separator); - if ( !file.exists() ) file.mkdir(); + if ( !file.exists() ) file.mkdirs(); } @@ -5341,14 +5337,14 @@ protected VOI saveAnnotationStatistics(final String imageDir, final ModelImage m if (voiFileDir.exists() && voiFileDir.isDirectory()) { // do nothing } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; voiFileDir = new File(voiDir); if (voiFileDir.exists() && voiFileDir.isDirectory()) {} else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + "AnnotationInfo" + postFix + ".csv"); @@ -5404,7 +5400,7 @@ protected void saveDiameters( Vector diameters, String imageDir ) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveDiameters " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; @@ -5412,7 +5408,7 @@ protected void saveDiameters( Vector diameters, String imageDir ) { if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist // System.err.println( "saveDiameters " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + "Diameters.csv"); @@ -5460,14 +5456,14 @@ protected void saveLatticeStatistics( String imageDir, final float length, final if (voiFileDir.exists() && voiFileDir.isDirectory()) { // do nothing } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; voiFileDir = new File(voiDir); if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + "LatticeInfo" + postFix + ".csv"); @@ -5508,7 +5504,7 @@ protected void savePositions( VOIContour contour, String imageDir, String name ) } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "savePositions " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; @@ -5516,7 +5512,7 @@ protected void savePositions( VOIContour contour, String imageDir, String name ) if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist // System.err.println( "savePositions " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + name + "Positions.csv"); @@ -5558,7 +5554,7 @@ protected void saveSamplePlanes( VOI planes, String imageDir ) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveSamplePlanes " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = imageDir + "statistics" + File.separator; @@ -5566,7 +5562,7 @@ protected void saveSamplePlanes( VOI planes, String imageDir ) { if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist // System.err.println( "saveSamplePlanes " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + "SamplePlanes.csv"); @@ -6654,13 +6650,13 @@ private void saveImage(final String imageName, final ModelImage image, final boo } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + "output_images" + File.separator; voiFileDir = new File(voiDir); if (voiFileDir.exists() && voiFileDir.isDirectory()) {} else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } final File file = new File(voiDir + imageName); @@ -6688,7 +6684,7 @@ public static void saveImage(final ModelImage originalImage, final ModelImage im } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + subDir + File.separator; @@ -6729,7 +6725,7 @@ public static void saveImage(final ModelImage originalImage, final ModelImage im // } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); // } else { // voiFileDir does not exist // // System.err.println( "saveImage " + voiDir); -// voiFileDir.mkdir(); +// voiFileDir.mkdirs(); // } // // maxVal++; @@ -6785,7 +6781,7 @@ public static void saveContourAsCSV( ModelImage image, String subDir, String pos } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + subDir + File.separator; @@ -6805,7 +6801,7 @@ public static void saveContourAsCSV( final String voiDir, final String fileName, if (fileDir.exists() && fileDir.isDirectory()) {} else if (fileDir.exists() && !fileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - fileDir.mkdir(); + fileDir.mkdirs(); } File file = new File(fileDir + File.separator + fileName); if (file.exists()) { @@ -6901,7 +6897,7 @@ public static void saveBasisVectorsAsCSV( ModelImage image, String subDir, Strin } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + subDir + File.separator; @@ -6914,7 +6910,7 @@ public static void saveBasisVectorsAsCSV( ModelImage image, String subDir, Strin if (fileDir.exists() && fileDir.isDirectory()) {} else if (fileDir.exists() && !fileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - fileDir.mkdir(); + fileDir.mkdirs(); } File file = new File(fileDir + File.separator + imageName); if (file.exists()) { @@ -6956,7 +6952,7 @@ private void saveContours( ModelImage image, Box3f[] contours ) { if (voiFileDir.exists() && voiFileDir.isDirectory()) { // do nothing } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } String imageName = JDialogBase.makeImageName(image.getImageFileName(), ""); imageName = imageName + "_contours"; @@ -7080,7 +7076,7 @@ public static void saveTriMesh( ModelImage image, String outputDirectory, String } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + subDir + File.separator; voiFileDir = new File(voiDir); @@ -7088,7 +7084,7 @@ public static void saveTriMesh( ModelImage image, String outputDirectory, String } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveImage " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } String imageName = JDialogBase.makeImageName(image.getImageFileName(), ""); @@ -7130,7 +7126,7 @@ private void saveSpline(String outputDirectory, VOI data, Vector3f transformedOr } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) { // voiFileDir.delete(); } else { // voiFileDir does not exist // System.err.println( "saveSpline " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } voiDir = outputDirectory + File.separator + "statistics" + File.separator; voiFileDir = new File(voiDir); @@ -7139,7 +7135,7 @@ private void saveSpline(String outputDirectory, VOI data, Vector3f transformedOr // not // exist // System.err.println( "saveSpline " + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + data.getName() + postFix + ".csv"); @@ -7312,7 +7308,7 @@ private void untwistLattice(final ModelImage image, final int[] resultExtents) if (voiFileDir.exists() && voiFileDir.isDirectory()) { } else if (voiFileDir.exists() && !voiFileDir.isDirectory()) {} else { // voiFileDir does not exist // System.err.println( "untwistLattice" + voiDir); - voiFileDir.mkdir(); + voiFileDir.mkdirs(); } File file = new File(voiDir + imageName + "_Frame_Straight.csv"); @@ -8427,6 +8423,9 @@ private void updateLattice(final boolean rebuild) { } if (right.getCurves().size() == 0) { updateLatticeListeners(); + // added updateSelected() to fix the movable of first 3D marker to reflects the + // mourseDrag of the marker in the plot + updateSelected(); return; } diff --git a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/WormData.java b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/WormData.java index 7e730d6315..85dc3f9919 100644 --- a/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/WormData.java +++ b/src/gov/nih/mipav/view/renderer/WildMagic/WormUntwisting/WormData.java @@ -87,17 +87,15 @@ public WormData( ModelImage image ) checkParentDir(parentDir); } File dir = new File(outputDirectory); - if ( !dir.exists() ) - { - // System.err.println( "WormData " + outputDirectory); - dir.mkdir(); - } + + if (!dir.exists() && !dir.mkdirs()) { // dir does not exist + System.err.println("Failed to create directory in WormData: " + dir); + } + if ( outputImagesDirectory != null ) { dir = new File(outputImagesDirectory); - if ( !dir.exists() ) - { - // System.err.println( "WormData " + outputImagesDirectory); - dir.mkdir(); + if ( !dir.exists() && !dir.mkdirs()){ + System.err.println("Failed to create directory in WormData: " + dir); } } } diff --git a/src/lib/jfreechart-1.5.4.jar b/src/lib/jfreechart-1.5.4.jar new file mode 100644 index 0000000000..ddd7c23d17 Binary files /dev/null and b/src/lib/jfreechart-1.5.4.jar differ diff --git a/src/org/janelia/mipav/BatchProcessLogFrame.java b/src/org/janelia/mipav/BatchProcessLogFrame.java index 5f6ac23e0c..c19b28abda 100644 --- a/src/org/janelia/mipav/BatchProcessLogFrame.java +++ b/src/org/janelia/mipav/BatchProcessLogFrame.java @@ -1,77 +1,53 @@ package org.janelia.mipav; -import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; -import java.awt.LayoutManager; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; import java.awt.Dialog.ModalityType; -import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileWriter; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTextArea; -import javax.swing.JTextField; public class BatchProcessLogFrame implements ActionListener { - // add variables here + // variables JFrame frame; JButton buttonOk, buttonCopyAll, buttonBrowse; - JPanel panelButton, checkPanel, radioPanel; - JOptionPane panelButtonDialog, panelNoDupAnnDialog; - JDialog dialogAfterButton, noDupAnnDialog; - LayoutManager layout; - StringBuffer choices; - JRadioButton radioButtonA, radioButtonB; - String newline = "\n"; - JTextField textField; + JDialog noDupAnnDialog; JTextArea textArea; JFileChooser fileChooser; File file, directory; + // Constructor public BatchProcessLogFrame() { - // write code here this.frame = new JFrame("Batch Process Log"); - this.layout = new FlowLayout(); createGUI(); } private void createGUI() { + JOptionPane panelNoDupAnnDialog; + frame.setLayout(new GridBagLayout()); - // create buttonOK - buttonOk = new JButton("Ok to Close"); - // add button to actionListener + // create buttons and add top actionListener + buttonOk = new JButton("Ok"); buttonOk.addActionListener(this); - // add buttonCopyAll - buttonCopyAll = new JButton("Copy All"); - //buttonCopyAll.setBounds(70, 270, 150, 50); - //buttonCopyAll.setPreferredSize(new Dimension(200,500)); + buttonCopyAll = new JButton("Copy"); buttonCopyAll.addActionListener(this); - // add buttonBrowse - buttonBrowse = new JButton("Browse to save"); - //buttonBrowse.setBounds(70, 270, 150, 50); - //buttonBrowse.setPreferredSize(new Dimension(200,500)); + buttonBrowse = new JButton("Save"); buttonBrowse.addActionListener(this); - // create JPanel for the button - panelButton = new JPanel(); - panelButton.add(buttonOk); // Add button to JPanel - panelButton.add(buttonCopyAll); - panelButton.add(buttonBrowse); - // a dialog pop out when there is no content in the textArea panelNoDupAnnDialog = new JOptionPane("No duplicated annotations are found!"); noDupAnnDialog = panelNoDupAnnDialog.createDialog("Dialog"); @@ -99,29 +75,29 @@ private void createGUI() { c.gridy = 1; c.gridheight = 1; c.gridwidth = 1; - c.weightx = 1; // distribute space + c.weightx = 1; c.weighty = 0; - frame.add(buttonCopyAll, c); // add buttonCopyAll to the frame + frame.add(buttonCopyAll, c); // Anchor the buttonBrowse to the right (EAST) of the frame c.anchor = GridBagConstraints.EAST; c.fill = GridBagConstraints.NONE; c.gridx = 2; c.gridy = 1; - c.gridheight = 1; // see the height of the button the same as the height of checkbox1 + checkbox2 + c.gridheight = 1; c.weightx = 1; - frame.add(buttonBrowse, c); // add button to the frame + frame.add(buttonBrowse, c); // Anchor the "OK" button to the CENTER of the frame c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.NONE; c.gridx = 1; c.gridy = 2; - c.gridheight = 2; // see the height of the button the same as the height of checkbox1 + checkbox2 + c.gridheight = 2; c.weightx = 1; - frame.add(buttonOk, c); // add button to the frame + frame.add(buttonOk, c); - // Display the window. + // Display the frame. frame.setBounds(80, 80, 80, 80); frame.pack(); frame.setVisible(true); @@ -131,7 +107,7 @@ private void createGUI() { @Override public void actionPerformed(ActionEvent e) { - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + String newline = "\n"; String data = textArea.getText().trim(); // read contents of text area into 'data' if (!data.equals("")) { @@ -144,9 +120,6 @@ public void actionPerformed(ActionEvent e) { clpbrd.setContents(stringSelection, null); JOptionPane.showMessageDialog(null, "Texts are copied to clipboard!"); } else if (e.getSource() == buttonBrowse) { - // fileChooser = new JFileChooser("C:\\Users\\chend\\Desktop"); - // fileChooser = new JFileChooser(latticeStraighten.outputDirectory); - fileChooser.setSelectedFile(new File("Duplicated_Annotations.txt")); int returnVal = fileChooser.showSaveDialog(frame); @@ -155,12 +128,17 @@ public void actionPerformed(ActionEvent e) { // save to file try (FileWriter fw = new FileWriter(fileChooser.getSelectedFile())) { fw.write(textArea.getText()); + textArea.append("\n\n <<" + file.getName() + ">> is saved to: " + + fileChooser.getSelectedFile().getAbsolutePath() + newline); + } catch (FileNotFoundException ex) { + JOptionPane panelSaveFailedDialog = new JOptionPane("File save failed! " + "\n" + ex); + JDialog saveFailedDialog = panelSaveFailedDialog.createDialog("Dialog"); + saveFailedDialog.setModalityType(ModalityType.APPLICATION_MODAL); + saveFailedDialog.setVisible(true); + } catch (Exception ex) { ex.printStackTrace(); } - - textArea.append("\n\n <<" + file.getName() + ">> is saved to: " - + fileChooser.getSelectedFile().getAbsolutePath() + newline); } } } else { diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java b/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java new file mode 100644 index 0000000000..61dba10b75 --- /dev/null +++ b/src/org/janelia/mipav/plugins/worm/untwisting/AccurateModeListener.java @@ -0,0 +1,20 @@ +package org.janelia.mipav.plugins.worm.untwisting; + +/** + * The listener interface for receiving changes to accurateMode. + * The class that is interested in processing an action event + * implements this interface, and the object created with that + * class is registered with a component, using the component's + * {@code addAccurateModeListener} method. When the action event + * occurs, that object's {@code accurateModeChanged} method is + * invoked. + * + * @author diyi chen + */ +public interface AccurateModeListener { + /** + * Add this method for responding to the accurateModeListener + * @param isAccurateMode true if the state of the mode is accurate + */ + void accurateModeChanged(boolean isAccurateMode); +} \ No newline at end of file diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlotDataUpdateListener.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlotDataUpdateListener.java new file mode 100644 index 0000000000..30e7ae3475 --- /dev/null +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlotDataUpdateListener.java @@ -0,0 +1,30 @@ +package org.janelia.mipav.plugins.worm.untwisting; + +import java.util.List; + +import WildMagic.LibFoundation.Mathematics.Vector3f; + +/** + * Interface for handling updates to a plot panel. Classes that need to respond + * to plot data changes should implement this interface. An instance of a class + * implementing this interface can be registered with components that generate + * plot data updates. When plot data changes occur, the {@code updatePlotPanel} + * method of each registered listener is called, allowing the implementing class + * to update the plot display accordingly. + * + * @author diyi chen + */ +public interface PlotDataUpdateListener { + + /** + * Invoked to update the plot panel with new data. + * @param points + * + * @param values The list of data values to plot. Each value represents a data + * point on the plot. + * @param title The title of the plot, describing the data or context of what + * is being displayed + */ + void updatePlotPanel(List points, List values, String title); + +} diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInAlgorithmWormUntwistingJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInAlgorithmWormUntwistingJanelia.java index 3e41fe34f0..eb2f05eb54 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInAlgorithmWormUntwistingJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInAlgorithmWormUntwistingJanelia.java @@ -494,17 +494,20 @@ public static void batchFlipLattices( JProgressBar batchProgress, final Vector includeRange, final String[] baseFileDir, final String baseFileName, final String outputDir, final int paddingFactor, final boolean segmentLattice) { diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java index c7e96f2ad1..b2d5185864 100644 --- a/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java +++ b/src/org/janelia/mipav/plugins/worm/untwisting/PlugInDialogVolumeRenderDualJanelia.java @@ -1,6 +1,5 @@ package org.janelia.mipav.plugins.worm.untwisting; - //MIPAV is freely available from http://mipav.cit.nih.gov //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, @@ -68,20 +67,29 @@ import static org.jocl.CL.CL_DEVICE_TYPE_GPU; +import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.BufferedReader; @@ -92,6 +100,8 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Vector; import java.util.stream.Collectors; @@ -111,8 +121,12 @@ import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; +import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.ImageIcon; import org.jocl.Sizeof; @@ -128,14 +142,15 @@ import WildMagic.LibGraphics.SceneGraph.TriMesh; /** -* Implements the user-interface for the semi-automatic straightening of the -* worm. Provides batch-process algorithms for segmenting the seam cells and -* building lattices. Provides the framework for enabling the user to step -* through the selected image volumes and view/edit results from the automatic -* processes. Provides framework for animating the annotations after untwisting. -*/ -public class PlugInDialogVolumeRenderDualJanelia extends JFrame implements ActionListener, RendererListener, - PropertyChangeListener, ViewImageUpdateInterface, WindowListener, ChangeListener { + * Implements the user-interface for the semi-automatic straightening of the + * worm. Provides batch-process algorithms for segmenting the seam cells and + * building lattices. Provides the framework for enabling the user to step + * through the selected image volumes and view/edit results from the automatic + * processes. Provides framework for animating the annotations after untwisting. + */ +public class PlugInDialogVolumeRenderDualJanelia extends JFrame + implements ActionListener, RendererListener, PropertyChangeListener, ViewImageUpdateInterface, WindowListener, + ChangeListener, AccurateModeListener, PlotDataUpdateListener { private static final long serialVersionUID = -9056581285643263551L; @@ -215,6 +230,9 @@ public class PlugInDialogVolumeRenderDualJanelia extends JFrame implements Actio private JPanel opacityPanel; private JTabbedPane opacityTab; private JPanel clipPanel; + private JPanel accuratePanel; + private JToggleButton accurateModeButton; + private JButton nextPeakButton; private PlugInDialogVolumeRenderDualJanelia parent; private JTextField rangeFusionText; @@ -251,6 +269,9 @@ public class PlugInDialogVolumeRenderDualJanelia extends JFrame implements Actio private Vector annotationNames; private JSplitPane latticePanel = null; private JPanel latticePanelSingle = null; + + private SelectionChartPanel chartPanel; + private class IntegratedWormData { private VOIVector annotations; @@ -307,15 +328,15 @@ public PlugInDialogVolumeRenderDualJanelia() { public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); Object source = event.getSource(); - - if ( command.equals("BrowseConclude") ) { + + if (command.equals("BrowseConclude")) { UntwistDialog inputs = new UntwistDialog(baseFileLocText.getText()); - if ( inputs.latticeOutputDir != null ) { + if (inputs.latticeOutputDir != null) { latticeFileDir = new String(inputs.latticeOutputDir.getText()); setDefaultInputList(latticeFileDir); } } - + if (currentSize == null) { currentSize = new Dimension(getSize()); System.err.println(currentSize); @@ -330,8 +351,7 @@ public void actionPerformed(ActionEvent event) { this.setVisible(false); annotationAnimationFromSpreadSheet(); return; - } - else { + } else { // this.setVisible(false); // testForceGraph(); MipavUtil.displayError("Please specify a range of images."); @@ -342,8 +362,8 @@ public void actionPerformed(ActionEvent event) { if (batchFlipLattice.isSelected()) { try { // Batch Automatic Lattice-Building - PlugInAlgorithmWormUntwistingJanelia.batchFlipLattices(batchProgress, includeRange, latticeFileDir, - baseFileNameText.getText()); + PlugInAlgorithmWormUntwistingJanelia.batchFlipLattices(batchProgress, includeRange, + latticeFileDir, baseFileNameText.getText()); } catch (java.lang.OutOfMemoryError e) { MipavUtil .displayError("Error: Not enough memory. Unable to finish automatic lattice-building."); @@ -378,8 +398,7 @@ public void actionPerformed(ActionEvent event) { try { if (resliceImageCheck.isSelected()) { PlugInAlgorithmWormUntwistingJanelia.reslice(batchProgress, includeRange, baseFileDir, - baseFileNameText.getText(), resliceXValue, resliceYValue, - resliceZValue); + baseFileNameText.getText(), resliceXValue, resliceYValue, resliceZValue); } } catch (java.lang.OutOfMemoryError e) { MipavUtil.displayError("Error: Not enough memory. Unable to finish reslice calculation."); @@ -408,7 +427,8 @@ public void actionPerformed(ActionEvent event) { validate(); leftRenderer.setVisible(true); - if ( rightRenderer != null ) rightRenderer.setVisible(true); + if (rightRenderer != null) + rightRenderer.setVisible(true); } else { editMode = EditNONE; startButton.setEnabled(true); @@ -422,12 +442,13 @@ public void actionPerformed(ActionEvent event) { editCrossSections.setVisible(true); predict.setVisible(true); previewUntwisting.setVisible(true); - displayControls.setVisible(false); + displayControls.setVisible(false); imageChannels.setVisible(true); validate(); leftRenderer.setVisible(true); - if ( rightRenderer != null ) rightRenderer.setVisible(true); + if (rightRenderer != null) + rightRenderer.setVisible(true); } else { editMode = EditNONE; startButton.setEnabled(true); @@ -445,7 +466,8 @@ public void actionPerformed(ActionEvent event) { validate(); leftRenderer.setVisible(true); - if ( rightRenderer != null ) rightRenderer.setVisible(true); + if (rightRenderer != null) + rightRenderer.setVisible(true); } else { editMode = EditNONE; startButton.setEnabled(true); @@ -548,12 +570,12 @@ public void actionPerformed(ActionEvent event) { // Closes the editing: else if (command.equals("done")) { if (!displayControls.isVisible()) { - displayControls.setVisible(true); + displayControls.setVisible(true); imageChannels.setVisible(false); validate(); } - //mkitti 2023_03_01: this is where we save when "done" + // mkitti 2023_03_01: this is where we save when "done" saveAll(); if (parent != null) { parent.enableNext(editMode); @@ -562,10 +584,9 @@ else if (command.equals("done")) { } else { enableNext(editMode); } - } - else if (command.equals("demo")) { + } else if (command.equals("demo")) { updateSurfacePanels(); - activeRenderer.addAnimationLattice( activeImage.voiManager.getLattice() ); + activeRenderer.addAnimationLattice(activeImage.voiManager.getLattice()); } // Enables user to generate a new lattice (when none of the automatic ones match // well) @@ -641,16 +662,15 @@ else if (command.equals("newLattice")) { // activeImage.opacity[i] = activeImage.volOpacityPanel[i].getCompA().getOpacityTransferFunction(); //// activeImage.hyperstack[i].UpdateImages(activeImage.opacity[i], 0, null); // } - + if (previewUntwisting.getText().equals("preview")) { previewUntwisting.setText("return"); - + previewCount++; - + // save relative contours here upon returning from preview activeImage.voiManager.saveCrossSections(); - // save image orientation: activeImage.volumeMatrix = new Matrix3f(activeRenderer.GetSceneRotation()); @@ -661,28 +681,25 @@ else if (command.equals("newLattice")) { activeImage.annotationsTwisted = null; } activeImage.latticeTwisted = new VOIVector(activeImage.voiManager.getLattice()); - - if (activeImage.previewHS != null) { - for ( int i = 0; i < activeImage.previewHS.length; i++ ) { + for (int i = 0; i < activeImage.previewHS.length; i++) { activeImage.previewHS[i].GetImage().disposeLocal(false); } } activeImage.previewHS = untwistingTest(); - for ( int i = 0; i < activeImage.previewHS.length; i++ ) { - activeImage.previewHS[i].UpdateImages( activeImage.hyperstack[i].GetLUT()); + for (int i = 0; i < activeImage.previewHS.length; i++) { + activeImage.previewHS[i].UpdateImages(activeImage.hyperstack[i].GetLUT()); } activeImage.voiManager.removeListeners(); activeImage.voiManager.setImage(activeImage.previewHS[0].GetImage(), null); activeImage.volumeImage = activeImage.previewHS[0]; activeRenderer.setHyperStack(activeImage.previewHS, activeImage.colormap); - } - else { + } else { // "return" previewCount--; previewUntwisting.setText("preview"); - + // save relative contours here upon returning from preview activeImage.voiManager.saveCrossSections(); @@ -690,8 +707,6 @@ else if (command.equals("newLattice")) { activeImage.voiManager.setImage(activeImage.hyperstack[0].GetImage(), null); activeImage.volumeImage = activeImage.hyperstack[0]; - - // restore twisted annotations and lattice: activeImage.wormImage.unregisterAllVOIs(); VOIVector newLatticeTwisted = activeImage.voiManager.retwistLattice(activeImage.latticeTwisted); @@ -741,18 +756,15 @@ else if (command.equals("newLattice")) { } } } - - - + if (activeImage.previewHS != null) { - for ( int i = 0; i < activeImage.previewHS.length; i++ ) { + for (int i = 0; i < activeImage.previewHS.length; i++) { activeImage.previewHS[i].GetImage().disposeLocal(false); } activeImage.previewHS = null; } activeRenderer.setHyperStack(activeImage.hyperstack, activeImage.colormap); - - + // activeImage.voiManager.setImage(activeImage.wormImage, null); // activeImage.volumeImage.UpdateData(activeImage.wormImage, activeImage.volumeImage.GetLUT(), true); // activeRenderer.resetAxis(); @@ -819,44 +831,44 @@ else if (command.equals("newLattice")) { if (activeImage.voiManager != null) { activeImage.voiManager.setPaddingFactor(paddingFactor); } - } - - if ( source instanceof JCheckBox ) { - boolean selected = ((JCheckBox)source).isSelected(); - int which = imageChannel(command); + } + + if (source instanceof JCheckBox) { + boolean selected = ((JCheckBox) source).isSelected(); + int which = imageChannel(command); // System.err.println(which + " " + selected ); - if ( which != -1 ) { - activeRenderer.setImageOn(which, selected); + if (which != -1) { + activeRenderer.setImageOn(which, selected); } - if(opacityTab != null && opacityTab.getTabCount() > 0) { + if (opacityTab != null && opacityTab.getTabCount() > 0) { which = opacityTab.getSelectedIndex(); - for(int i = 0; i < opacityTab.getTabCount(); ++i) { - if( opacityTab.getTitleAt(i).startsWith(command) ) { + for (int i = 0; i < opacityTab.getTabCount(); ++i) { + if (opacityTab.getTitleAt(i).startsWith(command)) { opacityTab.setEnabledAt(i, selected); - if( !selected && i == which) { + if (!selected && i == which) { opacityTab.setSelectedIndex((which + 1) % opacityTab.getTabCount()); } - if( selected ) { + if (selected) { opacityTab.setSelectedIndex(i); } } } } - if(lutTab != null && lutTab.getTabCount() > 0) { + if (lutTab != null && lutTab.getTabCount() > 0) { which = lutTab.getSelectedIndex(); - for(int i = 0; i < lutTab.getTabCount(); ++i) { - if( lutTab.getTitleAt(i).startsWith(command) ) { + for (int i = 0; i < lutTab.getTabCount(); ++i) { + if (lutTab.getTitleAt(i).startsWith(command)) { lutTab.setEnabledAt(i, selected); - if( !selected && i == which) { + if (!selected && i == which) { lutTab.setSelectedIndex((which + 1) % lutTab.getTabCount()); } - if ( selected ) { + if (selected) { lutTab.setSelectedIndex(i); } } } } - + } } @@ -878,7 +890,6 @@ private void checkAnnotations() { } } - private String resultsDir(String sharedDir, ModelImage image) { String imageName = image.getImageFileName(); if (imageName.contains("_clone")) { @@ -887,13 +898,13 @@ private String resultsDir(String sharedDir, ModelImage image) { if (imageName.contains("_straight")) { imageName = imageName.replaceAll("_straight", ""); } - String outputDir = sharedDir + File.separator + JDialogBase.makeImageName(imageName, "") + - File.separator + JDialogBase.makeImageName(imageName, "_results") + File.separator; + String outputDir = sharedDir + File.separator + JDialogBase.makeImageName(imageName, "") + File.separator + + JDialogBase.makeImageName(imageName, "_results") + File.separator; return outputDir; } - + public void rendererConfigured(VolumeTriPlanarRenderBase renderer) { - + if ((annotationList != null) && (annotationNames != null) && (triVolume != null)) { triVolume.addVOIS(annotationList, annotationNames); triVolume.displayAnnotationSpheres(); @@ -914,6 +925,7 @@ public void rendererConfigured(VolumeTriPlanarRenderBase renderer) { leftDisplayPanel.setBorder(JDialogBase.buildTitledBorder(leftImage.wormImage.getImageName())); rightDisplayPanel.setBorder(JDialogBase.buildTitledBorder(rightImage.wormImage.getImageName(), Color.red)); } + activeRenderer.addPlotListener(this); if (activeImage.voiManager == null) { activeImage.voiManager = new VOILatticeManagerInterface(null, activeRenderer.getVolumeImage().GetImage(), @@ -921,29 +933,26 @@ public void rendererConfigured(VolumeTriPlanarRenderBase renderer) { } activeRenderer.setVOILatticeManager(activeImage.voiManager); - if ( activeImage.previewHS != null ) { + if (activeImage.previewHS != null) { activeImage.voiManager.setPreviewMode(true, activeImage.voiManager.getLatticeStraight(), activeImage.voiManager.getAnnotationsStraight()); activeRenderer.resetAxisXInv(); - } - else if ( activeImage.latticeTwisted != null ) { - activeImage.voiManager.setPreviewMode(false, activeImage.latticeTwisted, - activeImage.annotationsTwisted); + } else if (activeImage.latticeTwisted != null) { + activeImage.voiManager.setPreviewMode(false, activeImage.latticeTwisted, activeImage.annotationsTwisted); activeRenderer.SetSceneRotation(activeImage.volumeMatrix); - } - else { - if (editMode == ReviewResults) { - activeImage.voiManager.setLattice(WormData.readStraightLattice( resultsDir(latticeFileDir, activeImage.wormImage))); - } - else { + } else { + if (editMode == ReviewResults) { + activeImage.voiManager + .setLattice(WormData.readStraightLattice(resultsDir(latticeFileDir, activeImage.wormImage))); + } else { if (loadLegacyLatticeCheck.isSelected()) { activeImage.wormImage.setResolutions(originalResolutions); } final String dir = resultsDir(latticeFileDir, activeImage.wormImage); - activeImage.voiManager - .setLattice(WormData.readFinalLattice(dir, - loadLegacyLatticeCheck.isSelected(), activeImage.wormImage), dir); + activeImage.voiManager.setLattice( + WormData.readFinalLattice(dir, loadLegacyLatticeCheck.isSelected(), activeImage.wormImage), + dir); if (loadLegacyLatticeCheck.isSelected()) { activeImage.wormImage.setResolutions(new float[] { 1f, 1f, 1 }); @@ -957,17 +966,19 @@ else if ( activeImage.latticeTwisted != null ) { activeImage.annotations = null; } VOI annotations = activeImage.voiManager.getAnnotations(); - // System.err.println( activeImage.wormImage.getImageName() + " " + annotations); + // System.err.println( activeImage.wormImage.getImageName() + " " + + // annotations); if (annotations != null) { for (int i = 0; i < annotations.getCurves().size(); i++) { final VOIText text = (VOIText) annotations.getCurves().elementAt(i); - // System.err.println( " " + i + " " + text.getText() + " " + text.elementAt(0)); + // System.err.println( " " + i + " " + text.getText() + " " + + // text.elementAt(0)); text.createVolumeVOI(activeImage.volumeImage, activeRenderer.getTranslate()); } } } - + activeRenderer.displayVOIs(true); activeImage.voiManager.editAnnotations(editMode == EditSeamCells); activeImage.voiManager.colorAnnotations(editMode == EditSeamCells); @@ -979,7 +990,7 @@ else if ( activeImage.latticeTwisted != null ) { activeImage.annotationPanelUI.setPreviewMode(activeImage.previewHS != null); activeImage.latticeTable.setPreviewMode(activeImage.previewHS != null); - + activeImage.voiManager.editAnnotations(false); // initDisplaySeamPanel(); } else if (editMode == EditLattice) { @@ -998,16 +1009,16 @@ else if ( activeImage.latticeTwisted != null ) { renderer.resetAxisXInv(); } - initImageChannels(activeImage); updateHistoLUTPanels(activeImage); updateClipPanel(activeImage, activeRenderer, true); updateSurfacePanels(); + updateSelectionPanel(); if (activeImage.currentTab != -1) { tabbedPane.setSelectedIndex(activeImage.currentTab); } - + int nextStep = dualGPU == null ? 1 : 2; imageIndex = Math.min(includeRange.size() - 1, imageIndex); imageIndex = Math.max(0, imageIndex); @@ -1021,7 +1032,6 @@ else if ( activeImage.latticeTwisted != null ) { backButton.setEnabled(false); nextButton.setEnabled(false); } - setExtendedState(JFrame.MAXIMIZED_BOTH); if (dualGPU != null) { @@ -1042,7 +1052,7 @@ else if ( activeImage.latticeTwisted != null ) { public void setActiveRenderer(VolumeTriPlanarRenderBase renderer) { // System.err.println("setActiveRenderer"); - VolumeTriPlanarRenderBase previousActive = activeRenderer; + VolumeTriPlanarRender previousActive = activeRenderer; if (leftRenderer == renderer) { activeRenderer = leftRenderer; leftDisplayPanel.setBorder(JDialogBase.buildTitledBorder(leftImage.wormImage.getImageName(), Color.red)); @@ -1056,7 +1066,8 @@ public void setActiveRenderer(VolumeTriPlanarRenderBase renderer) { activeImage = rightImage; } - + activeRenderer.addPlotListener(this); + if (previousActive != activeRenderer) { if (activeImage.voiManager.isPreview()) { @@ -1070,6 +1081,7 @@ public void setActiveRenderer(VolumeTriPlanarRenderBase renderer) { updateSurfacePanels(); updateClipPanel(activeImage, activeRenderer, true); updateHistoLUTPanels(activeImage); + updateSelectionPanel(); if (editMode == EditLattice) { leftImage.voiManager.editLattice(); @@ -1079,6 +1091,8 @@ public void setActiveRenderer(VolumeTriPlanarRenderBase renderer) { tabbedPane.setSelectedIndex(activeImage.currentTab); stateChanged(null); } + previousActive.removePlotListener(this); + } int nextStep = dualGPU == null ? 1 : 2; if (includeRange != null) { @@ -1107,7 +1121,7 @@ public void dispose() { * lattice, or annotations. The next step in the straightening process is * automatically enabled and selected. * - * @param mode + * @param mode TODO */ public void enableNext(int mode) { closeAll(); @@ -1132,9 +1146,11 @@ public void enableNext(int mode) { lutPanel.removeAll(); opacityPanel.removeAll(); clipPanel.removeAll(); + accuratePanel.removeAll(); tabbedPane.addTab("LUT", null, lutPanel); tabbedPane.addTab("Opacity", null, opacityPanel); tabbedPane.addTab("Clip", null, clipPanel); + tabbedPane.addTab("AccurateMode", null, accuratePanel); tabbedPane.addChangeListener(this); startButton.setEnabled(true); @@ -1195,11 +1211,11 @@ private void closeAll() { if (imageStack[i].voiManager != null) { imageStack[i].voiManager = null; } - if ( imageStack[i].hyperstack != null ) { - for ( int j = 0; j < imageStack[i].hyperstack.length; j++ ) { - if ( imageStack[i].hyperstack[j] != null ) { + if (imageStack[i].hyperstack != null) { + for (int j = 0; j < imageStack[i].hyperstack.length; j++) { + if (imageStack[i].hyperstack[j] != null) { imageStack[i].hyperstack[j].dispose(); - imageStack[i].hyperstack[j] = null; + imageStack[i].hyperstack[j] = null; } } } @@ -1228,8 +1244,9 @@ public void propertyChange(PropertyChangeEvent event) { if (propertyName.equals("Opacity")) { int which = opacityTab.getSelectedIndex(); // System.err.println("propertyChange " + which); - if ( which != -1 ) { - final TransferFunction kTransfer = activeImage.volOpacityPanel[which].getCompA().getOpacityTransferFunction(); + if (which != -1) { + final TransferFunction kTransfer = activeImage.volOpacityPanel[which].getCompA() + .getOpacityTransferFunction(); updateImages(activeImage.colormap, activeImage.hyperstack[which].GetImage(), kTransfer, which); activeImage.hyperstack[which].UpdateImages(kTransfer, 0, null); // @@ -1258,8 +1275,8 @@ public boolean updateImages() { // System.err.println("updateImages" ); if (activeImage.hyperstack != null && lutTab != null) { int which = lutTab.getSelectedIndex(); - if ( which != -1 ) { - updateImages(activeImage.colormap, activeImage.hyperstack[which].GetLUT(), which ); + if (which != -1) { + updateImages(activeImage.colormap, activeImage.hyperstack[which].GetLUT(), which); // System.err.println("updateImages " + which); activeImage.hyperstack[which].UpdateImages(activeImage.hyperstack[which].getLUT()); // if ( activeImage.previewHS != null ) { @@ -1272,14 +1289,14 @@ public boolean updateImages() { } return false; } - - private void updateImages(Texture texture, ModelLUT lut, int index ) { + + private void updateImages(Texture texture, ModelLUT lut, int index) { byte[] data = texture.GetImage().GetData(); ModelLUT.exportIndexedLUTMin(lut, data, index); texture.Reload(true); } - private void updateImages(Texture texture, ModelImage image, TransferFunction tf, int index ) { + private void updateImages(Texture texture, ModelImage image, TransferFunction tf, int index) { final int lutHeight = texture.GetImage().GetBound(0); final byte[] data = texture.GetImage().GetData(); int offset = index * lutHeight * 4; @@ -1295,6 +1312,7 @@ private void updateImages(Texture texture, ModelImage image, TransferFunction tf } texture.Reload(true); } + /* * (non-Javadoc) * @@ -1319,6 +1337,21 @@ public boolean updateImages(ModelLUT LUTa, ModelLUT LUTb, boolean flag, int inte return false; } + /** + * Responds to changes in accurate mode by updating the button's appearance and + * text to reflect the current mode state. This method is typically called by a + * listener that is notified when accurate mode settings are toggled elsewhere + * in the application. + * + * @param isAccurateMode A boolean indicating whether accurate mode is enabled + * (true) or disabled (false). + */ + @Override + public void accurateModeChanged(boolean isAccurateMode) { + accurateModeButton.setSelected(isAccurateMode); + accurateModeButton.setText(isAccurateMode ? "Accurate Mode is now On" : "Accurate Mode is now Off"); + } + @Override public void windowActivated(WindowEvent e) { } @@ -1419,7 +1452,7 @@ public boolean trackMIPAV_Python() throws Exception { // rightImage.wormData.getAnnotationsPath() ); ProcessBuilder processBuilder = new ProcessBuilder("python", baseFileDir + File.separator + "track_MIPAV.py", - WormData.getStraightAnnotationsPath(resultsDir(latticeFileDir, leftImage.wormImage)), + WormData.getStraightAnnotationsPath(resultsDir(latticeFileDir, leftImage.wormImage)), WormData.getStraightAnnotationsPath(resultsDir(latticeFileDir, rightImage.wormImage)), WormData.getIntegratedMarkerAnnotationsPath(resultsDir(latticeFileDir, leftImage.wormImage)), WormData.getIntegratedMarkerAnnotationsPath(resultsDir(latticeFileDir, rightImage.wormImage))); @@ -1447,10 +1480,10 @@ public boolean restraightenMIPAV_Python(IntegratedWormData data) throws Exceptio // leftImage.wormData.getAnnotationsPath() + " " + // rightImage.wormData.getAnnotationsPath() ); - String predictedTwisted = resultsDir(latticeFileDir, data.wormImage) + File.separator + "prediction" + File.separator - + "predicted_annotations.csv"; - String predictedStraight = resultsDir(latticeFileDir, data.wormImage) + File.separator + "prediction" + File.separator - + "predicted_straightened_annotations.csv"; + String predictedTwisted = resultsDir(latticeFileDir, data.wormImage) + File.separator + "prediction" + + File.separator + "predicted_annotations.csv"; + String predictedStraight = resultsDir(latticeFileDir, data.wormImage) + File.separator + "prediction" + + File.separator + "predicted_straightened_annotations.csv"; File file = new File(predictedTwisted); if (!file.exists()) @@ -1461,7 +1494,7 @@ public boolean restraightenMIPAV_Python(IntegratedWormData data) throws Exceptio ProcessBuilder processBuilder = new ProcessBuilder("python", baseFileDir[0] + File.separator + "restraighten_MIPAV.py", predictedStraight, predictedTwisted, - WormData.getIntegratedMarkerAnnotationsPath( resultsDir(latticeFileDir, rightImage.wormImage))); + WormData.getIntegratedMarkerAnnotationsPath(resultsDir(latticeFileDir, rightImage.wormImage))); processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); @@ -1479,8 +1512,8 @@ public boolean restraightenMIPAV_Python(IntegratedWormData data) throws Exceptio private void runPython() { // delete the predicted files for the right image: - String fileName = resultsDir(latticeFileDir, rightImage.wormImage) + File.separator + "prediction" + File.separator - + "predicted_straightened_annotations.csv"; + String fileName = resultsDir(latticeFileDir, rightImage.wormImage) + File.separator + "prediction" + + File.separator + "predicted_straightened_annotations.csv"; File straightFile = new File(fileName); if (straightFile.exists()) { straightFile.delete(); @@ -1495,7 +1528,8 @@ private void runPython() { // when user presses 'predict' // prototype: // 1. straighten the right image - left annotations remain fixed: - ModelImage contourImage = rightImage.voiManager.untwistAnnotations(resultsDir(latticeFileDir, rightImage.wormImage), rightImage.contourImage); + ModelImage contourImage = rightImage.voiManager + .untwistAnnotations(resultsDir(latticeFileDir, rightImage.wormImage), rightImage.contourImage); if (rightImage.contourImage != null && rightImage.contourImage != contourImage) { rightImage.contourImage.disposeLocal(false); rightImage.contourImage = null; @@ -1547,7 +1581,7 @@ private VolumeImage[] untwistingTest() { activeImage.voiManager.setPaddingFactor(paddingFactor); ModelImage[] images = activeImage.voiManager.untwistTest(activeImage.hyperstack); VolumeImage[] hyperstack = new VolumeImage[images.length]; - for ( int i = 0; i < images.length; i++ ) { + for (int i = 0; i < images.length; i++) { images[i].unregisterAllVOIs(); hyperstack[i] = new VolumeImage(false, images[i], "" + i, null, 0, false); } @@ -1566,19 +1600,19 @@ private boolean checkGPUMemory(ModelImage[] images) { OpenCLInfo.getMaxMemSize(CL_DEVICE_TYPE_GPU, maxMemSizeArray); long maxAllocSize = maxMemSizeArray[0]; long totalMemSize = maxMemSizeArray[1]; - + long memoryUsed = 0; - for ( int i = 0; i < images.length; i++ ) { - if ( images[i] != null ) { + for (int i = 0; i < images.length; i++) { + if (images[i] != null) { int dataSize = images[i].getDataSize(); - if ( dataSize > (maxAllocSize / (Sizeof.cl_float)) ) { + if (dataSize > (maxAllocSize / (Sizeof.cl_float))) { return false; } memoryUsed += dataSize; } } - if ( memoryUsed >= (totalMemSize / Sizeof.cl_float) ) { + if (memoryUsed >= (totalMemSize / Sizeof.cl_float)) { return false; } return true; @@ -1627,12 +1661,12 @@ private boolean openHyperStack() { long memoryInUse = MipavUtil.getUsedHeapMemory(); for (int i = 0; i < includeRange.size(); i++) { - + ModelImage[] images = new ModelImage[baseFileDir.length]; String fileName = baseFileName + "_" + includeRange.elementAt(i) + ".tif"; File[] imageFiles = new File[baseFileDir.length]; - for ( int j = 0; j < baseFileDir.length; j++ ) { + for (int j = 0; j < baseFileDir.length; j++) { if (editMode == ReviewResults) { fileName = baseFileName + "_" + includeRange.elementAt(i) + "_straight.tif"; String subDirName = baseFileName + "_" + includeRange.elementAt(i) + File.separator; @@ -1640,16 +1674,15 @@ private boolean openHyperStack() { + File.separator; imageFiles[j] = new File(baseFileDir[j] + File.separator + subDirName + subDirNameResults + PlugInAlgorithmWormUntwistingJanelia.outputImages + File.separator + fileName); - } - else { + } else { imageFiles[j] = new File(baseFileDir[j] + File.separator + fileName); } if (imageFiles[j].exists()) { images[j] = openImage(imageFiles[j], fileName); - System.err.println("Opening... " + fileName + " " + images[j].isColorImage() ); + System.err.println("Opening... " + fileName + " " + images[j].isColorImage()); images[j].calcMinMax(); - if ( images[j].isColorImage() ) { + if (images[j].isColorImage()) { images[j] = convertToGray(images[j]); } } @@ -1657,12 +1690,12 @@ private boolean openHyperStack() { // Add memory check here: if (i == 0) { - if ( !checkGPUMemory(images) ) { + if (!checkGPUMemory(images)) { MipavUtil.displayError("Image size too big to load on GPU."); progressBar.setVisible(false); progressBar.dispose(); progressBar = null; - for ( int j = 0; j < images.length; j++ ) { + for (int j = 0; j < images.length; j++) { if (images[j] != null) { images[j].disposeLocal(); images[j] = null; @@ -1683,7 +1716,7 @@ private boolean openHyperStack() { progressBar.setVisible(false); progressBar.dispose(); progressBar = null; - for ( int j = 0; j < images.length; j++ ) { + for (int j = 0; j < images.length; j++) { if (images[j] != null) { images[j].disposeLocal(); images[j] = null; @@ -1697,7 +1730,7 @@ private boolean openHyperStack() { thresholdImage(images); leftImage.wormImage = images[0]; - + imageStack[i] = leftImage; System.err.println("... adding " + i + " " + leftImage.wormImage.getImageName()); @@ -1708,15 +1741,18 @@ private boolean openHyperStack() { leftImage.annotations = null; } leftImage.hyperstack = new VolumeImage[images.length]; - for ( int j = 0; j < images.length; j++ ) { + for (int j = 0; j < images.length; j++) { leftImage.hyperstack[j] = new VolumeImage(false, images[j], "" + j, null, 0, false); } - leftImage.volumeImage = leftImage.hyperstack[0]; //new VolumeImage(false, leftImage.wormImage, "A", null, 0, false); + leftImage.volumeImage = leftImage.hyperstack[0]; // new VolumeImage(false, leftImage.wormImage, "A", + // null, 0, false); leftImage.voiManager = new VOILatticeManagerInterface(null, leftImage.volumeImage.GetImage(), null, 0, true, null); - + leftImage.voiManager.addAccurateModeListener(this); + byte[] aucData = new byte[256 * 4 * images.length]; - GraphicsImage cmImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, images.length, aucData, new String("colormap" + leftImage.volumeImage.GetImage().getImageFileName())); + GraphicsImage cmImage = new GraphicsImage(GraphicsImage.FormatMode.IT_RGBA8888, 256, images.length, + aucData, new String("colormap" + leftImage.volumeImage.GetImage().getImageFileName())); leftImage.colormap = new Texture(); leftImage.colormap.SetImage(cmImage); @@ -1736,7 +1772,7 @@ private boolean openHyperStack() { return false; success = true; - leftRenderer = new VolumeTriPlanarRender( leftImage.hyperstack, leftImage.colormap ); + leftRenderer = new VolumeTriPlanarRender(leftImage.hyperstack, leftImage.colormap); leftRenderer.setVisible(false); leftRenderer.addConfiguredListener(this); @@ -1836,10 +1872,10 @@ private boolean openHyperStack() { return success; } - private void thresholdImage(ModelImage[] images ) { + private void thresholdImage(ModelImage[] images) { if (thresholdImageCheck.isSelected()) { - for ( int i = 0; i < images.length; i++ ) { - if ( images[i] != null ) { + for (int i = 0; i < images.length; i++) { + if (images[i] != null) { for (int j = 0; j < images[i].getDataSize(); j++) { if (images[i].getFloat(j) > threshold) { @@ -1852,40 +1888,45 @@ private void thresholdImage(ModelImage[] images ) { } } - private ModelImage convertToGray(ModelImage image ) { + private ModelImage convertToGray(ModelImage image) { ModelImage result = new ModelImage(ModelStorageBase.FLOAT, image.getExtents(), JDialogBase.makeImageName(image.getImageFileName(), "")); - final int dimX = image.getExtents().length > 0 ? image.getExtents()[0] : 1; + final int dimX = image.getExtents().length > 0 ? image.getExtents()[0] : 1; final int dimY = image.getExtents().length > 1 ? image.getExtents()[1] : 1; - final int dimZ = image.getExtents().length > 2 ? image.getExtents()[2] : 1; - + final int dimZ = image.getExtents().length > 2 ? image.getExtents()[2] : 1; + float max = 0; float maxSum = 0; - for ( int z = 0; z < dimZ; z++ ) { - for ( int y = 0; y < dimY; y++ ) { - for ( int x = 0; x < dimX; x++ ) { + for (int z = 0; z < dimZ; z++) { + for (int y = 0; y < dimY; y++) { + for (int x = 0; x < dimX; x++) { float r = image.getFloatC(x, y, z, 1); float g = image.getFloatC(x, y, z, 2); float b = image.getFloatC(x, y, z, 3); - if ( r > max ) max = r; - if ( g > max ) max = g; - if ( b > max ) max = b; + if (r > max) + max = r; + if (g > max) + max = g; + if (b > max) + max = b; float sum = r + b + g; - if ( sum > maxSum ) maxSum = sum; + if (sum > maxSum) + maxSum = sum; result.set(x, y, z, sum); } } } - if ( maxSum > max ) { + if (maxSum > max) { float scale = maxSum / max; - for ( int i = 0; i < result.getDataSize(); i++ ) { - result.set(i, result.getFloat(i) / scale);; + for (int i = 0; i < result.getDataSize(); i++) { + result.set(i, result.getFloat(i) / scale); + ; } } result.calcMinMax(); return result; } - + private ModelImage combineImages(ModelImage imageA, ModelImage imageB, ModelImage imageC) { // imageA is never null and is always written into the 'green' channel... @@ -1895,7 +1936,7 @@ private ModelImage combineImages(ModelImage imageA, ModelImage imageB, ModelImag // Make algorithm ModelImage blank = null; - if ( imageB == null && imageC != null ) { + if (imageB == null && imageC != null) { blank = new ModelImage(ModelImage.SHORT, imageA.getExtents(), JDialogBase.makeImageName(imageA.getImageName(), "")); @@ -1903,8 +1944,7 @@ private ModelImage combineImages(ModelImage imageA, ModelImage imageB, ModelImag true, true); mathAlgo.run(); blank.disposeLocal(false); - } - else if ( imageB != null && imageC == null ) { + } else if (imageB != null && imageC == null) { blank = new ModelImage(ModelImage.SHORT, imageA.getExtents(), JDialogBase.makeImageName(imageA.getImageName(), "")); @@ -1912,8 +1952,7 @@ else if ( imageB != null && imageC == null ) { true, true); mathAlgo.run(); blank.disposeLocal(false); - } - else { + } else { AlgorithmRGBConcat mathAlgo = new AlgorithmRGBConcat(imageB, imageA, imageC, displayImage, true, false, 255, true, true); mathAlgo.run(); @@ -1949,7 +1988,7 @@ private void annotationAnimationFromSpreadSheet() { MipavUtil.centerOnScreen(progress); String inputDirName = baseFileDir[0] + File.separator; - System.err.println( inputDirName ); + System.err.println(inputDirName); final File inputFileDir = new File(inputDirName); Vector3f min = new Vector3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE); @@ -2163,7 +2202,34 @@ private void annotationAnimationFromSpreadSheet() { progress.dispose(); progress = null; } - + + /** + * Updates the plot panel by invoking the chart creation and updating the + * display. + * + * @param points List of 3D points for the plot. + * @param values List of values corresponding to each 3D point. + * @param title Title for the updated plot. + */ + public void updatePlotPanel(List points, List values, String title) { + SwingUtilities.invokeLater(() -> { + chartPanel.updateChart(values, title); + chartPanel.setChart3DPoints(points); + + chartPanel.revalidate(); + chartPanel.repaint(); + }); + } + + /** + * Updates the 3D model visualization based on a new 3D point. + * + * @param point The 3D point that the model needs to reflect. + */ + public void update3DModel(Vector3f point) { + activeImage.voiManager.modify3DMarker(point, point, point); + } + /** * User-interface initialization. If the UI is integrated all panels are * displayed in one window. Otherwise the UI is divided into volume display and @@ -2255,20 +2321,70 @@ private void init() { dialogGUI.getContentPane().add(panel1, BorderLayout.NORTH); dialogGUI.getContentPane().add(panel2, BorderLayout.SOUTH); - lutPanel = new JPanel(new GridLayout(2,1)); + lutPanel = new JPanel(new GridLayout(2, 1)); lutTab = new JTabbedPane(); lutTab.addChangeListener(this); lutPanel.add(lutTab, BorderLayout.CENTER); - opacityPanel = new JPanel( new BorderLayout() ); + opacityPanel = new JPanel(new BorderLayout()); opacityTab = new JTabbedPane(); opacityTab.addChangeListener(this); opacityPanel.add(opacityTab, BorderLayout.CENTER); + clipPanel = new JPanel(new BorderLayout()); + + // Create a panel for toggling between accurate and 3-color modes(not working + // currently) + accuratePanel = new JPanel(new BorderLayout()); + // Panel get larger horizontally if remove the two lines below + accuratePanel.setPreferredSize(new Dimension(400, 400)); + accuratePanel.setMaximumSize(new Dimension(400, 400)); + JPanel buttonPanel = new JPanel(); + + // Initialize a toggle button for switching modes + accurateModeButton = new JToggleButton("Accurate Mode"); + accurateModeChanged(true); + accurateModeButton.setPreferredSize(new Dimension(200, 50)); + accurateModeButton.setToolTipText("Press 'M' to toggle Accurate Mode"); + + // Add an action response to the button + accurateModeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JToggleButton toggleButton = (JToggleButton) e.getSource(); + boolean isSelected = toggleButton.isSelected(); + activeImage.voiManager.toggleAccurateMode(); + } + }); + + // Add the toggle button to the button panel and then add the panel to the + // accurate mode panel + buttonPanel.add(accurateModeButton); + accuratePanel.add(buttonPanel, BorderLayout.NORTH); + + chartPanel = new SelectionChartPanel(Arrays.asList(0.0f, 0.0f, 0.0f), "Selection Chart", this); - clipPanel = new JPanel( new BorderLayout() ); + // Created a fakePanel to split the panel + JPanel fakePanel = new JPanel(); + fakePanel.setPreferredSize(new Dimension(100,500)); // plot gets larger vertically if remove this line + JSplitPane chartFakeSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, chartPanel, fakePanel); + chartFakeSplit.setOneTouchExpandable(true); + chartFakeSplit.setContinuousLayout(true); + chartFakeSplit.setResizeWeight(0.7); + chartFakeSplit.setDividerLocation(0.5); + + nextPeakButton = new JButton("Next Peak"); + nextPeakButton.setPreferredSize(new Dimension(200, 50)); + nextPeakButton.setToolTipText("Press 'N' move to Next Peak"); + nextPeakButton.addActionListener(chartPanel); + + fakePanel.add(nextPeakButton); + + accuratePanel.add(chartFakeSplit, BorderLayout.CENTER); + tabbedPane = new JTabbedPane(); tabbedPane.addTab("LUT", null, lutPanel); tabbedPane.addTab("Opacity", null, opacityPanel); tabbedPane.addTab("Clip", null, clipPanel); + tabbedPane.addTab("Selection", null, accuratePanel); tabbedPane.setVisible(false); tabbedPane.addChangeListener(this); @@ -2276,9 +2392,10 @@ private void init() { displayControls.add(dialogGUI.getContentPane(), BorderLayout.NORTH); JPanel leftPanel = new JPanel(new BorderLayout()); leftPanel.add(displayControls, BorderLayout.NORTH); - + imageChannels = new JPanel(); - imageChannels.add(new JLabel("Select image channel:") ); + imageChannels.add(new JLabel("Select image channel:")); + imageChannels.add(new JLabel("Select Mode:")); imageChannels.setVisible(false); leftPanel.add(imageChannels, BorderLayout.CENTER); leftPanel.add(tabbedPane, BorderLayout.SOUTH); @@ -2307,6 +2424,8 @@ private void init() { segmentSeamCells.setSelected(true); } + + /** * Sets up the GPU volume display panel, with the 'back' and 'next' buttons for @@ -2343,7 +2462,7 @@ private Container initGPUPanel(int editMode) { doneButton.setVisible(true); doneButton.setEnabled(true); backNextPanel.add(doneButton); - + JButton demo = gui.buildButton("demo"); demo.addActionListener(this); demo.setActionCommand("demo"); @@ -2352,7 +2471,7 @@ private Container initGPUPanel(int editMode) { // backNextPanel.add(demo); latticeSelectionPanel = new JPanel(); - + newLatticeButton = gui.buildButton("new lattice"); newLatticeButton.addActionListener(this); newLatticeButton.setActionCommand("newLattice"); @@ -2373,13 +2492,13 @@ private Container initGPUPanel(int editMode) { displayModel.setVisible(false); displayModel.setEnabled(true); latticeSelectionPanel.add(displayModel); - + displaySurface = gui.buildCheckBox("Show Surface", false); displaySurface.addActionListener(this); displaySurface.setActionCommand("displaySurface"); displaySurface.setVisible(false); displaySurface.setEnabled(true); - + editCrossSections = gui.buildCheckBox("Edit Sections", false); editCrossSections.addActionListener(this); editCrossSections.setActionCommand("editCrossSections"); @@ -2849,10 +2968,11 @@ private void openNeuriteCurves(IntegratedWormData data) { private void loadPredicted(IntegratedWormData data) { // load and save the straightened predicted values: - VOI markerAnnotations = LatticeModel.readAnnotationsCSV(resultsDir(latticeFileDir, data.wormImage) + File.separator - + "prediction" + File.separator + "predicted_straightened_annotations.csv"); + VOI markerAnnotations = LatticeModel.readAnnotationsCSV(resultsDir(latticeFileDir, data.wormImage) + + File.separator + "prediction" + File.separator + "predicted_straightened_annotations.csv"); if (markerAnnotations != null) { - LatticeModel.saveAnnotationsAsCSV( resultsDir(latticeFileDir, data.wormImage) + File.separator + "straightened_annotations", + LatticeModel.saveAnnotationsAsCSV( + resultsDir(latticeFileDir, data.wormImage) + File.separator + "straightened_annotations", "straightened_annotations.csv", markerAnnotations); } // load, save and display the predicted annotations: @@ -2868,7 +2988,8 @@ private void loadPredicted(IntegratedWormData data) { System.err.println(markerAnnotations + " " + markerAnnotations.getCurves().size()); data.annotations.add(markerAnnotations); data.voiManager.addAnnotations(data.annotations); - WormData.saveIntegratedMarkerAnnotations(resultsDir(latticeFileDir, data.wormImage), data.voiManager.getAnnotations()); + WormData.saveIntegratedMarkerAnnotations(resultsDir(latticeFileDir, data.wormImage), + data.voiManager.getAnnotations()); data.annotations = null; VOI annotations = data.voiManager.getAnnotations(); @@ -2896,8 +3017,7 @@ private void openAnnotations(IntegratedWormData data) { if (markers != null) { data.annotations.add(markers); } - } - else { + } else { // if (loadLegacyAnnotationsCheck.isSelected()) { // data.wormImage.setResolutions(originalResolutions); // } @@ -2925,7 +3045,7 @@ private void openAnnotations(IntegratedWormData data) { System.err.println("openAnnotations " + data.wormImage.getImageName() + " " + data.annotations.elementAt(0).getCurves().size()); } - + /** * Saves the seam cells to the default edited file for the current image. */ @@ -2936,8 +3056,9 @@ private void saveSeamCells() { if (imageIndex >= includeRange.size()) { return; } - WormData.saveSeamAnnotations(resultsDir(latticeFileDir, activeImage.wormImage), activeImage.voiManager.getAnnotations(), - (editMode != CheckSeam) && (editMode != IntegratedEditing), true); + WormData.saveSeamAnnotations(resultsDir(latticeFileDir, activeImage.wormImage), + activeImage.voiManager.getAnnotations(), (editMode != CheckSeam) && (editMode != IntegratedEditing), + true); } /** @@ -2986,7 +3107,7 @@ private boolean setVariables() { } baseFileName = baseFileNameText.getText(); - + includeRange = new Vector(); String rangeFusion = rangeFusionText.getText(); if (rangeFusion != null) { @@ -3032,36 +3153,38 @@ private boolean setVariables() { } private boolean imageChannelsInit = false; + private void initImageChannels(IntegratedWormData integratedData) { - if ( imageChannelsInit ) return; + if (imageChannelsInit) + return; imageChannelsInit = true; int numImages = integratedData.hyperstack.length; String[] subDir = new String[numImages]; - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { int index = baseFileDir[i].lastIndexOf(File.separator) + 1; int len = baseFileDir[i].length(); - subDir[i] = baseFileDir[i].substring(index,len); + subDir[i] = baseFileDir[i].substring(index, len); JCheckBox box = new JCheckBox(subDir[i], true); box.setActionCommand(subDir[i]); box.addActionListener(this); - imageChannels.add( box ); + imageChannels.add(box); } } - + private int imageChannel(String cmd) { int numImages = activeImage.hyperstack.length; - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { int index = baseFileDir[i].lastIndexOf(File.separator) + 1; int len = baseFileDir[i].length(); - String subDir = baseFileDir[i].substring(index,len); - if ( subDir.equals(cmd) ) { + String subDir = baseFileDir[i].substring(index, len); + if (subDir.equals(cmd)) { return i; } } return -1; } - + /** * Creates or updates the histogram / LUT panel and opacity panels when a new * image is loaded. @@ -3122,9 +3245,9 @@ private void initHistoLUTPanel(IntegratedWormData integratedData) { int numImages = integratedData.hyperstack.length; integratedData.volOpacityPanel = new JPanelVolumeOpacity[numImages]; - for ( int i = 0; i < numImages; i++ ) { - integratedData.volOpacityPanel[i] = new JPanelVolumeOpacity(integratedData.hyperstack[i].GetImage(), - null, null, null, true); + for (int i = 0; i < numImages; i++) { + integratedData.volOpacityPanel[i] = new JPanelVolumeOpacity(integratedData.hyperstack[i].GetImage(), null, + null, null, true); integratedData.volOpacityPanel[i].addPropertyChangeListener(this); TransferFunction kTransfer = integratedData.volOpacityPanel[i].getCompA().getOpacityTransferFunction(); @@ -3134,18 +3257,17 @@ private void initHistoLUTPanel(IntegratedWormData integratedData) { // System.err.println( "initHistoLUTPanel " + // integratedData.volumeImage.getLUT() ); integratedData.lutHistogramPanel = new JFrameHistogram[numImages]; - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { - integratedData.lutHistogramPanel[i] = new JFrameHistogram(this, integratedData.hyperstack[i].GetImage(), null, - integratedData.hyperstack[i].getLUT(), null); - integratedData.lutHistogramPanel[i].histogramLUT(true, false, false);//!integratedData.volumeImage.GetImage().isColorImage()); + integratedData.lutHistogramPanel[i] = new JFrameHistogram(this, integratedData.hyperstack[i].GetImage(), + null, integratedData.hyperstack[i].getLUT(), null); + integratedData.lutHistogramPanel[i].histogramLUT(true, false, false);// !integratedData.volumeImage.GetImage().isColorImage()); } - // if (integratedData.volumeImage.GetImage().isColorImage()) { // integratedData.displayBothChannels.setSelected(true); // } - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { integratedData.hyperstack[i].GetImage().addImageDisplayListener(this); } } @@ -3156,13 +3278,13 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { // integratedData.lutHistogramPanel.getContainingPanel() ); int numImages = integratedData.hyperstack.length; String[] subDir = new String[numImages]; - if ( !integratedData.colorMapInit ) { + if (!integratedData.colorMapInit) { integratedData.colorMapInit = true; - for ( int i = 0; i < numImages; i++ ) { + for (int i = 0; i < numImages; i++) { int index = baseFileDir[i].lastIndexOf(File.separator) + 1; int len = baseFileDir[i].length(); - subDir[i] = baseFileDir[i].substring(index,len); - if ( subDir[i].equals("405") ) { + subDir[i] = baseFileDir[i].substring(index, len); + if (subDir[i].equals("405")) { // set transfer function: ModelLUT lut = integratedData.hyperstack[i].GetLUT(); lut.makeBlueTransferFunctions(); @@ -3171,7 +3293,7 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { tf.replacePoint(64, 0, 2); lut.makeLUT(256); } - if ( subDir[i].equals("488") ) { + if (subDir[i].equals("488")) { // set transfer function: ModelLUT lut = integratedData.hyperstack[i].GetLUT(); lut.makeGreenTransferFunctions(); @@ -3180,7 +3302,7 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { tf.replacePoint(64, 0, 2); lut.makeLUT(256); } - if ( subDir[i].equals("561") ) { + if (subDir[i].equals("561")) { // set transfer function: ModelLUT lut = integratedData.hyperstack[i].GetLUT(); lut.makeRedTransferFunctions(); @@ -3189,7 +3311,7 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { tf.replacePoint(64, 0, 2); lut.makeLUT(256); } - if ( subDir[i].equals("637") ) { + if (subDir[i].equals("637")) { // set transfer function: ModelLUT lut = integratedData.hyperstack[i].GetLUT(); TransferFunction tf = lut.getTransferFunction(); @@ -3199,7 +3321,7 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { } } } - + // re-add lutTab to lutPanel due to removal in enableNext if (lutPanel.getComponentCount() == 0) { lutPanel.add(lutTab, BorderLayout.CENTER); @@ -3209,52 +3331,52 @@ private void updateHistoLUTPanels(IntegratedWormData integratedData) { opacityPanel.add(opacityTab, BorderLayout.CENTER); } - opacityTab.removeAll(); - for ( int i = 0; i < numImages; i++ ) { + opacityTab.removeAll(); + for (int i = 0; i < numImages; i++) { int index = baseFileDir[i].lastIndexOf(File.separator) + 1; int len = baseFileDir[i].length(); - subDir[i] = baseFileDir[i].substring(index,len); - - opacityTab.addTab( subDir[i] + File.separator + integratedData.hyperstack[i].GetImage().getImageName(), - null, integratedData.volOpacityPanel[i].getMainPanel() ); + subDir[i] = baseFileDir[i].substring(index, len); - - final TransferFunction kTransfer = integratedData.volOpacityPanel[i].getCompA().getOpacityTransferFunction(); + opacityTab.addTab(subDir[i] + File.separator + integratedData.hyperstack[i].GetImage().getImageName(), null, + integratedData.volOpacityPanel[i].getMainPanel()); + + final TransferFunction kTransfer = integratedData.volOpacityPanel[i].getCompA() + .getOpacityTransferFunction(); updateImages(integratedData.colormap, integratedData.hyperstack[i].GetImage(), kTransfer, i); integratedData.volOpacityPanel[i].getCompA().showHistogram(); integratedData.hyperstack[i].UpdateImages(kTransfer, 0, null); - if ( integratedData.previewHS != null ) { + if (integratedData.previewHS != null) { integratedData.previewHS[i].UpdateImages(kTransfer, 0, null); } } if (tabbedPane.getSelectedComponent() == opacityPanel) { int which = opacityTab.getSelectedIndex(); - if ( which != -1 ) { + if (which != -1) { integratedData.volOpacityPanel[which].getCompA().showHistogram(); } } lutTab.removeAll(); - for ( int i = 0; i < numImages; i++ ) { - lutTab.addTab( subDir[i] + File.separator + integratedData.hyperstack[i].GetImage().getImageName(), - null, integratedData.lutHistogramPanel[i].getContainingPanel()); - updateImages(integratedData.colormap, integratedData.hyperstack[i].GetLUT(), i ); - + for (int i = 0; i < numImages; i++) { + lutTab.addTab(subDir[i] + File.separator + integratedData.hyperstack[i].GetImage().getImageName(), null, + integratedData.lutHistogramPanel[i].getContainingPanel()); + updateImages(integratedData.colormap, integratedData.hyperstack[i].GetLUT(), i); + integratedData.hyperstack[i].UpdateImages(integratedData.hyperstack[i].getLUT()); - if ( integratedData.previewHS != null ) { + if (integratedData.previewHS != null) { integratedData.previewHS[i].UpdateImages(integratedData.hyperstack[i].getLUT()); } integratedData.lutHistogramPanel[i].redrawFrames(); } if (tabbedPane.getSelectedComponent() == lutPanel) { - int which = lutTab.getSelectedIndex(); - if ( which != -1 ) { + int which = lutTab.getSelectedIndex(); + if (which != -1) { integratedData.lutHistogramPanel[which].redrawFrames(); } } - + lutPanel.revalidate(); } @@ -3292,6 +3414,15 @@ private void updateSurfacePanels() { // } } + /** + * Updates the selection panel method ensures that the user interface components + * related to VOI management reflect the current operational mode. + */ + private void updateSelectionPanel() { + VOILatticeManagerInterface voiManager = activeImage.voiManager; + voiManager.setAccurateMode(voiManager.isAccurateMode()); + } + /* * private VOIVector autoLattice() { VOIVector latticeContainer = new * VOIVector(); short id = (short) wormImage.getVOIs().getUniqueID(); VOI @@ -3337,7 +3468,7 @@ public void stateChanged(ChangeEvent arg0) { } private void saveIntegrated() { - activeImage.voiManager.setSharedDirectory( resultsDir(latticeFileDir, activeImage.wormImage)); + activeImage.voiManager.setSharedDirectory(resultsDir(latticeFileDir, activeImage.wormImage)); if (editMode == EditSeamCells || editMode == CheckSeam) { saveSeamCells(); @@ -3353,76 +3484,78 @@ private void saveIntegrated() { // separately so not untwisted twice: saveSplineCurves(); - if ( activeImage.voiManager != null ) { + if (activeImage.voiManager != null) { // save annotations not in splines: - WormData.saveIntegratedMarkerAnnotations(resultsDir(latticeFileDir, activeImage.wormImage), activeImage.voiManager.getAnnotations()); + WormData.saveIntegratedMarkerAnnotations(resultsDir(latticeFileDir, activeImage.wormImage), + activeImage.voiManager.getAnnotations()); } } - + private void setDefaultInputList(String dir) { File file = new File(dir); - if ( file.exists() && file.isDirectory() ) { + if (file.exists() && file.isDirectory()) { final String[] list = file.list(); String imageList = ""; for (int i = 0; i < list.length; i++) { - if ( list[i].endsWith(".tif") ) { - String temp = list[i].substring(list[i].lastIndexOf("_") + 1, list[i].indexOf(".tif") ); + if (list[i].endsWith(".tif")) { + String temp = list[i].substring(list[i].lastIndexOf("_") + 1, list[i].indexOf(".tif")); System.err.println(list[i] + " " + temp); - if ( imageList != "" ) imageList += ","; + if (imageList != "") + imageList += ","; imageList += temp; } } - if ( imageList != "" ) { + if (imageList != "") { rangeFusionText.setText(imageList); } } } - + private class UntwistDialog extends JDialogBase implements ActionListener { private JCheckBox[] volumeChecks; private String[] volumeDirs; private JTextField latticeOutputDir; - public UntwistDialog( String baseDir ) { + + public UntwistDialog(String baseDir) { File file = new File(baseDir); - if ( file.exists() && file.isDirectory() ) { + if (file.exists() && file.isDirectory()) { final String[] list = file.list(); Vector tempList = new Vector(); for (int i = 0; i < list.length; i++) { File subDir = new File(file.getAbsolutePath() + File.separator + list[i]); - if ( subDir.exists() && subDir.isDirectory() ) - { + if (subDir.exists() && subDir.isDirectory()) { System.err.println(file.getAbsolutePath() + File.separator + list[i]); tempList.add(list[i]); } } - if ( tempList.size() > 0 ) { + if (tempList.size() > 0) { init(baseDir, tempList); setVisible(true); } } - } - - // Diyi: Created a simpler version of actionPerformed to replace the previous one + } + + // Diyi: Created a simpler version of actionPerformed to replace the previous + // one public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); Object source = event.getSource(); System.err.println("UntwistDialog " + command); - if ( command.equals("Cancel") ) { + if (command.equals("Cancel")) { baseFileLocText.setText(""); latticeOutputDir = null; setVisible(false); - } - else if ( command.equals("OK") ) { - + } else if (command.equals("OK")) { + ArrayList list = new ArrayList<>(volumeChecks.length); - - for (int i = 0; i < volumeChecks.length; i++){ + + for (int i = 0; i < volumeChecks.length; i++) { if (volumeChecks[i].isSelected()) { - list.add(volumeDirs[i]); + list.add(volumeDirs[i]); } - baseFileDir = new String [list.size()]; + baseFileDir = new String[list.size()]; list.toArray(baseFileDir); } if (list.size() == 0) @@ -3430,9 +3563,8 @@ else if ( command.equals("OK") ) { setVisible(false); } } - - - private void init( String baseDir, Vector tempList ) { + + private void init(String baseDir, Vector tempList) { setForeground(Color.black); setTitle("Untwisting C.elegans - lattice - 2.0"); @@ -3441,57 +3573,58 @@ private void init( String baseDir, Vector tempList ) { } catch (FileNotFoundException e) { Preferences.debug("Failed to load default icon", Preferences.DEBUG_MINOR); } - JPanel input = new JPanel( new GridLayout(tempList.size() + 1, 1)); + JPanel input = new JPanel(new GridLayout(tempList.size() + 1, 1)); GuiBuilder gui = new GuiBuilder(this); volumeChecks = new JCheckBox[tempList.size()]; volumeDirs = new String[tempList.size()]; String latticeDir = null; - for ( int i = 0; i < tempList.size(); i++ ) { + for (int i = 0; i < tempList.size(); i++) { volumeChecks[i] = gui.buildCheckBox(tempList.elementAt(i), true); volumeChecks[i].addActionListener(this); input.add(volumeChecks[i].getParent()); volumeDirs[i] = new String(baseDir + File.separator + tempList.elementAt(i)); final File subDir = new File(volumeDirs[i]); - if ( latticeDir == null ) { - latticeDir = new String(baseDir + File.separator + tempList.elementAt(i)); + if (latticeDir == null) { + latticeDir = new String(baseDir + File.separator + tempList.elementAt(i)); + } + if (containsLattice(subDir)) { + latticeDir = new String(baseDir + File.separator + tempList.elementAt(i)); } - if ( containsLattice(subDir) ) { - latticeDir = new String(baseDir + File.separator + tempList.elementAt(i)); - } - + } - - latticeOutputDir = gui.buildFileField("Lattice Directory:", latticeDir, false, JFileChooser.DIRECTORIES_ONLY, - this); + + latticeOutputDir = gui.buildFileField("Lattice Directory:", latticeDir, false, + JFileChooser.DIRECTORIES_ONLY, this); input.add(latticeOutputDir.getParent()); - + System.err.println(latticeDir); getContentPane().add(input, BorderLayout.CENTER); - + JPanel buttonPanel = new JPanel(); - buttonPanel.add(JDialogBase.buildOKButton( "OK", this )); - buttonPanel.add(JDialogBase.buildCancelButton( "Cancel", this )); + buttonPanel.add(JDialogBase.buildOKButton("OK", this)); + buttonPanel.add(JDialogBase.buildCancelButton("Cancel", this)); getContentPane().add(buttonPanel, BorderLayout.SOUTH); setModal(true); - if ( bar != null ) bar.setVisible(false); + if (bar != null) + bar.setVisible(false); pack(); } - - private boolean containsLattice(File dir ) { - if ( dir.exists() && dir.isDirectory() ) { + private boolean containsLattice(File dir) { + + if (dir.exists() && dir.isDirectory()) { final String[] list = dir.list(); - for ( int i = 0; i < list.length; i++ ) { - if ( list[i].equals("lattice_final") ) { + for (int i = 0; i < list.length; i++) { + if (list[i].equals("lattice_final")) { return true; - } - else { - File file = new File(dir.getAbsolutePath() + File.separator + list[i] ); - if ( file.exists() && file.isDirectory() ) { - if ( containsLattice(file) ) return true; + } else { + File file = new File(dir.getAbsolutePath() + File.separator + list[i]); + if (file.exists() && file.isDirectory()) { + if (containsLattice(file)) + return true; } } } @@ -3499,76 +3632,79 @@ private boolean containsLattice(File dir ) { return false; } } - + public void setDemoValues() { - //baseFileLocText.setText("\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking"); + // baseFileLocText.setText("\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking"); // X:\shrofflab\RW10752_NU\Untwisting\031219_RW10752_NU\RW10752_NU\RW10752_NU\Pos2\Decon_registered - - //diyi local test path: E:\Diyi\Pos2\Decon_registered - //String access_path = "E:\\Diyi\\Pos2\\Decon_registered"; - - //from diyi error mimic path: X:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB - //from jhonny:Z:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results - //copied to diyi from jhonny: E:\Diyi\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results - - //from online:\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking - //String access_path = "\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking"; - - //from online jhonny: X:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results - //String access_path = "X:\\shrofflab\\Vab-1\\Tracking\\Pos0\\SPIMB\\Reg_Sample\\For_Tracking"; - - //String access_path = "E:\\Diyi\\SPIMB\\Reg_Sample\\For_Tracking"; + + // diyi local test path: E:\Diyi\Pos2\Decon_registered + // String access_path = "E:\\Diyi\\Pos2\\Decon_registered"; + + // from diyi error mimic path: + // X:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB + // from + // jhonny:Z:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results + // copied to diyi from jhonny: + // E:\Diyi\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results + + // from + // online:\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking + // String access_path = + // "\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking"; + + // from online jhonny: + // X:\shrofflab\Vab-1\Tracking\Pos0\SPIMB\Reg_Sample\For_Tracking\RegB\Decon_reg_14\Decon_reg_14_results + // String access_path = + // "X:\\shrofflab\\Vab-1\\Tracking\\Pos0\\SPIMB\\Reg_Sample\\For_Tracking"; + + // String access_path = "E:\\Diyi\\SPIMB\\Reg_Sample\\For_Tracking"; String access_path = "D:\\shroff\\For_Tracking"; baseFileLocText.setText(access_path); editLattice.setSelected(true); - //actionPerformed(new ActionEvent(this, 0, "BrowseConclude")); - + // actionPerformed(new ActionEvent(this, 0, "BrowseConclude")); + String[] volumeDirs; - + String baseDir = baseFileLocText.getText(); File file = new File(baseDir); - + final String[] list = file.list(); Vector tempList = new Vector(); for (int i = 0; i < list.length; i++) { File subDir = new File(file.getAbsolutePath() + File.separator + list[i]); - if ( subDir.exists() && subDir.isDirectory() ) - { + if (subDir.exists() && subDir.isDirectory()) { System.err.println(file.getAbsolutePath() + File.separator + list[i]); - if (list[i].equals("RegA") || list[i].equals("RegB")) - { + if (list[i].equals("RegA") || list[i].equals("RegB")) { tempList.add(list[i]); } } } - + volumeDirs = new String[tempList.size()]; - + int count = 0; - for ( int i = 0; i < volumeDirs.length; i++ ) { - count++; + for (int i = 0; i < volumeDirs.length; i++) { + count++; volumeDirs[i] = new String(baseDir + File.separator + tempList.elementAt(i)); } - if ( count == 0 ) { + if (count == 0) { baseFileLocText.setText(""); - //latticeOutputDir = null; - } - else { + // latticeOutputDir = null; + } else { baseFileDir = new String[count]; count = 0; - for ( int i = 0; i < volumeDirs.length; i++ ) { + for (int i = 0; i < volumeDirs.length; i++) { baseFileDir[count++] = volumeDirs[i]; } } - //baseFileLocText.setText("\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking\\RegB"); + // baseFileLocText.setText("\\\\nearline4.hhmi.org\\shroff\\shrofflab\\efn-1\\Tracking\\Pos0\\For_Tracking\\RegB"); latticeFileDir = new String(access_path + "\\RegB"); setDefaultInputList(latticeFileDir); rangeFusionText.setText("60-61"); - + actionPerformed(new ActionEvent(this, 0, "start")); } } - diff --git a/src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java b/src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java new file mode 100644 index 0000000000..bece67fd75 --- /dev/null +++ b/src/org/janelia/mipav/plugins/worm/untwisting/SelectionChartPanel.java @@ -0,0 +1,371 @@ +package org.janelia.mipav.plugins.worm.untwisting; + +import org.jfree.chart.*; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.event.MarkerChangeEvent; +import org.jfree.chart.event.MarkerChangeListener; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.ValueMarker; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.ui.RectangleAnchor; +import org.jfree.chart.ui.TextAnchor; +import org.jfree.chart.ui.Layer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import WildMagic.LibFoundation.Mathematics.Vector3f; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class SelectionChartPanel extends ChartPanel implements MarkerChangeListener { + + +/** + * CustomChartPanel is a custom extension of the ChartPanel class from JFreeChart + * used to display and interact with a dynamic XY line chart. It includes custom + * behaviors for handling marker changes and mouse drag events. + */ + private static final long serialVersionUID = 1L; + private JFreeChart selectionChart; + private List chart3DPoints; + private PlugInDialogVolumeRenderDualJanelia parent; + + public SelectionChartPanel(List values, String title, PlugInDialogVolumeRenderDualJanelia parent) { + super(null); + this.selectionChart = createChart(values, title, this); + this.parent = parent; + this.setChart(selectionChart); + setPreferredSize(new Dimension(100,100));// plot gets larger vertically and horizontally if remove this line + initialize(); + + } + + private void initialize() { + setMouseWheelEnabled(false); + setDomainZoomable(false); + setRangeZoomable(false); + setMouseZoomable(false, false); + setFillZoomRectangle(false); + setZoomAroundAnchor(false); + + + // Add a mouse motion listener to handle dragging movements over the chart + addMouseMotionListener(new MouseAdapter() { + @Override + public void mouseDragged(MouseEvent e) { + // Get the plot area for accurate coordinate calculation + Rectangle2D plotArea = getScreenDataArea(); + XYPlot plot = selectionChart.getXYPlot(); + ValueAxis xAxis = plot.getDomainAxis(); + double x = xAxis.java2DToValue(e.getX(), plotArea, plot.getDomainAxisEdge()); + ValueAxis yAxis = plot.getRangeAxis(); + double yClick = yAxis.java2DToValue(e.getY(), plotArea, plot.getRangeAxisEdge()); + + // Determine the corresponding Y-value by finding the nearest index + XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); + XYSeries series = dataset.getSeries(0); + XYSeries slopeSeries = dataset.getSeries(1); + + int index = findNearestXIndex(series, x); + double y = series.getY(index).doubleValue(); + double slope = slopeSeries.getY(index).doubleValue(); // slope is derivative at x + + // Update the marker's value and label on the chart + ValueMarker marker = (ValueMarker) plot.getDomainMarkers(Layer.FOREGROUND).iterator().next(); + marker.setValue(x); + marker.setLabel(String.format("Value: %.2f, Derivative: %.2f", y, slope)); + marker.setLabelFont(new Font("Serif", Font.BOLD, 14)); + marker.setStroke(new BasicStroke(2.0f)); + + // Update the thresholdMarker's value and label on the chart + ValueMarker thresholdMarker = (ValueMarker) plot.getRangeMarkers(Layer.FOREGROUND).iterator().next(); + thresholdMarker.setValue(yClick); + thresholdMarker.setLabel(String.format("Threshold Value: %.2f", yClick)); + thresholdMarker.setLabelFont(new Font("Serif", Font.BOLD, 14)); + thresholdMarker.setStroke(new BasicStroke(2.0f)); + + + // Redraw the chart to reflect changes + repaint(); + e.consume(); + } + }); + } + + public void setChart(JFreeChart chart) { + super.setChart(chart); + setMouseWheelEnabled(false); + setDomainZoomable(false); + setRangeZoomable(false); + setMouseZoomable(false, false); + setFillZoomRectangle(false); + setZoomAroundAnchor(false); + } + + /** + * Finds the index of the closest X value to the given target x. + * + * @param series The series of data points. + * @param x The target x value to match. + * @return The index of the closest x value. + */ + private int findNearestXIndex(XYSeries series, double x) { + double minDistance = Double.MAX_VALUE; + int nearestIndex = -1; + + for (int i = 0; i < series.getItemCount(); i++) { + double distance = Math.abs(series.getX(i).doubleValue() - x); + if (distance < minDistance) { + minDistance = distance; + nearestIndex = i; + } + } + return nearestIndex; + } + + public void updateChart(List values, String title) { + this.selectionChart = createChart(values, title, this); + setChart(this.selectionChart); + revalidate(); + repaint(); + } + + // Set up a marker change listener to handle marker position changes + @Override + public void markerChanged(MarkerChangeEvent event) { + ValueMarker marker = (ValueMarker) event.getMarker(); + float tq = (float) marker.getValue(); + // Calculate the indices for interpolation + int t0 = (int) Math.floor(tq); + int t1 = (int) Math.ceil(tq); + + if (t0 >= 0 && t1 < chart3DPoints.size() && t0 != t1) { + Vector3f interpolatedPoint = interpolate(t0, t1, tq); + parent.update3DModel(interpolatedPoint); + } else if (t0 == t1 && t0 < chart3DPoints.size()) { + Vector3f exactPoint = chart3DPoints.get(t0); + parent.update3DModel(exactPoint); + } + } + + /** + * Interpolates between two 3D points based on a given interpolation parameter. + * + * @param t0 Index of the first point. + * @param t1 Index of the second point. + * @param tq The interpolation parameter, typically derived from the marker's + * position. + * @return The interpolated 3D point. + */ + private Vector3f interpolate(int t0, int t1, float tq) { + Vector3f pt0 = chart3DPoints.get(t0); + Vector3f pt1 = chart3DPoints.get(t1); + + float m0 = (t1 - tq) / (t1 - t0); + float m1 = (tq - t0) / (t1 - t0); + + float x = (float) (m0 * pt0.X + m1 * pt1.X); + float y = (float) (m0 * pt0.Y + m1 * pt1.Y); + float z = (float) (m0 * pt0.Z + m1 * pt1.Z); + + return new Vector3f(x, y, z); + } + + /** + * Creates a chart using a list of values and assigns it a title. Each value in + * the list is plotted against its index. + * + * @param values List of floating-point values for the Y-axis. + * @param title Title of the chart. + * @return A JFreeChart object fully initialized. + */ + private static JFreeChart createChart(List values, String title, MarkerChangeListener markerListener) { + XYSeries series = new XYSeries("Data"); + float maxValue = -Float.MAX_VALUE; + int maxIndex = -1; + + // Populate the series with values and track the maximum value and its index + for (int i = 0; i < values.size(); i++) { + float value = values.get(i); + series.add(i, value); + if (value > maxValue) { + maxValue = value; + maxIndex = i; + } + } + + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(series); + + // Calculate slopes and add to the series + List slopes = calculateSecantSlopes(values); + + XYSeries slopeSeries = new XYSeries("Slopes"); + for (int i = 2; i < values.size(); i++) { + // double slopeIndex = i - 0.5; // offset half an interval, 0.5, from the original values along the x-axis. + double slopeIndex = i -1; + slopeSeries.add(slopeIndex, slopes.get(i-2)); + } + dataset.addSeries(slopeSeries); + + // Create the chart + JFreeChart chart = ChartFactory.createXYLineChart(title, "Index", "Value", dataset, PlotOrientation.VERTICAL, + true, true, false); + + XYPlot plot = chart.getXYPlot(); + plot.setBackgroundPaint(Color.GRAY); + plot.setDomainGridlinePaint(Color.DARK_GRAY); + plot.setRangeGridlinePaint(Color.DARK_GRAY); + + XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + renderer.setSeriesPaint(0, Color.YELLOW); + renderer.setSeriesStroke(0, new BasicStroke(2.0f)); + plot.setRenderer(renderer); + + // Added vertical domianMarker + ValueMarker marker = new ValueMarker(maxIndex); + marker.setPaint(Color.CYAN); + marker.setLabel("Max Value: " + maxValue); + marker.setLabelFont(new Font("Serif", Font.BOLD, 14)); + marker.setLabelPaint(Color.WHITE); + marker.setLabelAnchor(RectangleAnchor.CENTER); + marker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); + marker.setStroke(new BasicStroke(2.0f)); + plot.addDomainMarker(marker); + + // Added horizontal theesholdMarker + float thresholdValue = maxValue / 2; + ValueMarker thresholdMarker = new ValueMarker(thresholdValue); + thresholdMarker.setPaint(Color.CYAN); + thresholdMarker.setLabel("Threshold Value: " + thresholdValue); + thresholdMarker.setLabelFont(new Font("Serif", Font.BOLD, 14)); + thresholdMarker.setLabelPaint(Color.WHITE); + thresholdMarker.setLabelAnchor(RectangleAnchor.CENTER); + thresholdMarker.setLabelTextAnchor(TextAnchor.CENTER_LEFT); + thresholdMarker.setStroke(new BasicStroke(2.0f)); + plot.addRangeMarker(thresholdMarker); + + marker.addChangeListener(markerListener); + + return chart; + } + + /** + * Calculates the slopes between points in the list + * + * @param values List of values for which slopes are to be calculated. + * @return List of calculated slopes. + */ + private static List calculateAdjacentSlopes(List values) { + List slopes = new ArrayList<>(); + for (int i = 1; i < values.size(); i++) { + float slope = (values.get(i) - values.get(i - 1)) / 1.0f; + slopes.add(slope); + } + return slopes; + } + + private static List calculateSecantSlopes(List values) { + List slopes = new ArrayList<>(); + for (int i = 2; i < values.size(); i++) { + float slope = (values.get(i) - values.get(i - 2)) / 1.0f; + slopes.add(slope); + } + return slopes; + } + + + /** + * Sets the 3D points corresponding to the data points in the plot. + * + * @param points List of 3D points to be used in corresponding 3D model updates. + */ + public void setChart3DPoints(List points) { + this.chart3DPoints = points; + } + + /** + * Method to find the next peak. It cycles through values starting from a given + * index to identify a peak where the slope changes sign. + * + * @param index Starting index for the search, ensuring it doesn't start + * before the beginning of the list. + * @param values List of Y-values from the dataset. + * @param threshold Minimum Y-value to consider for a peak. + * @return The index of the next peak if found; otherwise, returns -1. + */ + public float nextPeak(int index, List values, double threshold) { + // Calculate slopes between each pair of points using a secant method. + List slopes = calculateSecantSlopes(values); + // Ensure the starting index is not less than zero + index = Math.max(index, 0); + + // Loop through slopes to find where the sign changes. + for (int j = 0; j < slopes.size() - 1; j++) { + int i = (j + index) % (slopes.size() - 1); + float currentSlope = slopes.get(i); + float nextSlope = slopes.get(i + 1); + float y = values.get(i + 1); + + // Check if current point is a peak by comparing it against the threshold and + // slope changes. + if (y > threshold && currentSlope > 0 && nextSlope < 0) { + // Calculate interpolated index where the slope would cross zero. + float indexX = ((-currentSlope) / (nextSlope - currentSlope)) + i; + System.out.println("here is the indexX:" + indexX); + System.out.println("this is i:" + i); + return indexX + 1; + + } + } + System.out.println("No peak found."); + return -1; + } + + /*** + * Handles action events, specifically looking to handle "Next Peak" actions. + * + * @param e The ActionEvent object containing details about the event. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand() == "Next Peak") { + XYPlot plot = selectionChart.getXYPlot(); + // Retrieve data and convert it into a List for processing + XYSeriesCollection dataset = (XYSeriesCollection) plot.getDataset(); + XYSeries series = dataset.getSeries(0); + double[][] data = series.toArray(); + List values = Arrays.stream(data[1]).mapToObj(d -> Float.valueOf((float) d)) + .collect(Collectors.toList()); + + // Access current marker position and determine the next index to check for a + // peak + ValueMarker marker = (ValueMarker) plot.getDomainMarkers(Layer.FOREGROUND).iterator().next(); + int index = (int) Math.ceil(marker.getValue()) + 1; + + // Retrieve threshold value from the threshold marker + ValueMarker thresholdMarker = (ValueMarker) plot.getRangeMarkers(Layer.FOREGROUND).iterator().next(); + double threshold = thresholdMarker.getValue(); + + // Call the nextPeak method to find the next peak and update the chart + float peakIndex = nextPeak(index, values, threshold); + if (peakIndex != -1) { + System.out.println("Next peak is at index: " + peakIndex); + marker.setValue(peakIndex); + float y = values.get(Math.round(peakIndex)); + marker.setLabel(String.format("Value: %.2f, Derivative: %.2f", y, 0.0f)); + } + } else { + super.actionPerformed(e); + } + } +}