/*========================================================================= Program: FusionViewer Module: $RCSfile: PixelInfoPanel.java,v $ Language: Java Date: $Date: 2007/02/02 19:27:57 $ Version: $Revision: 1.3 $ Copyright (c) Insightful Corporation. All rights reserved. See Copyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ package org.fusionviewer.ui; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import javax.swing.BorderFactory; import javax.swing.JPanel; import org.fusionviewer.display.Image; import org.fusionviewer.display.PixelInfoListener; import org.fusionviewer.io.Env; import org.fusionviewer.model.ImageDisplayModel; /**\class PixelInfoPanel *\brief Displays information about a physical location in a set of images. * The location in millimeters, the location in voxel coordinates, and the value at the voxel. */ public class PixelInfoPanel extends JPanel implements PixelInfoListener, ComponentListener { private String[] m_labels; private String[] m_positions; private String[] m_values; private String m_physicalPosition; private boolean m_noLabels; private Font m_font; private int m_labelWidth; // width of the labels along the left private int m_pixelsWidth; // width of the pixel coordinates private int m_milliWidth; // width of the coordinates in millimeters private int m_valueWidth; // width of the pixel values private static final int LEADING = 4; /** * Initialize the display with the set of names to be used for labeling the images. * * @param names list of names corresponding to the images in an ImageDisplayModel */ public PixelInfoPanel(String[] names) { m_labels = new String[names.length]; m_positions = new String[names.length]; m_values = new String[names.length]; m_noLabels = true; for (int i = 0; i < names.length; i++) { m_labels[i] = new String(names[i]); if (m_labels[i].length() > 0) m_noLabels = false; } setBorder(BorderFactory.createEtchedBorder()); // Listen for resize events addComponentListener(this); } /** * For coordinates x, y, z in millimeters, iterate through the images in model and display the coordinates in * pixels and the pixel value. * * @param model image set * @param x x coordinate * @param y y coordinate * @param z z coordinate */ public void displayPixelInfo(ImageDisplayModel model, float x, float y, float z) { m_physicalPosition = new StringBuffer().append(FloatFormatter.formatFloat(x)).append(" "). append(FloatFormatter.formatFloat(y)).append(" ").append(FloatFormatter.formatFloat(z)).toString(); for (int i = 0; i < m_values.length; i++) { Image image = model.getImage(i); if (image != null) { m_values[i] = FloatFormatter.formatFloat(image.getPixel(x, y, z)); m_positions[i] = new StringBuffer().append(Integer.toString((int) (x / image.getPixelSpacing(0) + 0.5f))).append(" "). append(Integer.toString((int) (y / image.getPixelSpacing(1) + 0.5f))).append(" "). append(Integer.toString((int) (z / image.getPixelSpacing(2) + 0.5f))).toString(); } else { m_values[i] = ""; m_positions[i] = ""; } } repaint(); } /** * Erase the display. */ public void clearPixelInfo() { m_physicalPosition = ""; for (int i = 0; i < m_labels.length; i++) { m_values[i] = ""; m_positions[i] = ""; } repaint(); } /** * Draw this component. * * @param g object to use for drawing */ protected void paintComponent(Graphics g) { createFont(); if (m_font == null) return; // Erase the component g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); g.setColor(getForeground()); // Offset from the left side of the component to start drawing at final int X_OFFSET = 4; // Calculate the size of a line of text FontMetrics metrics = getFontMetrics(m_font); int lineHeight = metrics.getMaxAscent() + metrics.getMaxDescent() + (metrics.getLeading() + LEADING); g.setFont(m_font); // Draw column labels distributed horizontally across the top of the component Rectangle r = new Rectangle(); r.x = m_labelWidth; r.width = m_pixelsWidth; r.y = metrics.getLeading(); r.height = lineHeight; drawCenteredText(g, "pixels", r); r.x += m_pixelsWidth; r.width = m_milliWidth; drawCenteredText(g, "mm", r); r.x += m_milliWidth; r.width = m_valueWidth; drawCenteredText(g, "value", r); // For each row, draw pixel coordinates and voxel values for (int i = 0; i < m_labels.length; i++) { r.y += lineHeight; // Row label if (m_labelWidth > 0) g.drawString(m_labels[i], X_OFFSET, r.y + (metrics.getLeading() + LEADING) + metrics.getMaxAscent()); // Draw pixel coordinates if (m_positions[i] != null) { r.x = m_labelWidth; r.width = m_pixelsWidth; drawCenteredText(g, m_positions[i], r); } // Draw voxel value if (m_values[i] != null) { r.x += m_pixelsWidth + m_milliWidth; r.width = m_valueWidth; drawCenteredText(g, m_values[i], r); } } // Draw coordinates in millimeters centered vertically in the mm column if (m_physicalPosition != null) { r.x = m_labelWidth + m_pixelsWidth; r.y = lineHeight + (m_labels.length * lineHeight / 2) - (lineHeight / 2); r.width = m_milliWidth; drawCenteredText(g, m_physicalPosition, r); } } /* * Draw string s centered horizontally in rectangle r and clipped to r. */ private void drawCenteredText(Graphics g, String s, Rectangle r) { g.setClip(r.x, r.y, r.width, r.height); FontMetrics metrics = getFontMetrics(m_font); g.drawString(s, r.x + r.width / 2 - metrics.stringWidth(s) / 2, r.y + (metrics.getLeading() + LEADING) + metrics.getMaxAscent()); g.setClip(null); } /** * Provide an estimate for how large this component needs to be taking the number of labels into account. * * @return preferred size of this component */ public Dimension getPreferredSize() { createFont(); if (m_font == null) return super.getPreferredSize(); FontMetrics metrics = getFontMetrics(m_font); int lineHeight = metrics.getMaxAscent() + metrics.getMaxDescent() + (metrics.getLeading() + LEADING); int lineWidth = metrics.stringWidth("1234567 1234567 1234567") * 2 + metrics.stringWidth("12345"); int labelWidth = 0; for (int i = 0; i < m_labels.length; i++) { int testWidth = metrics.stringWidth(m_labels[i]); if (testWidth > labelWidth) labelWidth = testWidth; } return new Dimension(labelWidth + lineWidth, lineHeight * (m_labels.length + 1) + LEADING); } /** * Return the minimum screen real estate needed to display all the pixel data. * * @return minimum size of this component */ public Dimension getMinimumSize() { return getPreferredSize(); } /** * Respond to a component resize by recalculating the column widths. * * @param e resize event */ public void componentResized(ComponentEvent e) { createFont(); float LABEL_FRAC = 0.20f; // width of the labels along the left float PIXELS_FRAC = 0.22f; // width of the pixel coordinates float MILLI_FRAC = 0.40f; // width of the coordinates in millimeters float width = (float) getWidth(); if (m_noLabels) { m_labelWidth = 0; PIXELS_FRAC = 1.0f / 3.0f; MILLI_FRAC = 1.0f / 3.0f; } else { m_labelWidth = (int) (width * LABEL_FRAC + 0.5f); } m_pixelsWidth = (int) (width * PIXELS_FRAC + 0.5f); m_milliWidth = (int) (width * MILLI_FRAC + 0.5f); m_valueWidth = (int) (width) - (m_labelWidth + m_pixelsWidth + m_milliWidth) - 1; } /* * Create a font that is 2 point sizes smaller than the default font. */ private void createFont() { if (m_font == null) { Font font = getFont(); if (Env.isMac) m_font = new Font(font.getName(), font.getStyle(), font.getSize() - 2); else m_font = new Font(font.getName(), font.getStyle(), font.getSize()); } } public void componentMoved(ComponentEvent e) { } public void componentShown(ComponentEvent e) { } public void componentHidden(ComponentEvent e) { } }