26 added + 12 removed, total 38 files
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/Cluster.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/Cluster.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,136 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.awt.Point;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The class <code>Cluster</code> represents a seed hit and a collection
- * of additional hits that together form a hit cluster.
- *
- * @author Kyle McCarty
- */
-public class Cluster {
- private final Point seed;
- private ArrayList<Point> hitList = new ArrayList<Point>();
- private ArrayList<Point> shareList = new ArrayList<Point>();
-
- /**
- * <b>Cluster</b><br/><br/>
- * <code>public <b>Cluster</b>(int ix, int iy)</code><br/><br/>
- * Creates a new cluster. All clusters are required to have a seed
- * hit.
- * @param ix - The seed hit's x-coordinate.
- * @param iy - The seed hit's y-coordinate.
- */
- public Cluster(int ix, int iy) { this(new Point(ix, iy)); }
-
- /**
- * <b>Cluster</b><br/><br/>
- * <code>public <b>Cluster</b>(EcalHit seed)</code><br/><br/>
- * Creates a new cluster. All clusters are required to have a seed
- * hit.
- * @param seed - The <code>Point</code> object indicating in which
- * crystal the seed hit occurred.
- */
- public Cluster(Point seed) { this.seed = seed; }
-
- /**
- * <b>addComponentHit</b><br/><br/>
- * <code>public void <b>addComponentHit</b>(int ix, int iy)</code><br/><br/>
- * Adds an <code>Point</code> to the list of this cluster's
- * component hits.
- * @param ix - The component hit's x-coordinate.
- * @param iy - The component hit's y-coordinate.
- */
- public void addComponentHit(int ix, int iy) { hitList.add(new Point(ix, iy)); }
-
- /**
- * <b>addComponentHit</b><br/><br/>
- * <code>public void <b>addComponentHit</b>(Point eHit)</code><br/><br/>
- * Adds an <code>Point</code> to the list of this cluster's
- * component hits.
- * @param eHit - The <code>Point</code> object indicating in which
- * crystal the hit occurred.
- */
- public void addComponentHit(Point eHit) { hitList.add(eHit); }
-
- /**
- * <b>addSharedHit</b><br/><br/>
- * <code>public void <b>addSharedHit</b>(int ix, int iy)</code><br/><br/>
- * Adds an <code>Point</code> to the list of this cluster's shared
- * hits.
- * @param ix - The shared hit's x-coordinate.
- * @param iy - The shared hit's y-coordinate.
- */
- public void addSharedHit(int ix, int iy) { shareList.add(new Point(ix, iy)); }
-
- /**
- * <b>addSharedHit</b><br/><br/>
- * <code>public void <b>addSharedHit</b>(Point eHit)</code><br/><br/>
- * Adds an <code>Point</code> to the list of this cluster's shared
- * hits.
- * @param eHit - The <code>Point</code> object indicating in which
- * crystal the hit occurred.
- */
- public void addSharedHit(Point eHit) { shareList.add(eHit); }
-
- /**
- * <b>getComponentHits</b><br/><br/>
- * <code>public List<Point> <b>getComponentHits</b>()</code><br/><br/>
- * Gets the list of hits that make up the cluster, exempting the
- * seed hit and shared hits.
- * @return Returns the cluster hits as a <code>List</code> object
- * composed of <code>Point</code> objects.
- */
- public List<Point> getComponentHits() { return hitList; }
-
- /**
- * <b>getSharedHits</b><br/><br/>
- * <code>public List<Point> <b>getSharedHits</b>()</code><br/><br/>
- * Gets the list of hits that make up the cluster, exempting the
- * seed hit and component hits.
- * @return Returns the shared hits as a <code>List</code> object
- * composed of <code>Point</code> objects.
- */
- public List<Point> getSharedHits() { return shareList; }
-
- /**
- * <b>getSeedHit</b><br/><br/>
- * <code>public EcalHit <b>getSeedHit</b>()</code><br/><br/>
- * Gets the hit representing the cluster center.
- * @return Returns the cluster center hit as an <code>EcalHit
- * </code>.
- */
- public Point getSeedHit() { return seed; }
-
- /**
- * <b>getHitCount</b><br/><br/>
- * <code>public int <b>getHitCount</b>()</code><br/><br/>
- * Indicates how many total hits compose this cluster. This includes
- * component hits, shared hits, and the seed hit.
- * @return Returns the number of component hits in the cluster
- * as an <code>int</code>.
- */
- public int getHitCount() { return hitList.size() + shareList.size() + 1; }
-
- /**
- * <b>getComponentHitCount</b><br/><br/>
- * <code>public int <b>getComponentHitCount</b>()</code><br/><br/>
- * Indicates how many component hits compose this cluster. Note
- * that this does not include the seed hit or shared hits.
- * @return Returns the number of component hits in the cluster
- * as an <code>int</code>.
- */
- public int getComponentHitCount() { return shareList.size(); }
-
- /**
- * <b>getSharedHitCount</b><br/><br/>
- * <code>public int <b>getSharedHitCount</b>()</code><br/><br/>
- * Indicates how many shared hits compose this cluster. Note that
- * this does not include the seed hit or component hits.
- * @return Returns the number of shared hits in the cluster as an
- * <code>int</code>.
- */
- public int getSharedHitCount() { return hitList.size() + 1; }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ColorScale.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ColorScale.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,138 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.awt.Color;
-
-/**
- * The abstract class <code>ColorScale</code> contains shared methods for all
- * types of color scalers.
- *
- * @author Kyle McCarty
- **/
-public abstract class ColorScale {
- // Indicates if linear or logairthmic scaling should be used.
- protected boolean linear = true;
- // The minimum value for color scaling.
- protected double min = 0;
- // The maximum value for color scaling.
- protected double max = 1;
- // A scale variable used for mapping logarithmic values.
- protected double scale = 1.0;
- // An efficiency variable used for logarithmic mapping.
- protected double lMin = 0.0;
- // An efficiency variable used for logarithmic mapping.
- protected double lMax = 1.0;
-
- /**
- * <b>setMinimum</b><br/><br/>
- * <code>public void <b>setMinimum</b>(double minimum)</code><br/><br/>
- * Sets the value under which no color scaling will be performed.
- * @param minimum - The lowest value for which color scaling will be performed.
- **/
- public void setMinimum(double minimum) {
- min = minimum;
- revalidate();
- }
-
- /**
- * <b>setMaximum</b><br/><br/>
- * <code>public void <b>setMaximum</b>(double maximum)</code><br/><br/>
- * Sets the value over which no color scaling will be performed.
- * @param maximum - The highest value for which color scaling will be performed.
- **/
- public void setMaximum(double maximum) {
- max = maximum;
- revalidate();
- }
-
- /**
- * <b>getMinimum</b><br/><br/>
- * <code>public double <b>getMinimum</b>()</code><br/><br/>
- * Gets the lowest value for which color scaling is performed.
- * @return Returns the minimum value for color scaling as a <code>double</code>.
- **/
- public double getMinimum() { return min; }
-
- /**
- * <b>getMaximum</b><br/><br/>
- * <code>public double <b>getMaximum</b>()</code><br/><br/>
- * Gets the highest value for which color scaling is performed.
- * @return Returns the maximum value for color scaling as a <code>double</code>.
- **/
- public double getMaximum() { return max; }
-
- /**
- * <b>setScalingLinear</b><br/><br/>
- * <code>public void <b>setScalingLinear</b>()</code><br/><br/>
- * Sets the scaling behavior to linear.
- **/
- public void setScalingLinear() {
- linear = true;
- revalidate();
- }
-
- /**
- * <b>isLinearScale</b><br/><br/>
- * <code>public boolean <b>isLinearScale</b>()</code><br/><br/>
- * Indicates whether this color mapping is linear or not.
- * @return Returns <code>true</code> if this is a linear mapping and <code>false
- * </code> otherwise.
- **/
- public boolean isLinearScale() { return linear; }
-
- /**
- * <b>isLogarithmicScale</b><br/><br/>
- * <code>public boolean <b>isLogarithmicScale</b>()</code><br/><br/>
- * Indicates whether this color mapping is logarithmic or not.
- * @return Returns <code>true</code> if this is a logarithmic mapping and <code>
- * false</code> if it is not.
- **/
- public boolean isLogairthmicScale() { return !linear; }
-
- /**
- * <b>setScalingLogarithmic</b><br/><br/>
- * <code>public void <b>setScalingLogarithmic</b>()</code><br/><br/>
- * Sets the scaling behavior to logarithmic.
- **/
- public void setScalingLogarithmic() {
- linear = false;
- revalidate();
- }
-
- /**
- * <b>getColor</b><br/><br/>
- * <code>public Color <b>getColor</b>(double value)</code><br/><br/>
- * Determines the color representing the indicated value.
- * @param value - The value to relate to a color.
- * @return Returns a <code>Color</code> object associated with the argument value.
- **/
- public abstract Color getColor(double value);
-
- /**
- * <b>revalidate</b><br/><br/>
- * <code>protected void <b>revalidate</b>()</code><br/><br/>
- * Makes any necessary changes whenever a critical value is changed.
- **/
- protected void revalidate() {
- // Ensure that the minimum is not zero in the case of log scaling.
- if (!linear && min == 0) {
- if (max < 0.01) { min = max / 100.0; }
- else { min = 0.01; }
- }
-
- // We only need to revalidate if we are using a logarithmic scale.
- if (!linear) {
- // Determine the scaling variable for logarithmic results.
- double temp = min;
- int steps = 0;
- while (temp < 1) {
- temp = temp * 10;
- steps++;
- }
- scale = Math.pow(10, steps);
-
- // Revalidate the logarithmic variables.
- lMax = Math.log10(scale * max);
- lMin = Math.log10(scale * min);
- }
- }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/Datum.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/Datum.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,73 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.awt.Point;
-
-/**
- * The <code>Datum</code> class contains a point representing a crystal in the
- * calorimeter display panel. This is used for storing information when reading
- * from a file. HPS objects should be used when reading from LCIO.
- *
- * @author Kyle McCarty
- **/
-public class Datum {
- // The coordinate on the calorimeter panel.
- protected Point loc;
-
- /**
- * <b>Datum</b><br/><br/>
- * <code>public <b>Datum</b>()</code><br/><br/>
- * Initializes an empty <code>Datum</code>. Note that it will have an
- * invalid coordinate.
- **/
- public Datum() { this(-1, -1); }
-
- /**
- * <b>Datum</b><br/><br/>
- * <code>public <b>Datum</b>(int x, int y)</code><br/><br/>
- * Initializes a new <code>Datum</code> at the indicated coordinate.
- * @param x - The x-coordinate of the object.
- * @param y - The y-coordinate of the object.
- **/
- public Datum(int x, int y) { loc = new Point(x, y); }
-
- /**
- * <b>getX</b><br/><br/>
- * <code>public int <b>getX</b>()</code><br/><br/>
- * Indicates the x-coordinate of the object.
- * @return Returns the x-coordinate as an <code>int</code>.
- **/
- public int getX() { return loc.x; }
-
- /**
- * <b>getY</b><br/><br/>
- * <code>public int <b>getY</b>()</code><br/><br/>
- * Indicates the y-coordinate of the object.
- * @return Returns the y-coordinate as an <code>int</code>.
- **/
- public int getY() { return loc.y; }
-
- /**
- * <b>getLocation</b><br/><br/>
- * <code>public Point <b>getLocation</b>()</code><br/><br/>
- * Indicates the location of the object.
- * @return Returns the object's location as a <code>Point
- * </code> object.
- **/
- public Point getLocation() { return loc; }
-
- /**
- * <b>setX</b><br/><br/>
- * <code>public void <b>setX</b>(int x)</code><br/><br/>
- * Sets the object's x-coordinate.
- * @param x - The new x-coordinate.
- **/
- public void setX(int x) { loc.x = x; }
-
- /**
- * <b>setY</b><br/><br/>
- * <code>public void <b>setY</b>(int y)</code><br/><br/>
- * Sets the obejct's y-coordinate.
- * @param y - The new y-coordinate.
- **/
- public void setY(int y) { loc.y = y; }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/EcalHit.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/EcalHit.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,41 +0,0 @@
-package org.hps.monitoring.ecal;
-
-/**
- * The class <code>EcalHit</code> is an extension of <code>Datum</code>
- * that stores an energy. This is used for reading input from a text
- * file. <code>CalorimeterHit</code> should be used when reading from
- * an LCIO file.
- **/
-public final class EcalHit extends Datum {
- // The (raw) energy of this hit.
- private double energy = 0.0;
-
- /**
- * <b>EcalHit</b><br/><br/>
- * <code>public <b>EcalHit</b>(int x, int y, double energy)</code><br/><br/>
- * Initializes a calorimeter hit object.
- * @param x - The x-coordinate of the hit.
- * @param y - The y-coordinate of the hit.
- * @param energy - The raw energy of the hit.
- **/
- public EcalHit(int x, int y, double energy) {
- super(x, y);
- this.energy = energy;
- }
-
- /**
- * <b>getEnergy</b><br/><br/>
- * <code>public double <b>getEnergy</b>()</code><br/><br/>
- * Indicates the raw energy of this hit.
- * @return Returns the raw energy as a <code>double</code>.
- **/
- public double getEnergy() { return energy; }
-
- /**
- * <b>setEnergy</b><br/><br/>
- * <code>public void <b>setEnergy</b>(double energy)</code><br/><br/>
- * Sets the energy of the hit to the indicated value.
- * @param energy - The new energy of the hit.
- **/
- public void setEnergy(double energy) { this.energy = energy; }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/EcalMonitorDriver.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/EcalMonitorDriver.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,32 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import org.lcsim.event.EventHeader;
-import org.lcsim.util.Driver;
-
-public class EcalMonitorDriver extends Driver {
- private Viewer viewer;
- private String ecalCollectionName = "EcalHits";
- private String clusterCollectionName = "EcalClusters";
-
- public void setEcalCollectionName(String ecalCollectionName) {
- this.ecalCollectionName = ecalCollectionName;
- }
-
- public void setClusterCollectionName(String clusterCollectionName) {
- this.clusterCollectionName = clusterCollectionName;
- }
-
- public void startOfData() {
- viewer = new Viewer();
- viewer.setVisible(true);
- }
-
- public void process(EventHeader event) {
- viewer.displayLCIOEvent(event, ecalCollectionName, clusterCollectionName);
- }
-
- public void endOfData() {
- viewer.setVisible(false);
- viewer = null;
- }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/EcalPanel.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/EcalPanel.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,638 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.FontMetrics;
-import java.awt.Graphics;
-import java.awt.Point;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-
-import javax.swing.JPanel;
-
-/**
- * The class <code>EcalPanel</code> handles the rendering of the calorimeter
- * crystals as well as mapping colors to their values, rendering a color scale,
- * and marking cluster crystals.
- *
- * @author Kyle McCarty
- **/
-public class EcalPanel extends JPanel {
- // Java-suggested variable.
- private static final long serialVersionUID = 6292751227464151897L;
- // The color used for rendering seed hits.
- private Color clusterColor = Color.GREEN;
- // The default color of the calorimeter crystals.
- private Color defaultColor = null;
- // The color-mapping scale used by to color calorimeter crystals.
- private MultiGradientScale scale = MultiGradientScale.makeRainbowScale(0.0, 1.0);
- // The number of boxes in the x-direction.
- private int xBoxes = 1;
- // The number of boxes in the y-direction.
- private int yBoxes = 1;
- // The width of the scale.
- private int scaleWidth = 75;
- // Stores which calorimeter crystals are disabled.
- private boolean[][] disabled;
- // Stores the energies in each calorimeter crystal.
- private double[][] hit;
- // Stores whether a crystal is the location of a seed hit.
- private boolean[][] cluster;
- // Stores what color to highlight the crystal with.
- private Color[][] highlight;
- // The panel on which the scale is rendered.
- private ScalePanel scalePanel = new ScalePanel();
- // Store the size of the panel as of the last refresh.
- private int[] lastSize = new int[2];
-
- // Efficiency variables for crystal placement.
- private int[] widths;
- private int[] heights;
- private int[] xPosition;
- private int[] yPosition;
- private int[] clusterSpace = new int[3];
-
- /**
- * <b>EcalPanel</b><br/><br/>
- * Initializes the calorimeter panel.
- * @param numXBoxes - The number of crystals in the x-direction.
- * @param numYBoxes - The number of crystals in the y-direction.
- **/
- public EcalPanel(int numXBoxes, int numYBoxes) {
- // Initialize the base component.
- super();
-
- // Set the number of calorimeter crystals.
- xBoxes = numXBoxes;
- yBoxes = numYBoxes;
-
- // Initialize data the arrays.
- disabled = new boolean[xBoxes][yBoxes];
- hit = new double[xBoxes][yBoxes];
- cluster = new boolean[xBoxes][yBoxes];
- highlight = new Color[xBoxes][yBoxes];
-
- for(int x = 0; x < xBoxes; x++) {
- for(int y = 0; y < yBoxes; y++) {
- highlight[x][y] = null;
- }
- }
-
- // Initialize the size arrays,
- widths = new int[xBoxes];
- heights = new int[yBoxes];
- xPosition = new int [xBoxes + 1];
- yPosition = new int[yBoxes + 1];
-
- // Add the scale panel.
- setLayout(null);
- add(scalePanel);
- }
-
- /**
- * <b>setCrystalEnabled</b><br/><br/>
- * <code>public void <b>setCrystalEnabled</b>(int xIndex, int yIndex, boolean active)</code><br/><br/>
- * Sets whether the indicated crystal is enabled or not. Invalid indices
- * will be ignored.
- * @param xIndex - The x-coordinate of the crystal.
- * @param yIndex - The y-coordinate of the crystal.
- * @param active - This should be <code>true</code> if the crystal is
- * active and <code>false</code> if it is not.
- * @throws IndexOutOfBoundsException Occurs when the given xy crystal
- * coordinate does not point to a crystal.
- **/
- public void setCrystalEnabled(int xIndex, int yIndex, boolean active) throws IndexOutOfBoundsException {
- if (xIndex >= 0 && xIndex < xBoxes && yIndex >= 0 && yIndex < yBoxes) {
- disabled[xIndex][yIndex] = !active;
- }
- else {
- throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", xIndex, yIndex));
- }
- }
-
- /**
- * <b>addCrystalEnergy</b><br/><br/>
- * <code>public void <b>addCrystalEnergy</b>(int xIndex, int yIndex, double energy)</code><br/><br/>
- * Adds the indicated quantity of energy to the crystal at the given
- * coordinates.
- * @param xIndex - The x-coordinate of the crystal.
- * @param yIndex - The y-coordinate of the crystal.
- * @param energy - The energy to add.
- * @throws IndexOutOfBoundsException Occurs when the given xy crystal
- * coordinate does not point to a crystal.
- **/
- public void addCrystalEnergy(int xIndex, int yIndex, double energy) throws IndexOutOfBoundsException {
- if (xIndex >= 0 && xIndex < xBoxes && yIndex >= 0 && yIndex < yBoxes) {
- this.hit[xIndex][yIndex] += energy;
- }
- else {
- throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", xIndex, yIndex));
- }
- }
-
- /**
- * <b>setCrystalCluster</b><br/><br/>
- * <code>public void <b>setCrystalCluster</b>(int xIndex, int yIndex, boolean cluster)</code><br/><br/>
- * Sets whether a crystal is also the location of a seed hit.
- * @param xIndex - The x-coordinate of the crystal.
- * @param yIndex - The y-coordinate of the crystal.
- * @param cluster - This should be <code>true</code> if there
- * is a seed hit and <code>false</code> if there is not.
- * @throws IndexOutOfBoundsException Occurs when the given xy
- * crystal coordinate does not point to a crystal.
- **/
- public void setCrystalCluster(int xIndex, int yIndex, boolean cluster) throws IndexOutOfBoundsException {
- if (xIndex >= 0 && xIndex < xBoxes && yIndex >= 0 && yIndex < yBoxes) {
- this.cluster[xIndex][yIndex] = cluster;
- }
- else {
- throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", xIndex, yIndex));
- }
- }
-
- /**
- * <b>setCrystalHighlight</b><br/><br/>
- * <code>public void <b>setCrystalHighlight</b>(int xIndex, int yIndex, Color highlight)</code><br/><br/>
- * @param xIndex - The x-coordinate of the crystal.
- * @param yIndex - The y-coordinate of the crystal.
- * @param highlight - The color which the indicated crystal should
- * be highlighted. A value of <code>null</code> indicates that no
- * highlight should be used.
- * @throws IndexOutOfBoundsException Occurs when the given xy
- * crystal coordinate does not point to a crystal.
- */
- public void setCrystalHighlight(int xIndex, int yIndex, Color highlight) throws IndexOutOfBoundsException {
- if (xIndex >= 0 && xIndex < xBoxes && yIndex >= 0 && yIndex < yBoxes) {
- this.highlight[xIndex][yIndex] = highlight;
- }
- else {
- throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", xIndex, yIndex));
- }
- }
-
- /**
- * <b>clearCrystals</b><br/><br/>
- * <code>public void <b>clearCrystals</b>()</code><br/><br/>
- * Sets all crystal energies to zero and removes all clusters. This
- * <b>does not</b> enable disabled crystals.
- **/
- public void clearCrystals() {
- for (int x = 0; x < xBoxes; x++) {
- for (int y = 0; y < yBoxes; y++) {
- hit[x][y] = 0.0;
- cluster[x][y] = false;
- }
- }
- }
-
- /**
- * <b>clearHighlight</b><br/><br/>
- * <code>public void <b>clearHighlight</b>()</code><br/><br/>
- * Clears any highlighting on the crystals.
- */
- public void clearHighlight() {
- for (int x = 0; x < xBoxes; x++) {
- for (int y = 0; y < yBoxes; y++) { highlight[x][y] = null; }
- }
- }
-
- /**
- * <b>setClusterColor</b><br/><br/>
- * <code>public void <b>setClusterColor</b>(Color c)</code><br/><br/>
- * Sets the color of the seed hit marker.
- * @param c - The color to be used for seed hit markers. A value of <code>null
- * </code> will result in seed hit markers being the inverse color of the crystal
- * in which they appear.
- **/
- public void setClusterColor(Color c) { clusterColor = c; }
-
- /**
- * <b>setMinimum</b><br/><br>
- * <code>public void <b>setMinimum</b>(double minimum)</code><br/><br/>
- * Sets the minimum value of the color mapping scale. Energies below this
- * value will all be the same minimum color.
- * @param minimum - The minimum energy to be mapped.
- **/
- public void setMinimum(double minimum) {
- scale.setMinimum(minimum);
- }
-
- /**
- * <b>setMaximum</b><br/><br/>
- * <code>public void <b>setMaximum</b>(double maximum)</code><br/><br/>
- * Sets the maximum value of the color mapping scale. Energies above this
- * value will all be the same maximum color.
- * @param maximum - The maximum energy to be mapped.
- **/
- public void setMaximum(double maximum) {
- scale.setMaximum(maximum);
- }
-
- /**
- * <b>setScalingLinear</b><br/><br/>
- * <code>public void <b>setScalingLinear</b>()<br/><br/>
- * Sets the color mapping scale behavior to linear mapping.
- **/
- public void setScalingLinear() {
- scale.setScalingLinear();
- }
-
- /**
- * <b>setScalingLogarithmic</b><br/><br/>
- * <code>public void <b>setScalingLogarithmic</b>()</code><br/><br/>
- * Sets the color mapping scale behavior to logarithmic mapping.
- **/
- public void setScalingLogarithmic() {
- scale.setScalingLogarithmic();
- }
-
- /**
- * <b>isScalingLinear</b><br/><br/>
- * <code>public void <b>isScalingLinear</b></code>()<br/><br/>
- * Indicates whether the crystal colors are mapped linearly.
- * @return Returns <code>true</code> if the mapping is linear
- * and <code>false</code> otherwise.
- */
- public boolean isScalingLinear() { return scale.isLinearScale(); }
-
- /**
- * <b>isScalingLogarithmic</b><br/><br/>
- * <code>public void <b>isScalingLogarithmic</b></code>()<br/><br/>
- * Indicates whether the crystal colors are mapped logarithmically.
- * @return Returns <code>true</code> if the mapping is logarithmic
- * and <code>false</code> otherwise.
- */
- public boolean isScalingLogarithmic() { return scale.isLogairthmicScale(); }
-
- /**
- * <b>isCluster</b><br/><br/>
- * <code>public boolean <b>isCluster</b></code>()<br/><br/>
- * Determines if the crystal at the given coordinates is a cluster
- * center or not.
- * @param xCoor - The x-coordinate of the crystal.
- * @param yCoor - The y-coordinate of the crystal.
- * @return Returns <code>true</code> if the crystal is a cluster
- * center and <code>false</code> if it is not or if the indices
- * are invalid.
- */
- public boolean isCluster(int xCoor, int yCoor) {
- // If the coordinates are invalid, return false.
- if(!validateIndices(xCoor, yCoor)) { return false; }
-
- // Otherwise, check if it is a cluster.
- else { return cluster[xCoor][yCoor]; }
- }
-
- /**
- * <b>setScaleEnabled</b><br/><br/>
- * <code>public void <b>setScaleEnabled</b>(boolean enabled)</code><br/><br/>
- * Sets whether the scale should be visible or not.
- * @param enabled - <code>true</code> indicates that the scale should
- * be visible and <code>false</code> that it should be hidden.
- **/
- public void setScaleEnabled(boolean enabled) {
- if (scalePanel.isVisible() != enabled) {
- scalePanel.setVisible(enabled);
- }
- }
-
- /**
- * <b>setCrystalDefaultColor</b><br/><br/>
- * <code>public void <b>setCrystalDefaultColor</b>(Color c)</code><br/><br/>
- * Sets the color that crystals with zero energy will display.
- * @param c - The color to use for zero energy crystals. A value
- * of <code>null</code> will use the appropriate energy color
- * map value.
- */
- public void setCrystalDefaultColor(Color c) { defaultColor = c; }
-
- /**
- * <b>getCrystalID</b><br/><br/>
- * <code>public Point <b>getCrystalID</b>(int xCoor, int yCoor)</code><br/><br/>
- * Determines the panel crystal index of the crystal at the given
- * panel coordinates.
- * @param xCoor - The x-coordinate on the panel.
- * @param yCoor - The y-coordinate on the panel.
- * @return Returns a <code>Point</code> object containing the panel
- * crystal indices of the crystal at the given panel coordinates.
- * Returns <code>null</code> if the coordinates do not map to a crystal.
- */
- public Point getCrystalID(int xCoor, int yCoor) {
- // If either coordinate is negative, return the null result.
- if(xCoor < 0 || yCoor < 0) { return null; }
-
- // If either coordinate is too large, return the nul result.
- if(xCoor > xPosition[xBoxes] || yCoor > yPosition[yBoxes]) {
- return null;
- }
-
- // Make a point to identify the crystal index.
- Point loc = new Point(-1, -1);
-
- // Determine which y index it is.
- for(int y = 0; y < yBoxes; y++) {
- if(yCoor <= yPosition[y + 1]) {
- loc.y = y;
- break;
- }
- }
-
- // Determine which x index it is.
- for(int x = 0; x < xBoxes; x++) {
- if(xCoor <= xPosition[x + 1]) {
- loc.x = x;
- break;
- }
- }
-
- // If either coordinate is not valid, return null.
- if(loc.x == -1 || loc.y == -1) { return null; }
-
- // Return the crystal identifier.
- return loc;
- }
-
- /**
- * <b>getCrystalEnergy</b><br/><br/>
- * <code>public double <b>getCrystalEnergy</b>(int ix, int iy)</code><br/><br/>
- * Provides the energy stored in the indicated crystal.
- * @param ix - The crystal's x-index.
- * @param iy - The crystal's y-index.
- * @return Returns the energy as a <code>double</code>.
- * @throws IndexOutOfBoundsException - Occurs when either of the
- * given indices are invalid.
- */
- public double getCrystalEnergy(int ix, int iy) throws IndexOutOfBoundsException {
- if(!validateIndices(ix, iy)) {
- throw new IndexOutOfBoundsException("Invalid crystal index.");
- }
- else { return hit[ix][iy]; }
- }
-
- public Color getCrystalHighlight(int ix, int iy) throws IndexOutOfBoundsException {
- if(!validateIndices(ix, iy)) {
- throw new IndexOutOfBoundsException("Invalid crystal index.");
- }
- else { return highlight[ix][iy]; }
- }
-
- /**
- * <b>redraw</b><br/><br/>
- * <code>public void <b>redraw</b>()</code> Re-renders the calorimeter
- * panel.
- **/
- public void redraw() { super.repaint(); }
-
- public void setSize(Dimension d) { setSize(d.width, d.height); }
-
- public void setSize(int width, int height) {
- super.setSize(width, height);
- scalePanel.setLocation(width - scaleWidth, 0);
- scalePanel.setSize(scaleWidth, height);
- }
-
- protected void paintComponent(Graphics g) {
- // Check to see if the panel has changed sizes since the last
- // time it was rendered.
- boolean sizeChanged = false;
- if(getWidth() != lastSize[0] || getHeight() != lastSize[1]) {
- lastSize[0] = getWidth();
- lastSize[1] = getHeight();
- sizeChanged = true;
- }
-
- // If the size of the panel has changed, we need to update
- // the crystal locations.
- if (sizeChanged) {
- // Determine the width and heights of the calorimeter crystals.
- int width;
- if (scalePanel.isVisible()) { width = getWidth() - scaleWidth; }
- else { width = getWidth(); }
- int height = getHeight();
-
- int boxWidth = width / xBoxes;
- int widthRem = width % xBoxes;
- int boxHeight = height / yBoxes;
- int heightRem = height % yBoxes;
-
- // Store the widths for each crystal.
- for(int x = 0; x < xBoxes; x++) {
- widths[x] = boxWidth;
- if(widthRem > 0) {
- widths[x]++;
- widthRem--;
- }
- xPosition[x + 1] = xPosition[x] + widths[x];
- }
-
- // Store the height for each crystal.
- for(int y = 0; y < yBoxes; y++) {
- heights[y] = boxHeight;
- if(heightRem > 0) {
- heights[y]++;
- heightRem--;
- }
- yPosition[y + 1] = yPosition[y] + heights[y];
- }
-
- // Calculate the cluster position variables.
- double ltw = 0.25 * boxWidth;
- double lth = 0.25 * boxHeight;
-
- if(ltw > lth) {
- clusterSpace[0] = (int)Math.round((boxWidth - lth - lth) / 2.0);
- clusterSpace[1] = (int)Math.round(lth);
- clusterSpace[2] = (int)Math.round(lth + lth);
-
- }
- else {
- clusterSpace[0] = (int)Math.round(ltw);
- clusterSpace[1] = (int)Math.round((boxHeight - ltw - ltw) / 2.0);
- clusterSpace[2] = (int)Math.round(ltw + ltw);
-
- }
- }
-
- // Render the crystals at the locations calculated in the size
- // change block.
- for (int x = 0; x < xBoxes; x++) {
- for (int y = 0; y < yBoxes; y++) {
- // Determine the appropriate color for the box.
- Color crystalColor;
- if (disabled[x][y]) { crystalColor = Color.BLACK; }
- else if(defaultColor != null && hit[x][y] == 0) { crystalColor = defaultColor; }
- else { crystalColor = scale.getColor(hit[x][y]); }
- g.setColor(crystalColor);
-
- // Draw the crystal energy color.
- g.fillRect(xPosition[x], yPosition[y], widths[x], heights[y]);
-
- // Draw the crystal border.
- g.setColor(Color.BLACK);
- g.drawRect(xPosition[x], yPosition[y], widths[x] - 1, heights[y] - 1);
-
- // Draw a highlight, if needed.
- if(highlight[x][y] != null && !disabled[x][y]) {
- g.setColor(highlight[x][y]);
- g.drawRect(xPosition[x] + 1, yPosition[y] + 1, widths[x] - 3, heights[y] - 3);
- }
-
- // If there is a cluster, draw a circle.
- if (cluster[x][y]) {
- // Get the appropriate cluster color.
- Color c;
- if (clusterColor == null) {
- int red = Math.abs(255 - crystalColor.getRed());
- int blue = Math.abs(255 - crystalColor.getBlue());
- int green = Math.abs(255 - crystalColor.getGreen());
- c = new Color(red, green, blue);
- }
- else { c = clusterColor; }
-
- // Draw an circle on the cluster crystal.
- g.setColor(c);
- g.fillOval(xPosition[x] + clusterSpace[0], yPosition[y] + clusterSpace[1],
- clusterSpace[2], clusterSpace[2]);
- }
- }
- }
- }
-
- /**
- * <b>validateIndices</b><br/><br/>
- * <code>private boolean <b>validateIndices</b>(int ix, int iy)</code><br/><br/>
- * Indicates whether the given indices corresponds to a valid
- * crystal or not.
- * @param ix - The crystal's x index.
- * @param iy - The crystal's y index.
- * @return Returns <code>true</code> if the indices are valid
- * and <code>false</code> if they are not.
- */
- private boolean validateIndices(int ix, int iy) {
- boolean lowX = (ix > -1);
- boolean highX = (ix < xBoxes);
- boolean lowY = (iy > -1);
- boolean highY = (iy < yBoxes);
-
- return (lowX && highX && lowY && highY);
- }
-
- /**
- * The local class <b>ScalePanel</b> renders the scale for the calorimeter
- * color map.
- **/
- private class ScalePanel extends JPanel {
- private static final long serialVersionUID = -2644562244208528609L;
-
- protected void paintComponent(Graphics g) {
- // Set the text region width.
- int textWidth = 45;
- boolean useText;
-
- // Store height and width.
- int height = getHeight();
- int width;
- if (getWidth() > textWidth) {
- width = getWidth() - textWidth;
- useText = true;
- }
- else {
- width = getWidth();
- useText = false;
- }
-
- // Define the step size for the scale. This will differ depending
- // on whether we employ a linear or logarithmic scale.
- double step;
- double curValue;
- boolean linear = scale.isLinearScale();
- if (linear) {
- step = (scale.getMaximum() - scale.getMinimum()) / height;
- curValue = scale.getMinimum();
- }
- else {
- double max = Math.log10(scale.getMaximum());
- double min = Math.log10(scale.getMinimum());
- step = (max - min) / height;
- curValue = min;
- }
-
- // Color the text area.
- g.setColor(Color.BLACK);
- g.drawRect(0, 0, width, height);
- g.drawRect(1, 1, width - 1, height - 1);
- g.fillRect(width, 0, textWidth, height);
-
- // Render the scale.
- int sy = height;
- int[] sx = { 0, width };
- for (int i = 0; i <= height; i++) {
- // Get the appropriate value for the current pixel.
- double scaledValue;
- if (linear) { scaledValue = curValue; }
- else { scaledValue = Math.pow(10, curValue); }
- g.setColor(scale.getColor(scaledValue));
-
- // Draw a line.
- g.drawLine(sx[0], sy, sx[1], sy);
-
- // Update the spacing variables.
- curValue += step;
- sy--;
- }
-
- // Generate the scale text.
- if (useText) {
- // Determine the spacing of the text.
- FontMetrics fm = g.getFontMetrics(g.getFont());
- int fontHeight = fm.getHeight();
-
- // Populate the first and last values.
- NumberFormat nf = new DecimalFormat("0.#E0");
- g.setColor(Color.WHITE);
- g.drawString(nf.format(scale.getMaximum()), width + 5, fontHeight);
- g.drawString(nf.format(scale.getMinimum()), width + 5, height - 3);
-
- // Calculate text placement variables.
- double heightAvailable = height - 2.0 * fontHeight;
- double heightDefault = heightAvailable / (1.5 * fontHeight);
- int num = (int) Math.floor(heightAvailable / heightDefault);
- double heightRemainder = heightAvailable - (num * heightDefault);
- double heightExtra = heightRemainder / num;
- double lSpacing = heightDefault + heightExtra;
- double lHalfSpacing = lSpacing / 2.0;
- int lHeight = fontHeight + 3;
- int[] lX = { width - 4, width, width + 5 };
- int lShift = (int) (fontHeight * 0.25 + lHalfSpacing);
- double lTemp = 0.0;
-
- // Calculate value conversion variables.
- double lMin = scale.getMinimum();
- double lScale;
- if (linear) {
- lMin = scale.getMinimum();
- lScale = scale.getMaximum() - scale.getMinimum();
- }
- else {
- double min = Math.log10(scale.getMinimum());
- double max = Math.log10(scale.getMaximum());
- lMin = min;
- lScale = max - min;
- }
-
- // Write the labels.
- for (int i = 0; i < num; i++) {
- g.setColor(Color.BLACK);
- int h = (int) (lHeight + lHalfSpacing);
- g.drawLine(lX[0], h, lX[1], h);
- g.setColor(Color.WHITE);
- double lVal = lMin + (1.0 - ((double) h / height)) * lScale;
- if (!linear) { lVal = Math.pow(10, lVal); }
- g.drawString(nf.format(lVal), lX[2], lHeight + lShift);
- lTemp += lSpacing;
- lHeight = (int) (fontHeight + lTemp);
- }
- }
- }
- }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/EventManager.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/EventManager.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,159 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.StringTokenizer;
-
-/**
- * The class <code>EventManager</code> handles loading hits and clusters from a
- * text file to populate the calorimeter panel. Input should be of the form
- * Event
- * Indicates the start of a new event.
- *
- * EcalHit [X] [Y] [Energy]
- * Represents a calorimeter hit at coordinates ([X], [Y]) and with energy
- * [Energy]. Coordinates should be in calorimeter form (x = [-23, 23] and
- * y = [-5, 5]) and must integers. Energy can be a decimal value. Brackets
- * should not be included in the line.
- *
- * Cluster [X] [Y]
- * Represents the location of a cluster at coordinates ([X], [Y]). Brackets
- * should not be included in the line.
- *
- * @author Kyle McCarty
- **/
-public class EventManager {
- // File readers for reading the input.
- private FileReader fr;
- private BufferedReader reader;
- // List for storing the hits from the current event.
- private ArrayList<EcalHit> hitList = new ArrayList<EcalHit>();
- // List for storing the clusters from the current hit.
- private ArrayList<Cluster> clusterList = new ArrayList<Cluster>();
- // Whether the event manager has an open file.
- private boolean open = true;
-
- /**
- * <b>EventManager</b><br/><br/>
- * <code>public <b>EventManager</b>(String filename)</code><br/><br/>
- * Initializes an event manager that will read from the indicated file.
- * @param filename - The path to the file containing hit information.
- **/
- public EventManager(String filename) throws IOException {
- fr = new FileReader(filename);
- reader = new BufferedReader(fr);
- }
-
- /**
- * <b>readEvent</b><br/><br/>
- * <code>public boolean <b>readEvent</b>()</code><br/><br/>
- * Populates the event manager with hits and clusters from the next event.
- * @return Returns <code>true</code> if an event was read and <code>false
- * </code> if it was not.
- **/
- public boolean readEvent() throws IOException {
- // We can only read of the reader is open.
- if (!open) { return false; }
-
- // Clear the data lists.
- hitList.clear();
- clusterList.clear();
-
- // Store the current line.
- String curLine = reader.readLine();
-
- // Keep sorting until we hit a null or an event header.
- while (curLine != null && curLine.compareTo("Event") != 0) {
- curLine = reader.readLine();
- }
-
- // If we hit a null, we are at the end of the file.
- if (curLine == null) { return false; }
-
- // Otherwise, we have read an event header and must populate
- // the data lists.
- curLine = reader.readLine();
- while (curLine != null && curLine.compareTo("Event") != 0) {
- // Break apart the line.
- StringTokenizer st = new StringTokenizer(curLine);
- String name = st.nextToken();
- int ix = Integer.parseInt(st.nextToken());
- int iy = Integer.parseInt(st.nextToken());
-
- // If this is a cluster, add a new cluster object.
- if (name.compareTo("Cluster") == 0) { clusterList.add(new Cluster(ix, iy)); }
-
- // If this is a calorimeter hit, add a new calorimeter hit object.
- else if (name.compareTo("EcalHit") == 0) {
- double energy = Double.parseDouble(st.nextToken());
- hitList.add(new EcalHit(ix, iy, energy));
- }
-
- // If this is a cluster component hit, add it to the last cluster.
- else if(name.compareTo("CompHit") == 0) {
- // There must be a last cluster to process this hit type.
- if(clusterList.size() == 0) {
- System.err.println("File Format Error: A cluster component hit was read, but" +
- " no cluster has been declared. Terminating.");
- System.exit(1);
- }
- else { clusterList.get(clusterList.size() - 1).addComponentHit(ix, iy); }
- }
-
- // If this is a cluster shared hit, add it to the last cluster.
- else if(name.compareTo("SharHit") == 0) {
- // There must be a last cluster to process this hit type.
- if(clusterList.size() == 0) {
- System.err.println("File Format Error: A cluster shared hit was read, but" +
- " no cluster has been declared. Terminating.");
- System.exit(1);
- }
- else { clusterList.get(clusterList.size() - 1).addSharedHit(ix, iy); }
- }
-
- // Get the next line.
- curLine = reader.readLine();
- }
-
- // Indicate that an event was processed.
- return true;
- }
-
- /**
- * <b>close</b><br/><br/>
- * <code>public void <b>close</b>()</code><br/><br/>
- * Closes the event manager. Once this is performed, no additional events
- * may be read.
- * @throws IOException Occurs if there is an error closing the file stream.
- **/
- public void close() throws IOException {
- reader.close();
- fr.close();
- open = false;
- }
-
- /**
- * <b>getHits</b><br/><br/>
- * <code>public ArrayList<EcalHit> <b>getHits</b>()</code><br/><br/>
- * Allows access to the current event's list of hits.
- * @return Returns the current hits as an <code>ArrayList</code> object.
- **/
- public ArrayList<EcalHit> getHits() {
- if (!open) { return null; }
- else { return hitList; }
- }
-
- /**
- * <b>getClusters</b><br/><br/>
- * <code>public ArrayList<Cluster> <b>getClusters</b></code><br/><br/>
- * Allows access to the current event's list of clusters.
- * @return Returns the current clusters as an <code>ArrayList
- * </code> object.
- **/
- public ArrayList<Cluster> getClusters() {
- if (!open) { return null; }
- else { return clusterList; }
- }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/GradientScale.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/GradientScale.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,119 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.awt.Color;
-
-/**
- * The class <code>GradientScale</code> is an implementation of the abstract
- * class <code>ColorScale</code> which represents a simple gradient from one
- * color to another. It will map any argument value that exceeds its maximum
- * value to the hot color and any argument value that is below its minimum value
- * to its cold color. All other argument values will be mapped somewhere between
- * the cold and hot colors using either a linear or logarithmic scale.
- *
- * @author Kyle McCarty
- **/
-public final class GradientScale extends ColorScale {
- // The color associated with the maximum value.
- private Color hotColor = Color.WHITE;
- // The color associated with the minimum value.
- private Color coldColor = Color.BLACK;
- // Efficiency variable holding the rgb difference between the two
- // colors. This is used to prevent recalculation when mapping values.
- private int[] drgb = { 255, 255, 255 };
-
- /**
- * <b>setHotColor</b><br/><br/>
- * <code>public void <b>setHotColor</b>(Color c)</code><br/><br/>
- * Sets the color associated with the maximum value.
- * @param c - The new color to use.
- **/
- public void setHotColor(Color c) {
- hotColor = c;
- revalidateColor();
- }
-
- /**
- * <b>setColdColor</b><br/><br/>
- * <code>public void <b>setColdColor</b>(Color c)</code><br/><br/>
- * Sets the color assocaited with the minimum value.
- * @param c - The color to use.
- **/
- public void setColdColor(Color c) {
- coldColor = c;
- revalidateColor();
- }
-
- public Color getColor(double value) {
- // If the value is less than the minimum, return the cold color.
- if (value < min) { return coldColor; }
-
- // If the value is greater than the maximum, return the hot color.
- if (value > max) { return hotColor; }
-
- // Otherwise, calculate how far along the gradient the value is.
- double percent;
- if (linear) { percent = (value - min) / (max - min); }
- else {
- double lValue = Math.log10(scale * value);
- percent = (lValue - lMin) / (lMax - lMin);
- }
-
- // Scale the color.
- int dr = (int) Math.round(percent * drgb[0]);
- int dg = (int) Math.round(percent * drgb[1]);
- int db = (int) Math.round(percent * drgb[2]);
-
- // Return the result.
- return new Color(coldColor.getRed() + dr, coldColor.getGreen() + dg, coldColor.getBlue() + db);
- }
-
- /**
- * <b>revalidateColor</b><br/><br/>
- * <code>private void <b>revalidateColor</b>()</code><br/><br/>
- * Calculates the differences between the hot and cold colors and sets the
- * class related class variables.
- **/
- private void revalidateColor() {
- drgb[0] = hotColor.getRed() - coldColor.getRed();
- drgb[1] = hotColor.getGreen() - coldColor.getGreen();
- drgb[2] = hotColor.getBlue() - coldColor.getBlue();
- }
-
- /**
- * <b>makeGreyScale</b><br/><br/>
- * <code>public static GradientScale <b>makeGreyScale</b>(double minimum, double maximum)</code><br/><br/>
- * Creates a color scale that ranges from black (cold) to white (hot) with
- * the indicated maximum and minimum.
- * @param minimum - The lowest value for color scaling.
- * @param maximum - The highest value for color scaling.
- * @return Returns a <code>GradientScale</code> that maps to grey scale over
- * the indicated range.
- **/
- public static GradientScale makeGreyScale(double minimum, double maximum) {
- GradientScale gs = new GradientScale();
- gs.setMinimum(minimum);
- gs.setMaximum(maximum);
-
- return gs;
- }
-
- /**
- * <b>makeHeatScale</b><br/><br>
- * <code>public static GradientScale <b>makeHeatScale</b>(double minimum, double maximum)</code><br/><br/>
- * Creates a color scale that ranges from black (cold) to red (hot) with the
- * indicated maximum and minimum.
- * @param minimum - The lowest value for color scaling.
- * @param maximum - The highest value for color scaling.
- * @return Returns a <code>GradientScale</code> that maps to a black- to-red
- * gradient over the indicated range.
- **/
- public static GradientScale makeHeatScale(double minimum, double maximum) {
- GradientScale hs = new GradientScale();
- hs.setHotColor(Color.RED);
- hs.setColdColor(Color.BLACK);
- hs.setMinimum(minimum);
- hs.setMaximum(maximum);
-
- return hs;
- }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/Main.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/Main.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,87 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.awt.GraphicsDevice;
-import java.awt.GraphicsEnvironment;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.Random;
-
-/**
- * The class <code>Main</code> can be used to create an event display that
- * reads from file. By default it reads from "cluster-hit.txt" at the class
- * path root. This can be changed by altering the line<br/>
- * <code>window.setDataSource("cluster-hit.txt")</code><br/>
- **/
-public class Main {
- private static final Viewer window = new Viewer();
-
- public static void main(String[] args) throws IOException {
- // Get screen size of primary monitor
- GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
- int screenWidth = gd.getDisplayMode().getWidth();
- int screenHeight = gd.getDisplayMode().getHeight();
-
- // Set the viewer location and make it visible
- window.setLocation((screenWidth - window.getPreferredSize().width) / 2,
- (screenHeight - window.getPreferredSize().height) / 2);
- window.setDataSource("cluster-hit.txt");
- window.displayNextEvent();
-
- /**
- int key = 0;
- while((key = System.in.read()) != 10) { }
- **/
- window.setVisible(true);
- }
-
- static void makeData() {
- // Generate a random test input file.
- Random rng = new Random();
- try {
- // Make a file writer to write the results.
- FileWriter writer = new FileWriter("cluster-hit.txt");
-
- // Make 10 - 100 events.
- int events = 10 + rng.nextInt(91);
-
- // For each events, generate some data.
- for (int e = 0; e < events; e++) {
- // Write the event header.
- writer.append("Event\n");
-
- // Make 3 - 15 hits.
- int hits = 3 + rng.nextInt(13);
- for (int h = 0; h < hits; h++) {
- // Write identifier.
- writer.append("EcalHit\t");
-
- // Make a random address.
- // x = [0, 46); y = [0, 11)
- int ix = rng.nextInt(46);
- int iy = rng.nextInt(11);
- writer.append(ix + "\t" + iy + "\n");
- }
-
- // Make 0 - 4 clusters.
- int clusters = rng.nextInt(5);
- for (int c = 0; c < clusters; c++) {
- // Write identifier.
- writer.append("Cluster\t");
-
- // Make a random address.
- // x = [0, 46); y = [0, 11)
- int ix = rng.nextInt(46);
- int iy = rng.nextInt(11);
- writer.append(ix + "\t" + iy + "\n");
- }
- }
-
- // Close the writer.
- writer.close();
- }
- catch (IOException e) {
- System.err.println(e.getStackTrace());
- System.exit(1);
- }
- }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/MultiGradientScale.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/MultiGradientScale.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,148 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.awt.Color;
-import java.util.ArrayList;
-
-/**
- * The class <code>MultiGradientScale</code> is an implementation of
- * <code>ColorScale</code> that maps values to a color over several different
- * individual <code>GradientScale</code> objects to allow for multi-color
- * mapping.
- *
- * @author Kyle McCarty
- **/
-public final class MultiGradientScale extends ColorScale {
- // Stores the colors in the map.
- private ArrayList<Color> colorList = new ArrayList<Color>();
- // Stores the component mapping scales.
- private ArrayList<GradientScale> scaleList = new ArrayList<GradientScale>();
-
- /**
- * <b>addColor</b><br/><br/>
- * <code>public void <b>addColor</b>(Color c)</code><br/><br/>
- * Adds a new color to the mapping scale. The first color will be the
- * coldest with subsequent colors being more and more hot.
- * @param c - The color to add.
- **/
- public void addColor(Color c) {
- colorList.add(c);
- revalidate();
- }
-
- /**
- * <b>removeColor</b><br/><br/>
- * <code>public boolean <b>removeColor</b>(int colorIndex)</code><br/><br/>
- * Removes the nth color from the mapping scale.
- * @param colorIndex - The index of the color to be removed.
- * @return Returns <code>true</code> if the color was removed and
- * <code>false</code> if it was not.
- **/
- public boolean removeColor(int colorIndex) {
- // Only remove the value if the index is valid.
- if (colorIndex >= 0 && colorIndex < colorList.size()) {
- colorList.remove(colorIndex);
- revalidate();
- return true;
- }
- else { return false; }
- }
-
- public Color getColor(double value) {
- // Get the number of colors and scales.
- int colors = colorList.size();
- int scales = scaleList.size();
-
- // If there are no colors or scales, give black.
- if (colors == 0 && scales == 0) { return Color.BLACK; }
-
- // If there are no scales, but there is a color, give that.
- if (scales == 0 && colors == 1) { return colorList.get(0); }
-
- // Scale the value if logarithmic.
- double sValue;
- if (linear) { sValue = value; }
- else { sValue = Math.log10(scale * value); }
-
- // Otherwise, determine which scale should get the value.
- for (GradientScale s : scaleList) {
- if (sValue < s.getMaximum()) {
- return s.getColor(sValue);
- }
- }
-
- // If it didn'tappear in the list, it is the hottest color.
- return colorList.get(colors - 1);
- }
-
- protected void revalidate() {
- // Handle the default logarithmic revalidation.
- super.revalidate();
-
- // Redistribute the lists.
- scaleList.clear();
-
- // We need at least colors to make a scale - otherwise, the
- // special cases handle the color.
- int colors = colorList.size();
- if (colors < 2) { return; }
-
- // Otherwise, define the list variables.
- double sStep;
- double sMin;
- if (linear) {
- sStep = (max - min) / (colors - 1);
- sMin = min;
- }
- else {
- sStep = (lMax - lMin) / (colors - 1);
- sMin = lMin;
- }
- double sMax = sMin + sStep;
-
- // Generate a list of scales.
- for (int i = 0; i < (colors - 1); i++) {
- // Make and add a scale.
- GradientScale s = new GradientScale();
- s.setMinimum(sMin);
- s.setMaximum(sMax);
- s.setColdColor(colorList.get(i));
- s.setHotColor(colorList.get(i + 1));
- scaleList.add(s);
-
- // Update the min/max.
- sMin = sMax;
- sMax += sStep;
- }
- }
-
- /**
- * <b>makeRainboowScale</b><br/><br/>
- * <code>public static <b>makeRainbowScale</b>(double minimum, double maximum)</code><br/><br>
- * Creates a <code>MultiGradientScale</code> that maps values from purple,
- * to blue, to cyan, to green, to yellow, and to red at the hottest.
- * @param minimum - The lowet mapped value.
- * @param maximum - The highest mapped value.
- * @return Returns the rainbow color mapping scale.
- **/
- public static MultiGradientScale makeRainbowScale(double minimum, double maximum) {
- int str = 165;
- Color purple = new Color(55, 0, 55);
- Color blue = new Color(0, 0, str);
- Color cyan = new Color(0, str, str);
- Color green = new Color(0, str, 0);
- Color yellow = new Color(str, str, 0);
- Color red = new Color(str, 0, 0);
-
- MultiGradientScale mgs = new MultiGradientScale();
- mgs.addColor(purple);
- mgs.addColor(blue);
- mgs.addColor(cyan);
- mgs.addColor(green);
- mgs.addColor(yellow);
- mgs.addColor(red);
- mgs.setMinimum(minimum);
- mgs.setMaximum(maximum);
-
- return mgs;
- }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/StatusPanel.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/StatusPanel.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,170 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.Graphics;
-
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-
-/**
- * Class <code>StatusPanel</code> displays text in a set of fields.
- *
- *@author Kyle McCarty
- */
-public class StatusPanel extends JPanel {
- private static final long serialVersionUID = -8353479383875379010L;
- private int leftBuffer = 10;
- private int upperBuffer = 10;
- private BackPanel background = new BackPanel();
- private JLabel[][] field;
- private static final String nullValue = "---";
-
- /**
- * <b>StatusPanel</b><br/><br/>
- * <code>public <b>StatusPanel</b>(String... fieldName)</code><br/><br/>
- * Creates a new status panel with display fields with the indicated
- * names. They will be assigned a field index in the order that they
- * are given starting with zero.
- * @param fieldName - The names of the fields to display.
- */
- public StatusPanel(String... fieldName) {
- // Initialize the component.
- super();
-
- // Set the layout manager to manual.
- setLayout(null);
-
- // Build the text fields.
- int curZ = 0;
- field = new JLabel[fieldName.length][2];
- for(int i = 0; i < field.length; i++) {
- for(int j = 0; j < field[i].length; j++) {
- field[i][j] = new JLabel();
- field[i][j].setOpaque(true);
- field[i][j].setBackground(Color.WHITE);
- add(field[i][j]);
- setComponentZOrder(field[i][j], curZ);
- curZ++;
- }
- field[i][0].setText(fieldName[i] + ":");
- }
-
- // Start the fields as null by default.
- clearValues();
-
- // Build the background panel.
- add(background);
- setComponentZOrder(background, curZ);
- }
-
- /**
- * <b>setFieldValue</b><br/><br/>
- * Sets the value of the indicated field.
- * @param index - The field's index.
- * @param value - The new value to display.
- * @throws IndexOutOfBoundsException Occurs when the field index
- * is neither more than the existing number of fields or is negative.
- */
- public void setFieldValue(int index, String value) throws IndexOutOfBoundsException {
- if(index >= 0 && index < field.length) {
- if(value == null) { field[index][1].setText(nullValue); }
- else { field[index][1].setText(value); }
- }
- else { throw new IndexOutOfBoundsException("Invalid field index."); }
- }
-
- public void setSize(int width, int height) {
- super.setSize(width, height);
- resize();
- }
-
- public void setSize(Dimension d) {
- super.setSize(d);
- resize();
- }
-
- /**
- * <b>clearValues</b><br/><br/>
- * <code>public void <b>clearValues</b>()</code><br/><br/>
- * Sets all of the fields on the status display to the null value.
- */
- public void clearValues() {
- for(int i = 0; i < field.length; i++) {
- field[i][1].setText(nullValue);
- }
- }
-
- private void resize() {
- // Define the width an height as convenience variables.
- int width = getWidth();
- int height = getHeight();
-
- // Size the background panel.
- background.setBounds(0, 0, width, height);
-
- // Size and place the text labels.
- if(field.length != 0) {
- int labelHeight = (height - (int)(upperBuffer + 5)) / field.length;
- int labelRem = (height - upperBuffer - 8) % field.length;
- int curY = (int)(upperBuffer + 2);
- for(int i = 0; i < field.length; i++) {
- // Determine the appropriate field height.
- int thisHeight = labelHeight;
- if(labelRem > 0) {
- thisHeight++;
- labelRem--;
- }
-
- // Place the field.
- field[i][0].setBounds(leftBuffer, curY, 65, thisHeight);
- field[i][1].setBounds(getNextX(field[i][0]), curY, 100, thisHeight);
-
- // Increment the current position.
- curY += thisHeight;
- }
- }
- }
-
- /**
- *<b>getNextX</b><br/><br/>
- * <code>private int <b>getNextX</b>(Component c)</code><br/><br/>
- * Finds the x-coordinate immediately after the component.
- * @param c - The component of which to find the end.
- * @return Returns the x-coordinate at the end of the component.
- */
- private final static int getNextX(Component c) { return getNextX(c, 0); }
-
- /**
- /**
- *<b>getNextX</b><br/><br/>
- * <code>private int <b>getNextX</b>(Component c, int buffer)</code><br/><br/>
- * Finds the x-coordinate after the component with a given buffer.
- * @param c - The component of which to find the end.
- * @param buffer - The extra space after the component to be included.
- * @return Returns the x-coordinate at the end of the component,
- * with a buffer length.
- */
- private final static int getNextX(Component c, int buffer) {
- return c.getX() + c.getWidth() + buffer;
- }
-
- /**
- * Class <code>BackPanel</code> simply renders the background panel
- * for the status panel.
- */
- private class BackPanel extends JPanel {
- private static final long serialVersionUID = 4997805650267243080L;
-
- public void paint(Graphics g) {
- // Render the panel background.
- g.setColor(Color.WHITE);
- g.fillRect(0, upperBuffer, getWidth(), getHeight() - upperBuffer);
- g.setColor(Color.GRAY);
- g.drawRect(0, upperBuffer, getWidth() - 1, getHeight() - upperBuffer - 1);
- g.setColor(Color.LIGHT_GRAY);
- g.drawRect(1, upperBuffer + 1, getWidth() - 3, getHeight() - upperBuffer - 3);
- }
- }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/Viewer.java 2014-03-17 19:07:10 UTC (rev 306)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/Viewer.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -1,458 +0,0 @@
-package org.hps.monitoring.ecal;
-
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Point;
-import java.awt.event.ComponentEvent;
-import java.awt.event.ComponentListener;
-import java.awt.event.KeyEvent;
-import java.awt.event.KeyListener;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.awt.event.MouseMotionListener;
-import java.io.IOException;
-import java.text.DecimalFormat;
-import java.util.HashMap;
-import java.util.List;
-
-import javax.swing.JFrame;
-
-import org.lcsim.event.EventHeader;
-import org.lcsim.hps.recon.ecal.HPSCalorimeterHit;
-import org.lcsim.hps.recon.ecal.HPSEcalCluster;
-
-/**
- * The class <code>Viewer</code> handles initialization of the calorimeter panel
- * with the proper settings, provides a window for it to live in, and feeds it
- * events.
- *
- * @author Kyle McCarty
- **/
-public class Viewer extends JFrame {
- // Java-suggested variable.
- private static final long serialVersionUID = -2022819652687941812L;
- // The calorimeter panel.
- private final EcalPanel ecalPanel = new EcalPanel(46, 11);
- // The crystal status panel.
- private final StatusPanel statusPanel = new StatusPanel("x Index", "y Index", "Energy");
- // The event data reader.
- private EventManager em = null;
- // Map the cluster location to the cluster object.
- private HashMap<Point, Cluster> clusterMap = new HashMap<Point, Cluster>();
- // Whether crystal should be highlighted when hovered over.
- private boolean highlight = true;
- // Store the last crystal to be highlighted.
- private Point lastCrystal = null;
- // DEPRECATED :: Store the old highlight color.
- //private Color oldHighlight = null;
- // DEPRECATED :: Store the currently highlighted cluster.
- //private Point lastCluster = null;
- // Stores whether the background color is set or not.
- private boolean background = false;
- // Define the highlight color for dark backgrounds.
- private static final Color HIGHLIGHT_CURSOR_DARK = new Color(90, 170, 250);
- // Define the highlight color for light backgrounds.
- private static final Color HIGHLIGHT_CURSOR_LIGHT = Color.BLUE;
- // Define the highlight color for cluster component hits.
- private static final Color HIGHLIGHT_CLUSTER_COMPONENT = Color.RED;
- // Define the highlight color for cluster shared hits.
- private static final Color HIGHLIGHT_CLUSTER_SHARED = Color.YELLOW;
- private boolean processing = false;
-
- /**
- * <b>Viewer</b><br/><br/>
- * <code>public <b>Viewer</b>()</code><br/><br/>
- * Initializes the viewer window and calorimeter panel.
- **/
- public Viewer() {
- // Initialize the viewer window
- super();
- setTitle("HPS Ecal Cluster Viewer");
- setDefaultCloseOperation(DISPOSE_ON_CLOSE);
- setPreferredSize(new Dimension(1060, 600));
- setMinimumSize(new Dimension(1060, 525));
- setLayout(null);
-
- // Set the scaling settings.
- ecalPanel.setMinimum(0.0001);
- ecalPanel.setMaximum(3000);
- ecalPanel.setScalingLogarithmic();
-
- // Disable the crystals in the calorimeter panel along the beam gap.
- for (int i = -23; i < 24; i++) {
- ecalPanel.setCrystalEnabled(getPanelX(i), 5, false);
- if (i > -11 && i < -1) {
- ecalPanel.setCrystalEnabled(getPanelX(i), 4, false);
- ecalPanel.setCrystalEnabled(getPanelX(i), 6, false);
- }
- }
-
- // Make a key listener to change events.
- addKeyListener(new EcalKeyListener());
-
- // Make a mouse motion listener to monitor mouse hovering.
- getContentPane().addMouseListener(new EcalMouseListener());
- getContentPane().addMouseMotionListener(new EcalMouseMotionListener());
-
- // Add the panels.
- add(ecalPanel);
- add(statusPanel);
-
- // Add a listener to update everything when the window changes size
- addComponentListener(new ResizeListener());
- }
-
- public void setSize(int width, int height) {
- super.setSize(width, height);
- resize();
- }
-
- public void setSize(Dimension d) {
- setSize(d.width, d.height);
- }
-
- /**
- * <b>setDataSouce</b><br/><br/>
- * <code>public void <b>setDataSource</b>(String filepath)</code><br/><br/>
- * Sets the viewer to read from the indicated data source.
- * @param filepath - The full path to the desired data file.
- * @throws IOException Occurs when there is an error opening the data file.
- **/
- public void setDataSource(String filepath) throws IOException {
- em = new EventManager(filepath);
- }
-
- /**
- * <b>displayNextEvent</b><br/><br/>
- * <code>public void <b>displayNextEvent</b>()</code><br/><br/>
- * Feeds the calorimeter panel the data from the next event.
- * @throws IOException Occurs when there is an issue with reading the data file.
- **/
- public void displayNextEvent() throws IOException {
- // Clear the calorimeter panel.
- ecalPanel.clearCrystals();
- ecalPanel.clearHighlight();
-
- // Clear the cluster map and cluster highlighting.
- clusterMap.clear();
- resetCursor();
-
- // If there is no data source, we can not do anything.
- if (em == null) { return; }
-
- // Otherwise, get the next event.
- em.readEvent();
-
- // Display it.
- for (EcalHit h : em.getHits()) {
- int ix = getPanelX(h.getX());
- int iy = getPanelY(h.getY());
- ecalPanel.addCrystalEnergy(ix, iy, h.getEnergy());
- }
- for (Cluster c : em.getClusters()) {
- Point seedHit = c.getSeedHit();
- int ix = getPanelX(seedHit.x);
- int iy = getPanelY(seedHit.y);
- ecalPanel.setCrystalCluster(ix, iy, true);
- clusterMap.put(new Point(getPanelX(seedHit.x), getPanelY(seedHit.y)), c);
- }
-
- // Redraw the calorimeter panel.
- ecalPanel.redraw();
- }
-
- public void displayLCIOEvent(EventHeader event, String ecalCollectionName, String clusterCollectionName) {
- // Make sure that a draw is not in process.
- if(processing) { return; }
-
- // Otherwise, we are now processing.
- processing = true;
-
- // Get the list of clusters and hits.
- List<HPSEcalCluster> clusters = event.get(HPSEcalCluster.class, clusterCollectionName);
- List<HPSCalorimeterHit> hits = event.get(HPSCalorimeterHit.class, ecalCollectionName);
-
- // Reset the calorimeter panel.
- ecalPanel.clearCrystals();
-
- // Add the calorimeter hits.
- for(HPSCalorimeterHit hit : hits) {
- int ix = hit.getIdentifierFieldValue("ix");
- int iy = hit.getIdentifierFieldValue("iy");
- double energy = hit.getRawEnergy();
- ecalPanel.addCrystalEnergy(getPanelX(ix), getPanelY(iy), energy);
- }
-
- // Add clusters.
- for(HPSEcalCluster cluster : clusters) {
- HPSCalorimeterHit seed = (HPSCalorimeterHit) cluster.getSeedHit();
- int ix = seed.getIdentifierFieldValue("ix");
- int iy = seed.getIdentifierFieldValue("iy");
- ecalPanel.setCrystalCluster(getPanelX(ix), getPanelY(iy), true);
- }
-
- // Redraw the panel.
- ecalPanel.redraw();
-
- // We are finished drawing.
- processing = false;
- }
-
- /**
- * <b>resize</b><br/><br/>
- * <code>private void <b>resize</b>()</code><br/><br/>
- * Handles proper resizing of the window and its components.
- **/
- private void resize() {
- // Define the status panel height.
- int statusHeight = 125;
-
- // Size and position the calorimeter display.
- ecalPanel.setLocation(0, 0);
- ecalPanel.setSize(getContentPane().getWidth(), getContentPane().getHeight() - statusHeight);
-
- // Size and position the status panel.
- statusPanel.setLocation(0, ecalPanel.getHeight());
- statusPanel.setSize(getContentPane().getWidth(), statusHeight);
- }
-
- /**
- * <b>getPanelX</b><br/><br/>
- * <code>public int <b>getPanelX</b>(int ecalX)</code><br/><br/>
- * Converts the LCSim x-coordinate to the calorimeter panel's coordinate
- * system.
- * @param ecalX - An LCSim calorimeter x-coordinate.
- * @return Returns the x-coordinate in the calorimeter panel's coordinate
- * system as an <code>int</code>.
- **/
- public int getPanelX(int ecalX) {
- if (ecalX <= 0) { return ecalX + 23; }
- else { return ecalX + 22; }
- }
-
- /**
- * <b>getPanelY</b><br/><br/>
- * <code>public int <b>getPanelY</b>(int ecalY)</code><br/><br/>
- * Converts the LCSim y-coordinate to the calorimeter panel's coordinate
- * system.
- * @param ecalY - An LCSim calorimeter y-coordinate.
- * @return Returns the y-coordinate in the calorimeter panel's coordinate
- * system as an <code>int</code>.
- **/
- public int getPanelY(int ecalY) { return 5 - ecalY; }
-
- /**
- * <b>getEcalX</b><br/><br/>
- * <code>public int <b>getEcalX</b>(int panelX)</code><br/><br/>
- * Converts the panel x-coordinate to the calorimeter's coordinate
- * system.
- * @param panelX - A panel x-coordinate.
- * @return Returns the x-coordinate in the calorimeter's coordinate
- * system as an <code>int</code>.
- */
- public int getEcalX(int panelX) {
- if(panelX > 22) { return panelX - 22; }
- else { return panelX - 23; }
- }
-
- /**
- * <b>getEcalY</b><br/><br/>
- * <code>public int <b>getEcalY</b>(int panelY)</code><br/><br/>
- * Converts the panel y-coordinate to the calorimeter's coordinate
- * system.
- * @param panelY - A panel y-coordinate.
- * @return Returns the y-coordinate in the calorimeter's coordinate
- * system as an <code>int</code>.
- */
- public int getEcalY(int panelY) { return 5 - panelY; }
-
- /**
- * <b>resetCursor</b><br/><br/>
- * <code>private void <b>resetCursor</b>()</code><br/><br/>
- * Performs the cursor highlight calculations for whatever the
- * most recently highlighted crystal was. This should be called
- * if the panel is cleared to reset the cursor highlight.
- */
- private void resetCursor() {
- Point temp = lastCrystal;
- lastCrystal = null;
- setCursorHighlight(temp);
- }
-
- /**
- * <b>setCursorHighlight</b><br/><br/>
- * <code>private boolean <b>setCursorHighlight</b>(Point crystal)</code><br/><br/>
- * Sets the highlighting for the indicated crystal and clears the
- * highlighting on any previously highlighted crystal. Note that
- * this will clear any existing highlighting.
- * @param crystal - The crystal to highlight.
- * @return Returns <code>true</code> if a different crystal is
- * highlighted than before and <code>false</code> if it is the
- * same crystal.
- */
- private boolean setCursorHighlight(Point crystal) {
- // Get the appropriate highlight color.
- Color hc = null;
- if(!background) { hc = HIGHLIGHT_CURSOR_DARK; }
- else { hc = HIGHLIGHT_CURSOR_LIGHT; }
-
- // Define helper variables.
- boolean crystalChanged;
- boolean cNull = (crystal == null);
- boolean lNull = (lastCrystal == null);
-
- // Determine if the selected crystal has changed.
- if(cNull && lNull) { crystalChanged = false; }
- else if(cNull ^ lNull) { crystalChanged = true; }
- else { crystalChanged = !lastCrystal.equals(crystal); }
-
- // If so, clear the highlighting and reset it.
- if(crystalChanged) {
- // Clear the old highlighting.
- ecalPanel.clearHighlight();
-
- // If the current crystal is a cluster, highlight the cluster.
- Cluster cluster = clusterMap.get(crystal);
- if(highlight && cluster != null) {
- for(Point ch : cluster.getComponentHits()) {
- ecalPanel.setCrystalHighlight(getPanelX(ch.x), getPanelY(ch.y), HIGHLIGHT_CLUSTER_COMPONENT);
- }
- for(Point sh : cluster.getSharedHits()) {
- ecalPanel.setCrystalHighlight(getPanelX(sh.x), getPanelY(sh.y), HIGHLIGHT_CLUSTER_SHARED);
- }
- }
-
- // If the current crystal is defined, highlight it.
- if(highlight && crystal != null) { ecalPanel.setCrystalHighlight(crystal.x, crystal.y, hc); }
- }
-
- // Set the last crystal to match the current one.
- lastCrystal = crystal;
-
- // Return whether a redraw is necessary.
- return crystalChanged;
- }
-
- /**
- * The <code>EcalListener</code> class binds the enter key to the
- * <code>displayNextEvent</code> method. Also allows for toggling
- * of highlighting with 'h' and background with 'b.' Swaps scale
- * from linear to logarithmic with 'l'.
- **/
- private class EcalKeyListener implements KeyListener {
- public void keyPressed(KeyEvent e) { }
-
- public void keyReleased(KeyEvent e) {
- // If enter was pressed, go to the next event.
- if (e.getKeyCode() == 10) {
- try { displayNextEvent(); }
- catch (IOException ex) {
- System.err.println(ex.getMessage());
- System.exit(1);
- }
- }
- // 'b' toggles the default white background.
- else if(e.getKeyCode() == 66) {
- if(background) { ecalPanel.setCrystalDefaultColor(null); }
- else { ecalPanel.setCrystalDefaultColor(Color.WHITE); }
- ecalPanel.redraw();
- background = !background;
- }
- // 'h' toggles highlighting the crystal under the cursor.
- else if(e.getKeyCode() == 72) {
- if(highlight) {
- if(setCursorHighlight(null)) { ecalPanel.redraw(); }
- }
- highlight = !highlight;
- }
- // 'l' toggles linear or logarithmic scaling.
- else if(e.getKeyCode() == 76) {
- if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
- else { ecalPanel.setScalingLinear(); }
- ecalPanel.redraw();
- }
- else { System.out.printf("Key Code: %d%n", e.getKeyCode()); }
- }
-
- public void keyTyped(KeyEvent e) { }
- }
-
- /**
- * The <code>EcalMouseListener</code> handles removing highlighting
- * and crystal field information when the cursor leaves the window.
- */
- private class EcalMouseListener implements MouseListener {
- public void mouseClicked(MouseEvent e) { }
-
- public void mouseEntered(MouseEvent e) { }
-
- public void mouseExited(MouseEvent e) {
- setCursorHighlight(null);
- statusPanel.clearValues();
- ecalPanel.redraw();
- }
-
- public void mousePressed(MouseEvent e) { }
-
- public void mouseReleased(MouseEvent e) { }
- }
-
- /**
- * The <code>EcalMouseMotionListener</code> handles updating of
- * the highlighted crystal and status panel information when the
- * mouse moves over the window.
- */
- private class EcalMouseMotionListener implements MouseMotionListener {
- public void mouseDragged(MouseEvent arg0) { }
-
- public void mouseMoved(MouseEvent e) {
- // Get the mouse coordinates, corrected for the panel position.
- int correctedX = e.getX();
- int correctedY = e.getY();
-
- // Get the crystal that the mouse is in.
- Point crystal = ecalPanel.getCrystalID(correctedX, correctedY);
-
- // Mark the current crystal for highlighting.
- boolean redraw = setCursorHighlight(crystal);
-
- // If this necessitates a redraw of the panel, do so.
- if(redraw) {
- lastCrystal = crystal;
- ecalPanel.redraw();
-
- if(crystal != null) {
- // Determine if the crystal is in the beam gap.
- boolean[] beamGap = new boolean[2];
- beamGap[0] = (getEcalY(crystal.y) == 0);
- beamGap[1] = Math.abs(getEcalY(crystal.y)) == 1 &&
- (getEcalX(crystal.x) >= -10 && getEcalX(crystal.x) <= -2);
-
- if(beamGap[0] || beamGap[1]) { statusPanel.clearValues(); }
- else {
- statusPanel.setFieldValue(0, String.valueOf(getEcalX(crystal.x)));
- statusPanel.setFieldValue(1, String.valueOf(getEcalY(crystal.y)));
- DecimalFormat formatter = new DecimalFormat("0.####E0");
- String energy = formatter.format(ecalPanel.getCrystalEnergy(crystal.x, crystal.y));
- statusPanel.setFieldValue(2, energy);
- }
- }
- else { statusPanel.clearValues(); }
- }
- }
- }
-
- /**
- * The <code>ResizeListener</code> class ensures that the components remain
- * at the correct size and location when the window is resized.
- **/
- private class ResizeListener implements ComponentListener {
- public void componentResized(ComponentEvent e) { resize(); }
-
- public void componentHidden(ComponentEvent e) { }
-
- public void componentMoved(ComponentEvent e) { }
-
- public void componentShown(ComponentEvent e) { }
- }
-}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event/Association.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event/Association.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,61 @@
+package org.hps.monitoring.ecal.event;
+
+import java.awt.Color;
+import java.awt.Point;
+
+/**
+ * Class <code>Association</code> tells the <code>CalorimeterPanel
+</code> to highlight the child crystal in the given color whenever the
+ * parent crystal is selected.
+ * @author Kyle McCarty
+ */
+public final class Association {
+ private final Point parent;
+ private final Point child;
+ private final Color highlight;
+
+ /**
+ * <b>Association</b><br/><br/>
+ * <code>public <b>Association</b>(Point parentCrystal, Point childCrystal, Color highlightColor)</code><br/><br/>
+ * Creates an association between a child crystal and a parent
+ * crystal.
+ * @param parentCrystal - The crystal with which the child crystal
+ * is connected.
+ * @param childCrystal - The connected crystal.
+ * @param highlightColor - The color in which the child crystal
+ * should be highlighted.
+ */
+ public Association(Point parentCrystal, Point childCrystal, Color highlightColor) {
+ parent = parentCrystal;
+ child = childCrystal;
+ highlight = highlightColor;
+ }
+
+ /**
+ * <b>getChildCrystal</b><br/><br/>
+ * <code>public Point <b>getChildCrystal</b>()</code><br/><br/>
+ * Indicates the indices for the child crystal.
+ * @return Returns the child crystal's indices in a <code>Point
+ * </code> object.
+ */
+ public Point getChildCrystal() { return child; }
+
+ /**
+ * <b>getHighlight</b><br/><br/>
+ * <code>public Color <b>getHighlight</b>()</code><br/><br/>
+ * Gets the color with which the child crystal should be highlighted
+ * whenever the parent crystal is selected.
+ * @return Returns the highlight color as a <code>Color</code> object.
+ */
+ public Color getHighlight() { return highlight; }
+
+ /**
+ * <b>getParentCrystal</b><br/><br/>
+ * <code>public Point <b>getParentCrystal</b>()</code><br/><br/>
+ * Indicates the indices for the parent crystal with which the
+ * child crystal is connected.
+ * @return Returns the parent crystal's indices in a <code>Point
+ * </code> object.
+ */
+ public Point getParentCrystal() { return parent; }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event/Cluster.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event/Cluster.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,171 @@
+package org.hps.monitoring.ecal.event;
+
+import java.awt.Point;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The class <code>Cluster</code> represents a cluster center and a
+ * collection of additional hits that together form a hit cluster.
+ *
+ * @author Kyle McCarty
+ */
+public final class Cluster {
+ private final Point center;
+ private final double energy;
+ private ArrayList<Point> hitList = new ArrayList<Point>();
+ private ArrayList<Point> shareList = new ArrayList<Point>();
+
+ /**
+ * <b>Cluster</b><br/><br/>
+ * <code>public <b>Cluster</b>(int ix, int iy)</code><br/><br/>
+ * Creates a new cluster. All clusters are required to have a
+ * cluster center.
+ * @param ix - The cluster center's x-index.
+ * @param iy - The cluster center's y-index.
+ */
+ public Cluster(int ix, int iy) { this(new Point(ix, iy), Double.NaN); }
+
+ /**
+ * <b>Cluster</b><br/><br/>
+ * <code>public <b>Cluster</b>(Point clusterCenter)</code><br/><br/>
+ * Creates a new cluster. All clusters are required to have a seed
+ * hit.
+ * @param clusterCenter - The <code>Point</code> object indicating in
+ * which crystal the seed hit occurred.
+ */
+ public Cluster(Point clusterCenter) { this(clusterCenter, Double.NaN); }
+
+ /**
+ * <b>Cluster</b><br/><br/>
+ * <code>public <b>Cluster</b>(int ix, int iy)</code><br/><br/>
+ * Creates a new cluster. All clusters are required to have a
+ * cluster center.
+ * @param ix - The cluster center's x-index.
+ * @param iy - The cluster center's y-index.
+ * @param energy - The cluster's energy.
+ */
+ public Cluster(int ix, int iy, double energy) { this(new Point(ix, iy), energy); }
+
+ /**
+ * <b>Cluster</b><br/><br/>
+ * <code>public <b>Cluster</b>(Point clusterCenter)</code><br/><br/>
+ * Creates a new cluster. All clusters are required to have a seed
+ * hit.
+ * @param clusterCenter - The <code>Point</code> object indicating in
+ * which crystal the seed hit occurred.
+ * @param energy - The cluster's energy.
+ */
+ public Cluster(Point clusterCenter, double energy) {
+ center = clusterCenter;
+ this.energy = energy;
+ }
+
+ /**
+ * <b>addComponentHit</b><br/><br/>
+ * <code>public void <b>addComponentHit</b>(int ix, int iy)</code><br/><br/>
+ * Adds an <code>Point</code> to the list of this cluster's
+ * component hits.
+ * @param ix - The component hit's x-coordinate.
+ * @param iy - The component hit's y-coordinate.
+ */
+ public void addComponentHit(int ix, int iy) { hitList.add(new Point(ix, iy)); }
+
+ /**
+ * <b>addComponentHit</b><br/><br/>
+ * <code>public void <b>addComponentHit</b>(Point eHit)</code><br/><br/>
+ * Adds an <code>Point</code> to the list of this cluster's
+ * component hits.
+ * @param eHit - The <code>Point</code> object indicating in which
+ * crystal the hit occurred.
+ */
+ public void addComponentHit(Point eHit) { hitList.add(eHit); }
+
+ /**
+ * <b>addSharedHit</b><br/><br/>
+ * <code>public void <b>addSharedHit</b>(int ix, int iy)</code><br/><br/>
+ * Adds an <code>Point</code> to the list of this cluster's shared
+ * hits.
+ * @param ix - The shared hit's x-coordinate.
+ * @param iy - The shared hit's y-coordinate.
+ */
+ public void addSharedHit(int ix, int iy) { shareList.add(new Point(ix, iy)); }
+
+ /**
+ * <b>addSharedHit</b><br/><br/>
+ * <code>public void <b>addSharedHit</b>(Point eHit)</code><br/><br/>
+ * Adds an <code>Point</code> to the list of this cluster's shared
+ * hits.
+ * @param eHit - The <code>Point</code> object indicating in which
+ * crystal the hit occurred.
+ */
+ public void addSharedHit(Point eHit) { shareList.add(eHit); }
+
+ /**
+ * <b>getClusterCenter</b><br/><br/>
+ * <code>public Point <b>getClusterCenter</b>()</code><br/><br/>
+ * Gets the hit representing the cluster center.
+ * @return Returns the cluster center hit as an <code>Point</code>.
+ */
+ public Point getClusterCenter() { return center; }
+
+ /**
+ * <b>getClusterEnergy</b><br/><br/>
+ * <code>public double <b>getClusterEnergy</b>()</code><br/><br/>
+ * Gets the cluster's energy, if it was defined when the cluster
+ * was constructed.
+ * @return Returns the energy of the cluster if it was defined,
+ * and <code>NaN</code> otherwise.
+ */
+ public double getClusterEnergy() { return energy; }
+
+ /**
+ * <b>getComponentHitCount</b><br/><br/>
+ * <code>public int <b>getComponentHitCount</b>()</code><br/><br/>
+ * Indicates how many component hits compose this cluster. Note
+ * that this does not include the seed hit or shared hits.
+ * @return Returns the number of component hits in the cluster
+ * as an <code>int</code>.
+ */
+ public int getComponentHitCount() { return hitList.size(); }
+
+ /**
+ * <b>getComponentHits</b><br/><br/>
+ * <code>public List<Point> <b>getComponentHits</b>()</code><br/><br/>
+ * Gets the list of hits that make up the cluster, exempting the
+ * seed hit and shared hits.
+ * @return Returns the cluster hits as a <code>List</code> object
+ * composed of <code>Point</code> objects.
+ */
+ public List<Point> getComponentHits() { return hitList; }
+
+ /**
+ * <b>getHitCount</b><br/><br/>
+ * <code>public int <b>getHitCount</b>()</code><br/><br/>
+ * Indicates how many total hits compose this cluster. This includes
+ * component hits, shared hits, and the seed hit.
+ * @return Returns the number of component hits in the cluster
+ * as an <code>int</code>.
+ */
+ public int getHitCount() { return hitList.size() + shareList.size() + 1; }
+
+ /**
+ * <b>getSharedHitCount</b><br/><br/>
+ * <code>public int <b>getSharedHitCount</b>()</code><br/><br/>
+ * Indicates how many shared hits compose this cluster. Note that
+ * this does not include the seed hit or component hits.
+ * @return Returns the number of shared hits in the cluster as an
+ * <code>int</code>.
+ */
+ public int getSharedHitCount() { return shareList.size(); }
+
+ /**
+ * <b>getSharedHits</b><br/><br/>
+ * <code>public List<Point> <b>getSharedHits</b>()</code><br/><br/>
+ * Gets the list of hits that make up the cluster, exempting the
+ * seed hit and component hits.
+ * @return Returns the shared hits as a <code>List</code> object
+ * composed of <code>Point</code> objects.
+ */
+ public List<Point> getSharedHits() { return shareList; }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event/EcalHit.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event/EcalHit.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,97 @@
+package org.hps.monitoring.ecal.event;
+
+import java.awt.Point;
+
+/**
+ * The class <code>EcalHit</code> is an extension of <code>Datum</code>
+ * that stores an energy. This is used for reading input from a text
+ * file. <code>CalorimeterHit</code> should be used when reading from
+ * an LCIO file.
+ **/
+public final class EcalHit {
+ // The coordinate on the calorimeter panel.
+ protected final Point loc;
+ // The (raw) energy of this hit.
+ private double energy = 0.0;
+
+ /**
+ * <b>EcalHit</b><br/><br/>
+ * <code>public <b>EcalHit</b>(int ix, int iy, double energy)</code><br/><br/>
+ * Initializes a calorimeter hit object.
+ * @param ix - The x-coordinate of the hit.
+ * @param iy - The y-coordinate of the hit.
+ * @param energy - The raw energy of the hit.
+ **/
+ public EcalHit(int ix, int iy, double energy) {
+ this(new Point(ix, iy), energy);
+ }
+
+ /**
+ * <b>EcalHit</b><br/><br/>
+ * <code>public <b>EcalHit</b>(Point ixy, double energy)</code><br/><br/>
+ * Initializes a calorimeter hit object.
+ * @param ixy - The x/y-coordinates of the hit.
+ * @param energy - The raw energy of the hit.
+ **/
+ public EcalHit(Point ixy, double energy) {
+ loc = ixy;
+ this.energy = energy;
+ }
+
+ /**
+ * <b>getEnergy</b><br/><br/>
+ * <code>public double <b>getEnergy</b>()</code><br/><br/>
+ * Indicates the raw energy of this hit.
+ * @return Returns the raw energy as a <code>double</code>.
+ **/
+ public double getEnergy() { return energy; }
+
+ /**
+ * <b>getLocation</b><br/><br/>
+ * <code>public Point <b>getLocation</b>()</code><br/><br/>
+ * Indicates the location of the object.
+ * @return Returns the object's location as a <code>Point
+ * </code> object.
+ **/
+ public Point getLocation() { return loc; }
+
+ /**
+ * <b>getX</b><br/><br/>
+ * <code>public int <b>getX</b>()</code><br/><br/>
+ * Indicates the x-coordinate of the object.
+ * @return Returns the x-coordinate as an <code>int</code>.
+ **/
+ public int getX() { return loc.x; }
+
+ /**
+ * <b>getY</b><br/><br/>
+ * <code>public int <b>getY</b>()</code><br/><br/>
+ * Indicates the y-coordinate of the object.
+ * @return Returns the y-coordinate as an <code>int</code>.
+ **/
+ public int getY() { return loc.y; }
+
+ /**
+ * <b>setEnergy</b><br/><br/>
+ * <code>public void <b>setEnergy</b>(double energy)</code><br/><br/>
+ * Sets the energy of the hit to the indicated value.
+ * @param energy - The new energy of the hit.
+ **/
+ public void setEnergy(double energy) { this.energy = energy; }
+
+ /**
+ * <b>setX</b><br/><br/>
+ * <code>public void <b>setX</b>(int x)</code><br/><br/>
+ * Sets the object's x-coordinate.
+ * @param x - The new x-coordinate.
+ **/
+ public void setX(int x) { loc.x = x; }
+
+ /**
+ * <b>setY</b><br/><br/>
+ * <code>public void <b>setY</b>(int y)</code><br/><br/>
+ * Sets the obejct's y-coordinate.
+ * @param y - The new y-coordinate.
+ **/
+ public void setY(int y) { loc.y = y; }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event/Event.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/event/Event.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,128 @@
+package org.hps.monitoring.ecal.event;
+
+import java.awt.Point;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class <code>Event</code> stores all the information the <code>
+ * CalorimeterPanel</code> needs to display an event.
+ * @author Kyle McCarty
+ */
+public final class Event {
+ private List<EcalHit> hitList;
+ private List<Point> clusterList;
+ private List<Association> connectList;
+
+ /**
+ * <b>Event</b><br/><br/>
+ * <code>public <b>Event</b>()</code><br/><br/>
+ * Creates a new <code>Event</code>.
+ */
+ public Event() { this(10, 10, 10); }
+
+ /**
+ * <b>Event</b><br/><br/>
+ * <code>public <b>Event</b>(int hits, int clusters)</code><br/><br/>
+ * Creates a new <code>Event</code> and reserves spaces for the
+ * given number of hits and cluster centers.
+ * @param hits - The number of hits for which to reserve space.
+ * @param clusters - The number of cluster centers for which to
+ * reserve space.
+ */
+ public Event(int hits, int clusters) { this(hits, clusters, 10); }
+
+ /**
+ * <b>Event</b><br/><br/>
+ * <code>public <b>Event</b>(int hits, int clusters, int associations)</code><br/><br/>
+ * Creates a new <code>Event</code> and reserves spaces for the
+ * given number of hits, cluster centers, and crystal associations.
+ * @param hits - The number of hits for which to reserve space.
+ * @param clusters - The number of cluster centers for which to
+ * reserve space.
+ * @param associations - The number of crystal associations for
+ * which to reserve space.
+ */
+ public Event(int hits, int clusters, int associations) {
+ hitList = new ArrayList<EcalHit>(hits);
+ clusterList = new ArrayList<Point>(clusters);
+ connectList = new ArrayList<Association>(associations);
+ }
+
+ /**
+ * <b>Event</b><br/><br/>
+ * <code>public <b>Event</b>(List<EcalHit> hits, List<Point> clusters, List<Association> associations)</code><br/><br/>
+ * Creates a new <code>Event</code> and sets its contents to those
+ * of the given lists. The crystal association list will be empty.
+ * @param hits - The list of calorimeter hits.
+ * @param clusters - The list of cluster centers.
+ */
+ public Event(List<EcalHit> hits, List<Point> clusters) {
+ this(hits, clusters, new ArrayList<Association>());
+ }
+
+ /**
+ * <b>Event</b><br/><br/>
+ * <code>public <b>Event</b>(List<EcalHit> hits, List<Point> clusters, List<Association> associations)</code><br/><br/>
+ * Creates a new <code>Event</code> and sets its contents to those
+ * of the given lists.
+ * @param hits - The list of calorimeter hits.
+ * @param clusters - The list of cluster centers.
+ * @param associations - The list of crystal associations.
+ */
+ public Event(List<EcalHit> hits, List<Point> clusters, List<Association> associations) {
+ hitList = hits;
+ clusterList = clusters;
+ connectList = associations;
+ }
+
+ /**
+ * <b>addAssociation</b><br/><br/>
+ * <code>public void <b>addAssociation</b>(Association connectedCrystal)</code><br/><br/>
+ * Adds a crystal association to the event.
+ * @param connectedCrystal - The crystal association to add.
+ */
+ public void addAssociation(Association connectedCrystal) {
+ connectList.add(connectedCrystal);
+ }
+
+ /**
+ * <b>addCluster</b><br/><br/>
+ * <code>public void <b>addCluster</b>(Point cluster)</code><br/><br/>
+ * Adds a cluster center to the event.
+ * @param cluster - The cluster center to add.
+ */
+ public void addCluster(Point cluster) { clusterList.add(cluster); }
+
+ /**
+ * <b>addHit</b><br/><br/>
+ * <code>public void <b>addHit</b>(EcalHit hit)</code><br/><br/>
+ * Adds a calorimeter hit to the event.
+ * @param hit - The calorimeter hit to add.
+ */
+ public void addHit(EcalHit hit) { hitList.add(hit); }
+
+ /**
+ * <b>getAssociations</b><br/><br/>
+ * <code>public List<Association> <b>getAssociations</b>()</code><br/><br/>
+ * Gets the list of associated crystals for this event.
+ * @return Returns the associations in a <code>List</code>.
+ */
+ public List<Association> getAssociations() { return connectList; }
+
+ /**
+ * <b>getClusterCenters</b><br/><br/>
+ * <code>List<Cluster><b>getClusterCenters</b>()</code><br/><br/>
+ * Gets the list of cluster centers for this event.
+ * @return Returns the cluster centers in a <code>List</code>.
+ */
+ public List<Point> getClusterCenters() { return clusterList; }
+
+ /**
+ * <b>getHits</b><br/><br/>
+ * <code>public List<EcalHit> <b>getHits</b>()</code><br/><br/>
+ * Gets the list of calorimeter hits for this event.
+ * @return Returns the hits in a <code>List</code>.
+ */
+ public List<EcalHit> getHits() { return hitList; }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/exec
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/exec/Main.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/exec/Main.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,84 @@
+package org.hps.monitoring.ecal.exec;
+
+import org.hps.monitoring.ecal.io.TextManager;
+
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.io.IOException;
+
+import org.hps.monitoring.ecal.ui.ActiveViewer;
+import org.hps.monitoring.ecal.ui.ClusterViewer;
+import org.hps.monitoring.ecal.ui.FileViewer;
+import org.hps.monitoring.ecal.ui.OccupancyViewer;
+
+/**
+ * The class <code>Main</code> can be used to create an event display that
+ * reads from file. By default it reads from "cluster-hit.txt" at the class
+ * path root. This can be changed by altering the line<br/>
+ * <code>window.setDataSource("cluster-hit.txt")</code><br/>
+ **/
+public class Main {
+
+ public static void main(String[] args) throws IOException {
+ // If "-h" was given as a command line argument, print the
+ // help text.
+ for(String s : args) {
+ if(s.compareTo("-h") == 0 || s.compareTo("--help") == 0) {
+ System.out.println("HPS Event Display");
+ System.out.println("Options:");
+ System.out.printf("%-4s%-12s%s%n", "-h", "--help", "Display help text.");
+ System.out.printf("%-4s%-12s%s%n", "-i", "--input", "Defines the input file.");
+ System.out.printf("%-4s%-12s%s%n", "-t", "--type", "Select the type of display.");
+ System.out.printf("%-4s%-12s%s%n", "", "", "Allowed types: Event, Cluster, Occupancy");
+ System.exit(0);
+ }
+ }
+
+ // If -i or --input was given, set the input file.
+ String filepath = "raw-hits.txt";
+ for(int i = 0; i < args.length; i++) {
+ if(args[i].compareTo("-i") == 0 || args[i].compareTo("--input") == 0) {
+ if(args.length <= i + 1) { System.out.println("Error: Expected additional argument."); }
+ else { filepath = args[i + 1]; }
+ }
+ }
+
+ // Define the viewer. By default, we employ a file viewer.
+ TextManager dataManager = new TextManager(filepath);
+ ActiveViewer window = new FileViewer(dataManager);
+
+ // Command line argument "-t" allows a different type to be declared.
+ if(args.length >= 2 && (args[0].compareTo("-t") == 0 || args[0].compareTo("--type") == 0)) {
+ // If an occupancy viewer has been specified...
+ if(args[1].compareToIgnoreCase("Occupancy") == 0) {
+ window = new OccupancyViewer(dataManager);
+ }
+ // If an event viewer has been specified...
+ else if(args[1].compareToIgnoreCase("Event") == 0) {
+ window = new FileViewer(dataManager);
+ }
+ // If a cluster viewer has been specified...
+ else if(args[1].compareToIgnoreCase("Cluster") == 0) {
+ window = new ClusterViewer(dataManager, 2);
+ }
+ // Otherwise, it is an invalid type.
+ else {
+ System.out.println("Display type \"" + args[1] + "\" not recognized.");
+ System.exit(0);
+ }
+ }
+
+ // Get screen size of primary monitor
+ GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
+ int screenWidth = gd.getDisplayMode().getWidth();
+ int screenHeight = gd.getDisplayMode().getHeight();
+
+ // Set the viewer window location and make it visible.
+ window.setLocation((screenWidth - window.getPreferredSize().width) / 2,
+ (screenHeight - window.getPreferredSize().height) / 2);
+ window.setVisible(true);
+
+ // Start the viewer with the first event.
+ window.displayNextEvent();
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/io
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/io/AdvancedReader.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/io/AdvancedReader.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,249 @@
+package org.hps.monitoring.ecal.io;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.Reader;
+
+/**
+ * Class <code>AdvancedReader</code> is an implementation of <code>Reader
+ * </code> that allows lines to be read both forwards and backwards.
+ *
+ * @author Kyle McCarty
+ */
+public class AdvancedReader extends Reader {
+ private RandomAccessFile file;
+ private long mark = -1;
+
+ /**
+ * <b>AdvancedReader</b><br/><br/>
+ * <code>public <b>AdvancedReader</b>(String filepath)</code><br/><br/>
+ * Constructs an <code>AdvancedReader</code> from the file located
+ * at the given file path.
+ * @param filepath - The path to the file that is to be loaded.
+ * @throws FileNotFoundException - Occurs when there is no file at
+ * the indicated file path.
+ */
+ public AdvancedReader(String filepath) throws FileNotFoundException {
+ file = new RandomAccessFile(filepath, "r");
+ }
+
+ /**
+ * <b>AdvancedReader</b><br/><br/>
+ * <code>public <b>AdvancedReader</b>(File inputFile)</code><br/><br/>
+ * Constructs an <code>AdvancedReader</code> from the indicated file.
+ * @param inputFile - The file that is to be loaded.
+ * @throws FileNotFoundException - Occurs when the referenced file
+ * does not exist.
+ */
+ public AdvancedReader(File inputFile) throws FileNotFoundException {
+ file = new RandomAccessFile(inputFile.getAbsolutePath(), "r");
+ }
+
+ public void close() throws IOException { file.close(); }
+
+ public void mark(int readAheadLimit) throws IOException { mark = file.getFilePointer(); }
+
+ public boolean markSupported() { return true; }
+
+ public int read() throws IOException { return file.read(); }
+
+ public int read(char[] cbuf) throws IOException {
+ // Read a character from the file into each spot in the array.
+ for(int i = 0; i < cbuf.length; i++) {
+ // Get the next character.
+ int curChar = file.read();
+
+ // If the character exists, write it to the array.
+ if(curChar != 0) { cbuf[i] = (char)curChar; }
+
+ // Otherwise, return noting the number of filled slots.
+ else { return i; }
+ }
+
+ // If we reach the end of the loop, all array slots are filled.
+ return cbuf.length;
+ }
+
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ // We will fill the char array until either the indicated number
+ // of slots are filled or until we run out of slots.
+ int[] max = { cbuf.length, off + len };
+
+ // Track the number of slots written to.
+ int slotsFilled = 0;
+
+ // Fill the array slots.
+ for(int i = off; (i < max[0] && i < max[1]); i++) {
+ // Get the next character.
+ int curChar = file.read();
+
+ // If the character is defined, write it.
+ if(curChar != -1) {
+ cbuf[i] = (char)curChar;
+ slotsFilled++;
+ }
+
+ // Otherwise, return noting the number of filled slots.
+ else { return slotsFilled; }
+ }
+
+ // If we reach the end of the loop, all array slots are filled.
+ return slotsFilled;
+ }
+
+ /**
+ * <b>readNextLine</b><br/><br/>
+ * <code>public String <b>readNextLine</b>()</code><br/><br/>
+ * Reads the next line of text. A line is considered to be
+ * terminated by any one of a line feed ('\n'), a carriage return
+ * ('\r'), or a carriage return followed immediately by a linefeed.
+ * @return A String containing the contents of the line, not
+ * including any line-termination characters, or null if the end
+ * of the stream has been reached.
+ * @throws IOException - Occurs if there is an issue reading
+ * the file.
+ */
+ public String readNextLine() throws IOException {
+ // Store whether the new line we found was a carriage return
+ // or a proper newline.
+ boolean readCarriageReturn = false;
+
+ // Add characters to a buffer until we reach either a new line
+ // or a carriage return.
+ int curChar = -1;
+ StringBuffer curLine = new StringBuffer();
+ while((curChar = file.read()) != -1) {
+ if(curChar != '\n' && curChar != '\r') { curLine.append((char)curChar); }
+ else if(curChar == '\r') {
+ readCarriageReturn = true;
+ break;
+ }
+ else { break; }
+ }
+
+ // If we reached this point because we hit the end of the file,
+ // check to make sure that the string buffer contains something.
+ // If it doesn't, then we started at the end of the file and
+ // should return null. Otherwise, just return what is in the
+ // string buffer.
+ if(curChar == -1) {
+ if(curLine.length() == 0) { return null; }
+ else { return curLine.toString(); }
+ }
+
+ // If we found a carriage return as the new line character, then
+ // we need to check if the next line is a newline '\n' character,
+ // since some systems us '/r/n' for a new line signifier.
+ if(readCarriageReturn) {
+ curChar = file.read();
+ if(curChar != '\n') { file.seek(file.getFilePointer() - 1); }
+ }
+
+ // Return the buffer.
+ return curLine.toString();
+ }
+
+ /**
+ * <b>readPreviousLine</b><br/><br/>
+ * <code>public String <b>readPreviousLine</b>()</code><br/><br/>
+ * Reads the previous line of text. A line is considered to be
+ * terminated by any one of a line feed ('\n'), a carriage return
+ * ('\r'), or a carriage return followed immediately by a linefeed.
+ * @return A <code>String</code> containing the contents of the
+ * line, not including any line-termination characters, or null
+ * if the beginning of the stream has been reached.
+ * @throws IOException - Occurs if there is an issue reading
+ * the file.
+ */
+ public String readPreviousLine() throws IOException {
+ // Define variables.
+ short newlinesRead = 0;
+ boolean allowDuplicate = false;
+ int lastChar = -1;
+ long offset = file.getFilePointer();
+ int curChar;
+
+ // If we are at the start of the file, return null.
+ if(offset == 0) { return null; }
+
+ while(newlinesRead < 3) {
+ // Decrement the offset.
+ offset -= 1;
+
+ // If the offset is still within the bounds of the file,
+ // then go to it.
+ if(offset >= 0) {
+ // Get the new file position.
+ file.seek(offset);
+
+ // Read the character there.
+ curChar = file.read();
+
+ // If this a new line character, account for it.
+ if(curChar == '\n' || curChar == '\r') {
+ // If we have read a newline, and we then read another
+ // immediately after it, then we will accept that newline
+ // as well, since some systems use
+ if(((lastChar == '\n' && curChar == '\r') || (lastChar == '\r' && curChar == '\n')) && allowDuplicate) {
+ allowDuplicate = false;
+ }
+
+ // If this is the first newline character we have
+ // seen, then we have reached the end of the desired
+ // line. We need to keep going until we find the start.
+ else if(newlinesRead < 3) {
+ newlinesRead++;
+ allowDuplicate = true;
+ }
+ }
+
+ // If a character other than a newline was read after
+ // a newline, then a second newline is always unrelated.
+ else { allowDuplicate = false; }
+
+ // Set the current character to the last read character.
+ lastChar = curChar;
+ }
+ // If the offset has reached zero, but didn't start there,
+ // then we have reached the start of the file. We handle
+ // this case.
+ else {
+ // Set the pointer to the beginning of the file.
+ file.seek(0);
+
+ // If we are looking for the final new line, then the
+ // start of the file counts and we should return.
+ if(newlinesRead == 2) { return readNextLine(); }
+
+ // Otherwise, there is no previous line.
+ else { return null; }
+ }
+ }
+
+ // If we reach this return line, we should be at the correct
+ // position to read the previous line.
+ return readNextLine();
+ }
+
+ public void reset() throws IOException {
+ if(mark != -1) { file.seek(mark); }
+ else { throw new IOException("mark() must be called before reset()."); }
+ }
+
+ public long skip(long n) throws IOException {
+ // Read n characters.
+ for(int i = 0; i < n; i++) {
+ // Get the skipped character.
+ int curChar = file.read();
+
+ // If it is invalid, return.
+ if(curChar == -1) { return i; }
+ }
+
+ // If we reached the end of the loop, the requested number of
+ // characters have been skipped.
+ return n;
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/io
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/io/EventManager.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/io/EventManager.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,62 @@
+package org.hps.monitoring.ecal.io;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.hps.monitoring.ecal.event.Cluster;
+import org.hps.monitoring.ecal.event.EcalHit;
+
+/**
+ * Interface <code>EventManager</code> is responsible for traversing
+ * an event data file and extracting lists of calorimeter hits and
+ * hit clusters from it to be passed to a <code>FileViewer</code>.
+ *
+ * @author Kyle McCarty
+ */
+public interface EventManager {
+ /**
+ * <b>close</b><br/><br/>
+ * <code>public void <b>close</b>()</code><br/><br/>
+ * Closes the event manager. Once this is performed, no additional events
+ * may be read.
+ * @throws IOException Occurs if there is an error closing the file stream.
+ **/
+ public void close() throws IOException;
+
+ /**
+ * <b>getClusters</b><br/><br/>
+ * <code>public ArrayList<Cluster> <b>getClusters</b>()</code><br/><br/>
+ * Allows access to the current event's list of clusters.
+ * @return Returns the current clusters as an <code>ArrayList
+ * </code> object.
+ **/
+ public List<Cluster> getClusters();
+
+ /**
+ * <b>getHits</b><br/><br/>
+ * <code>public ArrayList<EcalHit> <b>getHits</b>()</code><br/><br/>
+ * Allows access to the current event's list of hits.
+ * @return Returns the current hits as an <code>ArrayList</code> object.
+ **/
+ public List<EcalHit> getHits();
+
+ /**
+ * <b>nextEvent</b><br/><br/>
+ * <code>public boolean <b>nextEvent</b>()</code><br/><br/>
+ * Populates the event manager with hits and clusters from the next event.
+ * @return Returns <code>true</code> if an event was read and <code>false
+ * </code> if it was not.
+ * @throws IOException Occurs if there was a file read error.
+ **/
+ public boolean nextEvent() throws IOException;
+
+ /**
+ * <b>previousEvent</b><br/><br/>
+ * <code>public boolean <b>previousEvent</b>()</code><br/><br/>
+ * Populates the event manager with hits and clusters from the previous event.
+ * @return Returns <code>true</code> if an event was read and <code>false
+ * </code> if it was not.
+ * @throws IOException Occurs if there was a file read error.
+ **/
+ public boolean previousEvent() throws IOException;
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/io
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/io/TextManager.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/io/TextManager.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,191 @@
+package org.hps.monitoring.ecal.io;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+import org.hps.monitoring.ecal.event.Cluster;
+import org.hps.monitoring.ecal.event.EcalHit;
+
+/**
+ * The class <code>TextManager</code> handles loading hits and clusters from a
+ * text file to populate the calorimeter panel. Input should be of the form
+ * Event [Number]
+ * Indicates the start of a new event.
+ *
+ * EcalHit [X] [Y] [Energy]
+ * Represents a calorimeter hit at coordinates ([X], [Y]) and with energy
+ * [Energy]. Coordinates should be in calorimeter form (x = [-23, 23] and
+ * y = [-5, 5]) and must integers. Energy can be a decimal value. Brackets
+ * should not be included in the line.
+ *
+ * Cluster [X] [Y] [Energy]
+ * Represents the location of a cluster at coordinates ([X], [Y]) with
+ * [Energy] total cluster energy. Note that the [Energy] field is not
+ * required. Brackets should not be included in the line.
+ *
+ * CompHit [X] [Y]
+ * Represents a component hit of the previously declared cluster and located
+ * at coordinates ([X], [Y]). Brackets should not be included in the line.
+ *
+ * SharHit [X] [Y]
+ * Represents a hit that is shared between two or more clusters which is
+ * located at coordinates ([X], [Y]). Brackets should not be included in
+ * the line.
+ *
+ * EndEvent
+ * Indicates that the event has ended.
+ *
+ * @author Kyle McCarty
+ **/
+public final class TextManager implements EventManager {
+ // File reader for reading the input.
+ private AdvancedReader reader;
+ // List for storing the hits from the current event.
+ private ArrayList<EcalHit> hitList = new ArrayList<EcalHit>();
+ // List for storing the clusters from the current hit.
+ private ArrayList<Cluster> clusterList = new ArrayList<Cluster>();
+ // Whether the event manager has an open file.
+ private boolean open = true;
+ // Track the current event number.
+ private int curEvent = 0;
+
+ /**
+ * <b>EventManager</b><br/><br/>
+ * <code>public <b>EventManager</b>(String filename)</code><br/><br/>
+ * Initializes an event manager that will read from the indicated file.
+ * @param filename - The path to the file containing hit information.
+ **/
+ public TextManager(String filename) throws IOException {
+ reader = new AdvancedReader(filename);
+ }
+
+ public void close() throws IOException {
+ reader.close();
+ open = false;
+ }
+
+ public ArrayList<Cluster> getClusters() {
+ if (!open) { return null; }
+ else { return clusterList; }
+ }
+
+ public ArrayList<EcalHit> getHits() {
+ if (!open) { return null; }
+ else { return hitList; }
+ }
+
+ public boolean nextEvent() throws IOException {
+ // We can only read of the reader is open.
+ if (!open) { return false; }
+
+ // Store the current line.
+ String curLine = reader.readNextLine();
+
+ // Keep sorting until we hit a null or an event header.
+ while (curLine != null && !curLine.contains("Event")) {
+ curLine = reader.readNextLine();
+ }
+
+ // If we hit a null, we are at the end of the file.
+ if (curLine == null) { return false; }
+
+ // Clear the data lists.
+ hitList = new ArrayList<EcalHit>();
+ clusterList = new ArrayList<Cluster>();
+
+ // Get the current event.
+ StringTokenizer et = new StringTokenizer(curLine);
+ et.nextToken();
+ curEvent = Integer.parseInt(et.nextToken());
+
+ // Otherwise, we have read an event header and must populate
+ // the data lists.
+ curLine = reader.readNextLine();
+ while (curLine != null && curLine.compareTo("EndEvent") != 0) {
+ // Break apart the line.
+ StringTokenizer st = new StringTokenizer(curLine);
+ String name = st.nextToken();
+ int ix = Integer.parseInt(st.nextToken());
+ int iy = Integer.parseInt(st.nextToken());
+
+ // If this is a cluster, add a new cluster object.
+ if (name.compareTo("Cluster") == 0) {
+ // Get the cluster energy, if it is given.
+ double clusterEnergy = Double.NaN;
+ if(st.hasMoreTokens()) { clusterEnergy = Double.parseDouble(st.nextToken()); }
+
+ // Add a new cluster.
+ clusterList.add(new Cluster(ix, iy, clusterEnergy));
+ }
+
+ // If this is a calorimeter hit, add a new calorimeter hit object.
+ else if (name.compareTo("EcalHit") == 0) {
+ double energy = Double.parseDouble(st.nextToken());
+ hitList.add(new EcalHit(ix, iy, energy));
+ }
+
+ // If this is a cluster component hit, add it to the last cluster.
+ else if(name.compareTo("CompHit") == 0) {
+ // There must be a last cluster to process this hit type.
+ if(clusterList.size() == 0) {
+ System.err.println("File Format Error: A cluster component hit was read, but" +
+ " no cluster has been declared. Terminating.");
+ System.exit(1);
+ }
+ else { clusterList.get(clusterList.size() - 1).addComponentHit(ix, iy); }
+ }
+
+ // If this is a cluster shared hit, add it to the last cluster.
+ else if(name.compareTo("SharHit") == 0) {
+ // There must be a last cluster to process this hit type.
+ if(clusterList.size() == 0) {
+ System.err.println("File Format Error: A cluster shared hit was read, but" +
+ " no cluster has been declared. Terminating.");
+ System.exit(1);
+ }
+ else { clusterList.get(clusterList.size() - 1).addSharedHit(ix, iy); }
+ }
+
+ // Get the next line.
+ curLine = reader.readNextLine();
+ }
+
+ // Indicate that an event was processed.
+ return true;
+ }
+
+ public boolean previousEvent() throws IOException {
+ // If we are at the first event, do nothing. There is no
+ // previous event to display.
+ if(curEvent == 1) { return false; }
+
+ // Otherwise, loop backward until we find the previous event header.
+ String curLine;
+ while(true) {
+ // Get the previous line.
+ curLine = reader.readPreviousLine();
+
+ // Otherwise, if it is null, we've reached the start of the file.
+ if(curLine == null) {
+ System.err.println("Error: Unexpectedly reached SOF.");
+ System.exit(1);
+ }
+
+ // If the previous line is an event, note it.
+ if(curLine.substring(0, 5).compareTo("Event") == 0) {
+ // Get the event number of the current event.
+ StringTokenizer et = new StringTokenizer(curLine);
+ et.nextToken();
+ int readEvent = Integer.parseInt(et.nextToken());
+
+ // If the read event number is one back from the current
+ // event, jump back a step and read the event.
+ if(readEvent == (curEvent - 1)) {
+ reader.readPreviousLine();
+ return nextEvent();
+ }
+ }
+ }
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/lcsim
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/lcsim/LCIOBridgeDriver.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/lcsim/LCIOBridgeDriver.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,180 @@
+package org.hps.monitoring.ecal.lcsim;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.EventHeader;
+import org.lcsim.hps.recon.ecal.HPSEcalCluster;
+import org.lcsim.util.Driver;
+
+import org.hps.monitoring.ecal.event.Cluster;
+import org.hps.monitoring.ecal.event.EcalHit;
+import org.hps.monitoring.ecal.ui.PEventViewer;
+
+/**
+ * Class <code>LCIOBridgeDriver</code> displays LCIO events on the
+ * event display.
+ *
+ * @author Kyle McCarty
+ */
+public class LCIOBridgeDriver extends Driver {
+ // The identification name for getting the calorimeter object.
+ String ecalName;
+ // The collection name for the calorimeter hits.
+ String ecalCollectionName;
+ // The collection name in which to store clusters.
+ String clusterCollectionName = "EcalClusters";
+ // Sets how many events should pass before the display updates.
+ int displayInterval = 0;
+ // Whether the event display is currently processing an event or not.
+ private boolean updating = false;
+ // How many events where processed from the last displayed event.
+ private int eventsProcessed = 0;
+ // The event display.
+ private PEventViewer eventDisplay = new PEventViewer();
+
+ /**
+ * <b>process</b><br/><br/>
+ * <code>public void <b>process</b>(EventHeader event)</code><br/><br/>
+ * Converts an LCIO event into an event display compatible format.
+ * Additionally handles updating the event display, if appropriate.
+ * @param event - The LCIO event.
+ */
+ public void process(EventHeader event) {
+ // If we are still updating the display, skip this event.
+ if(updating) { return; }
+
+ // Make sure that this event has calorimeter hits.
+ if (event.hasCollection(CalorimeterHit.class, ecalCollectionName)) {
+ // Get the list of calorimeter hits from the event.
+ List<CalorimeterHit> hits = event.get(CalorimeterHit.class, ecalCollectionName);
+
+ // Define a list of clusters from the event.
+ List<HPSEcalCluster> clusters = event.get(HPSEcalCluster.class, clusterCollectionName);
+
+ // Increment the number of events we have seen.
+ eventsProcessed++;
+
+ // If this is the correct place to update, do so.
+ if(eventsProcessed >= displayInterval) {
+ // Lock the update method for the duration of the update.
+ updating = true;
+
+ // Clear the event display.
+ eventDisplay.resetDisplay();
+
+ // Add all of the hits.
+ for(CalorimeterHit hit : hits) {
+ // Get the hit's location and energy.
+ int ix = hit.getIdentifierFieldValue("ix");
+ int iy = hit.getIdentifierFieldValue("iy");
+ double energy = hit.getRawEnergy();
+
+ // Add the hit energy to the event display.
+ eventDisplay.addHit(new EcalHit(ix, iy, energy));
+ }
+
+ // Add all the clusters.
+ for(HPSEcalCluster cluster : clusters) {
+ // Get the seed hit.
+ CalorimeterHit seed = cluster.getSeedHit();
+ int ix = seed.getIdentifierFieldValue("ix");
+ int iy = seed.getIdentifierFieldValue("iy");
+ double energy = seed.getRawEnergy();
+
+ // Add the cluster center to the event display.
+ Cluster cc = new Cluster(ix, iy, energy);
+ eventDisplay.addCluster(cc);
+ }
+
+ // Update the display.
+ eventDisplay.updateDisplay();
+
+ // Reset the number of events we've seen since the last update.
+ eventsProcessed = 0;
+
+ // Unlock the update method so that more events can be processed.
+ updating = false;
+ }
+ }
+ }
+
+ /**
+ * <b>setClusterCollectionName</b><br/><br/>
+ * <code>public void <b>setClusterCollectionName</b>(String clusterCollectionName)</code><br/><br/>
+ * Sets the name of the LCIO collection that contains calorimeter
+ * cluster information.
+ * @param clusterCollectionName - The name of the LCIO collection.
+ */
+ public void setClusterCollectionName(String clusterCollectionName) {
+ this.clusterCollectionName = clusterCollectionName;
+ }
+
+ /**
+ * <b>setDisplayInterval</b><br/><br/>
+ * <code>public void <b>setDisplayInterval</b>(String displayInterval)</code><br/><br/>
+ * Sets the rate at which events are displayed. The driver will
+ * render a new event when <code>displayInterval</code> events
+ * have occurred since the last one. Note that e value of 0 or
+ * 1 will display events as quickly as they can be displayed.
+ * @param displayInterval - The number of events to skip before
+ * a new event is displayed.
+ */
+ public void setDisplayInterval(String displayInterval) {
+ // Convert the argument to an integer.
+ int disp = Integer.parseInt(displayInterval);
+
+ // If it is negative, make it zero.
+ if(disp < 0) { disp = 0; }
+
+ // Set the display interval.
+ this.displayInterval = disp;
+ }
+
+ /**
+ * <b>setEcalCollectionName</b><br/><br/>
+ * <code>public void <b>setEcalCollectionName</b>(String ecalCollectionName)</code><br/><br/>
+ * Sets the name of the LCIO collection that contains calorimeter
+ * hit information.
+ * @param ecalCollectionName - The name of the LCIO collection.
+ */
+ public void setEcalCollectionName(String ecalCollectionName) {
+ this.ecalCollectionName = ecalCollectionName;
+ }
+
+ /**
+ * <b>setEcalName</b><br/><br/>
+ * <code>public void <b>setEcalName</b>(String ecalName)</code><br/><br/>
+ * Sets which detector configuration should be used.
+ * @param ecalName - The name of the detector configuration.
+ */
+ public void setEcalName(String ecalName) { this.ecalName = ecalName; }
+
+ /**
+ * <b>startOfData</b><br/><br/>
+ * <code>public void <b>startOfData</b>()</code><br/><br/>
+ * Ensures that critical collection names are defined.
+ */
+ public void startOfData() {
+ // Make sure that there is a cluster collection name into which clusters may be placed.
+ if (ecalCollectionName == null) {
+ throw new RuntimeException("The parameter ecalCollectionName was not set!");
+ }
+
+ // Make sure that there is a calorimeter detector.
+ if (ecalName == null) {
+ throw new RuntimeException("The parameter ecalName was not set!");
+ }
+
+ // Set the events passed so that the first event will display.
+ eventsProcessed = displayInterval - 1;
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/ActiveViewer.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/ActiveViewer.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,153 @@
+package org.hps.monitoring.ecal.ui;
+
+import java.awt.Color;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import org.hps.monitoring.ecal.io.EventManager;
+
+/**
+ * Abstract class <code>ActiveViewer</code> describes a <code>Viewer
+ * </code> object that is connected to a static data source. It is
+ * designed to instruct the data source when to provide new events.
+ *
+ * @author Kyle McCarty
+ */
+public abstract class ActiveViewer extends Viewer {
+ private static final long serialVersionUID = -6107646224627009923L;
+ // Stores whether the background color is set or not.
+ private boolean background = false;
+ // Gets events from some file.
+ protected final EventManager em;
+
+ /**
+ * <b>ActiveViewer</b><br/><br/>
+ * <code>public <b>ActiveViewer</b>(EventManager em)</code><br/><br/>
+ * Creates an active-type <code>Viewer</code> window which draws
+ * events from the indicated data source.
+ * @param em - The data source event manager.
+ */
+ public ActiveViewer(EventManager em) { this(em, new String[0]); }
+
+ /**
+ * <b>ActiveViewer</b><br/><br/>
+ * <code>public <b>ActiveViewer</b>(EventManager em, String... fieldNames)</code><br/><br/>
+ * Creates an active-type <code>Viewer</code> window which draws
+ * events from the indicated data source with additional status
+ * fields defined by the <code>fieldNames</code> argument.
+ * @param em - The data source event manager.
+ * @param fieldNames - An array of additional status fields
+ * that should be displayed.
+ */
+ public ActiveViewer(EventManager em, String... fieldNames) {
+ // Pass any additional field values to the super class.
+ super(fieldNames);
+
+ // Set the data source.
+ this.em = em;
+
+ // Make a key listener to change events.
+ addKeyListener(new EcalKeyListener());
+ }
+
+ /**
+ * <b>displayNextEvent</b><br/><br/>
+ * <code>public void <b>displayNextEvent</b>()</code><br/><br/>
+ * Feeds the calorimeter panel the data from the next event.
+ * @throws IOException Occurs when there is an issue with reading the data file.
+ **/
+ public abstract void displayNextEvent() throws IOException;
+
+ /**
+ * <b>displayPreviousEvent</b><br/><br/>
+ * <code>public void <b>displayPreviousEvent</b>()</code><br/><br/>
+ * Feeds the calorimeter panel the data from the previous event.
+ * @throws IOException Occurs when there is an issue with reading the data file.
+ **/
+ public abstract void displayPreviousEvent() throws IOException;
+
+ /**
+ * The <code>EcalListener</code> class binds keys to actions.
+ * Bound actions include:
+ * [Right Arrow] :: Next event (stand-alone mode only)
+ * [Left Arrow ] :: Previous event (stand-alone mode only)
+ * b :: Toggle color-mapping for 0 energy crystals
+ * h :: Toggle selected crystal highlighting
+ * l :: Toggle logarithmic versus linear scaling
+ * s :: Saves the current display to a file
+ **/
+ private class EcalKeyListener implements KeyListener {
+ public void keyPressed(KeyEvent e) { }
+
+ public void keyReleased(KeyEvent e) {
+ // If right-arrow was pressed, go to the next event.
+ if (e.getKeyCode() == 39) {
+ try { displayNextEvent(); }
+ catch (IOException ex) {
+ System.err.println(ex.getMessage());
+ System.exit(1);
+ }
+ }
+
+ // If right-arrow was pressed, go to the next event.
+ else if (e.getKeyCode() == 37) {
+ try { displayPreviousEvent(); }
+ catch (IOException ex) {
+ System.err.println(ex.getMessage());
+ System.exit(1);
+ }
+ }
+
+ // 'b' toggles the default white background.
+ else if(e.getKeyCode() == 66) {
+ if(background) { ecalPanel.setDefaultCrystalColor(null); }
+ else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
+ background = !background;
+ }
+
+ // 'h' toggles highlighting the crystal under the cursor.
+ else if(e.getKeyCode() == 72) { ecalPanel.setSelectionHighlighting(!ecalPanel.isSelectionEnabled()); }
+
+ // 'l' toggles linear or logarithmic scaling.
+ else if(e.getKeyCode() == 76) {
+ if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
+ else { ecalPanel.setScalingLinear(); }
+ }
+
+ // 's' saves the panel to a file.
+ else if(e.getKeyCode() == 83) {
+ // Make a new buffered image on which to draw the content pane.
+ BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
+ getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
+
+ // Paint the content pane to image.
+ getContentPane().paint(screenshot.getGraphics());
+
+ // Get the lowest available file name.
+ int fileNum = 0;
+ File imageFile = new File("screenshot_" + fileNum + ".png");
+ while(imageFile.exists()) {
+ fileNum++;
+ imageFile = new File("screenshot_" + fileNum + ".png");
+ }
+
+ // Save the image to a PNG file.
+ try { ImageIO.write(screenshot, "PNG", imageFile); }
+ catch(IOException ioe) {
+ System.err.println("Error saving file \"screenshot.png\".");
+ }
+ System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
+ }
+
+ // Otherwise, print out the key code for the pressed key.
+ else { System.out.printf("Key Code: %d%n", e.getKeyCode()); }
+ }
+
+ public void keyTyped(KeyEvent e) { }
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/CalorimeterPanel.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/CalorimeterPanel.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,1347 @@
+package org.hps.monitoring.ecal.ui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.swing.JPanel;
+
+import org.hps.monitoring.ecal.event.Association;
+import org.hps.monitoring.ecal.util.MultiGradientScale;
+
+/**
+ * The class <code>CalorimeterPanel</code> handles the rendering of the
+ * calorimeter crystals as well as mapping colors to their values,
+ * rendering a color scale, and marking cluster crystals.
+ *
+ * @author Kyle McCarty
+ **/
+public final class CalorimeterPanel extends JPanel {
+ // Java-suggested variable.
+ private static final long serialVersionUID = 6292751227464151897L;
+ // The color used for rendering seed hits.
+ private Color clusterColor = Color.GREEN;
+ // The default color of the calorimeter crystals.
+ private Color defaultColor = null;
+ // The color if disabled crystals.
+ private Color disabledColor = Color.BLACK;
+ // The color for the selected crystal.
+ private Color selectedColor = new Color(90, 170, 250);
+ // Whether to allow highlighting of the selected crystal.
+ private boolean enabledSelection = true;
+ // Whether the crystals should redraw automatically or not.
+ private boolean suppress = false;
+ // The color-mapping scale used by to color calorimeter crystals.
+ private MultiGradientScale scale = MultiGradientScale.makeRainbowScale(0.0, 1.0);
+ // The number of boxes in the x-direction.
+ private int xBoxes = 1;
+ // The number of boxes in the y-direction.
+ private int yBoxes = 1;
+ // The width of the scale.
+ private int scaleWidth = 75;
+ // Store the crystal panels.
+ private Crystal[][] crystal;
+ // Store the highest and lowest energy value observed.
+ private double[] extremum = { Double.MAX_VALUE, 0.0 };
+ // Store the currently selected crystal.
+ private Point selectedCrystal = null;
+ // The panel on which the scale is rendered.
+ private ScalePanel scalePanel = new ScalePanel();
+
+ // Efficiency variables for crystal placement.
+ private int[] xPosition;
+ private int[] yPosition;
+ private int[] clusterSpace = new int[3];
+
+ // ================================================================================
+ // === Constructors ===============================================================
+ // ================================================================================
+
+ /**
+ * <b>EcalPanel</b><br/><br/>
+ * Initializes the calorimeter panel.
+ * @param numXBoxes - The number of crystals in the x-direction.
+ * @param numYBoxes - The number of crystals in the y-direction.
+ **/
+ public CalorimeterPanel(int numXBoxes, int numYBoxes) {
+ // Initialize the base component.
+ super();
+
+ // Set the number of calorimeter crystals.
+ xBoxes = numXBoxes;
+ yBoxes = numYBoxes;
+
+ // Initialize the crystal arrays.
+ crystal = new Crystal[xBoxes][yBoxes];
+
+ for(int x = 0; x < xBoxes; x++) {
+ for(int y = 0; y < yBoxes; y++) {
+ crystal[x][y] = new Crystal();
+ add(crystal[x][y]);
+ }
+ }
+
+ // Initialize the size arrays,
+ xPosition = new int [xBoxes + 1];
+ yPosition = new int[yBoxes + 1];
+
+ // Add the scale panel.
+ setLayout(null);
+ add(scalePanel);
+ }
+
+ // ================================================================================
+ // === Methods ====================================================================
+ // ================================================================================
+
+ /**
+ * <b>addAssociation</b><br/><br/>
+ * <code>public void <b>addAssociation</b>(Association crystalAssociation)</code><br/><br/>
+ * Connects the parent crystal to the child crystal such that when
+ * the parent crystal is active, the child will be highlighted with
+ * the highlight color set in the <code>Asscoiation</code> object.
+ * @param crystalAssociation
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates for either the parent or child crystals do
+ * not correspond to a crystal.
+ */
+ public void addAssociation(Association crystalAssociation) throws IndexOutOfBoundsException {
+ // Validate the parent crystal's indices.
+ Point parent = crystalAssociation.getParentCrystal();
+ if(validateIndices(parent)) {
+ // Validate the parent crystal's indices.
+ Point child = crystalAssociation.getChildCrystal();
+ if(validateIndices(child)) {
+ // If both crystal index sets are valid, add the association.
+ crystal[parent.x][parent.y].addAssociation(crystalAssociation);
+ }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid child crystal address (%2d, %2d).", parent.x, parent.y)); }
+ }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid parent crystal address (%2d, %2d).", parent.x, parent.y)); }
+ }
+
+ /**
+ * <b>addCrystalEnergy</b><br/><br/>
+ * <code>public void <b>addCrystalEnergy</b>(int xIndex, int yIndex, double energy)</code><br/><br/>
+ * Adds the indicated quantity of energy to the crystal at the given
+ * coordinates.
+ * @param ix - The x-index of the crystal.
+ * @param iy - The y-index of the crystal.
+ * @param energy - The energy to add.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ **/
+ public void addCrystalEnergy(int ix, int iy, double energy) throws IndexOutOfBoundsException {
+ if (validateIndices(ix, iy)) { crystal[ix][iy].addEnergy(energy); }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", ix, iy)); }
+ }
+
+ /**
+ * <b>addCrystalEnergy</b><br/><br/>
+ * <code>public void <b>addCrystalEnergy</b>(Point ixy, double energy)</code><br/><br/>
+ * Adds the indicated quantity of energy to the crystal at the given
+ * coordinates.
+ * @param ixy - The crystal's x/y-indices.
+ * @param energy - The energy to add.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ **/
+ public void addCrystalEnergy(Point ixy, double energy) throws IndexOutOfBoundsException {
+ addCrystalEnergy(ixy.x, ixy.y, energy);
+ }
+
+ /**
+ * <b>autoScale</b><br/><br/>
+ * <code>public void <b>autoScale</b>()</code><br/><br/>
+ * Chooses a maximum and minimum value for the scale that goes
+ * from slightly under the smallest recorded value to the highest
+ * recorded value.
+ */
+ public void autoScale() {
+ // If there is no energy on the calorimeter, just set the
+ // scale to some default value.
+ if(extremum[0] == Double.MAX_VALUE && extremum[1] == 0.0) {
+ scale.setMaximum(1.0);
+ scale.setMinimum(0.01);
+ }
+
+ // If the minimum is defined, set the scale such that it
+ // will map that value to the low end and the highest value
+ // to the top.
+ else if(extremum[0] != Double.MAX_VALUE) {
+ scale.setMaximum(extremum[1]);
+ scale.setMinimum(extremum[0] / 2);
+ }
+ }
+
+ /**
+ * <b>clearCrystals</b><br/><br/>
+ * <code>public void <b>clearCrystals</b>()</code><br/><br/>
+ * Sets all crystal energies to zero, removes all clusters, and
+ * clears all highlighting. This <b>does not</b> enable disabled
+ * crystals.
+ **/
+ public void clearCrystals() {
+ for (int ix = 0; ix < xBoxes; ix++) {
+ for (int iy = 0; iy < yBoxes; iy++) {
+ crystal[ix][iy].setState(0.0, false, null);
+ crystal[ix][iy].clearAssociations();
+ extremum[0] = Double.MAX_VALUE;
+ extremum[1] = 0.0;
+ }
+ }
+ }
+
+ /**
+ * <b>clearHighlight</b><br/><br/>
+ * <code>public void <b>clearHighlight</b>()</code><br/><br/>
+ * Clears any highlighting on the crystals.
+ */
+ public void clearHighlight() {
+ for (int x = 0; x < xBoxes; x++) {
+ for (int y = 0; y < yBoxes; y++) { crystal[x][y].setHighlight(null); }
+ }
+ }
+
+ /**
+ * <b>clearSelectedCrystal</b><br/><br/>
+ * <code>public void <b>clearSelectedCrystal</b>()</code><br/><br/>
+ * Clears crystal selection and sets it to <code>null</code>.
+ */
+ public void clearSelectedCrystal() {
+ // Clear any currently selected crystals.
+ if(selectedCrystal != null) { crystal[selectedCrystal.x][selectedCrystal.y].setSelected(false); }
+
+ // Set the selected crystal to null.
+ selectedCrystal = null;
+ }
+
+ /**
+ * <b>getCrystalEnergy</b><br/><br/>
+ * <code>public double <b>getCrystalEnergy</b>(int ix, int iy)</code><br/><br/>
+ * Provides the energy stored in the indicated crystal.
+ * @param ix - The crystal's x-index.
+ * @param iy - The crystal's y-index.
+ * @return Returns the energy as a <code>double</code>.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public double getCrystalEnergy(int ix, int iy) throws IndexOutOfBoundsException {
+ if(validateIndices(ix, iy)) { return crystal[ix][iy].getEnergy(); }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", ix, iy)); }
+ }
+
+ /**
+ * <b>getCrystalEnergy</b><br/><br/>
+ * <code>public double <b>getCrystalEnergy</b>(Point ixy)</code><br/><br/>
+ * Provides the energy stored in the indicated crystal.
+ * @param ixy - The crystal's x/y-indices.
+ * @return Returns the energy as a <code>double</code>.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public double getCrystalEnergy(Point ixy) throws IndexOutOfBoundsException {
+ return getCrystalEnergy(ixy.x, ixy.y);
+ }
+
+ /**
+ * <b>getCrystalHighlight</b><br/><br/>
+ * <code>public Color <b>getCrystalHighlight</b>(int ix, int iy)</code><br/><br/>
+ * Provides the highlight color for the indicated crystal.
+ * @param ix - The crystal's x-index.
+ * @param iy - The crystal's y-index.
+ * @return Returns the highlight color as a <code>Color</code>
+ * object.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public Color getCrystalHighlight(int ix, int iy) throws IndexOutOfBoundsException {
+ if(validateIndices(ix, iy)) { return crystal[ix][iy].getHighlight(); }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", ix, iy)); }
+ }
+
+ /**
+ * <b>getCrystalHighlight</b><br/><br/>
+ * <code>public Color <b>getCrystalHighlight</b>(Point ixy)</code><br/><br/>
+ * Provides the highlight color for the indicated crystal.
+ * @param ixy - The crystal's x/y-indices.
+ * @return Returns the highlight color as a <code>Color</code>
+ * object.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public Color getCrystalHighlight(Point ixy) throws IndexOutOfBoundsException {
+ return getCrystalHighlight(ixy.x, ixy.y);
+ }
+
+ /**
+ * <b>getCrystalID</b><br/><br/>
+ * <code>public Point <b>getCrystalID</b>(int xCoor, int yCoor)</code><br/><br/>
+ * Determines the panel crystal index of the crystal at the given
+ * panel coordinates.
+ * @param xCoor - The x-coordinate on the panel.
+ * @param yCoor - The y-coordinate on the panel.
+ * @return Returns a <code>Point</code> object containing the panel
+ * crystal indices of the crystal at the given panel coordinates.
+ * Returns <code>null</code> if the coordinates do not map to a crystal.
+ */
+ public Point getCrystalID(int xCoor, int yCoor) {
+ // If either coordinate is negative, return the null result.
+ if(xCoor < 0 || yCoor < 0) { return null; }
+
+ // If either coordinate is too large, return the nul result.
+ if(xCoor > xPosition[xBoxes] || yCoor > yPosition[yBoxes]) {
+ return null;
+ }
+
+ // Make a point to identify the crystal index.
+ Point loc = new Point(-1, -1);
+
+ // Determine which y index it is.
+ for(int y = 0; y < yBoxes; y++) {
+ if(yCoor <= yPosition[y + 1]) {
+ loc.y = y;
+ break;
+ }
+ }
+
+ // Determine which x index it is.
+ for(int x = 0; x < xBoxes; x++) {
+ if(xCoor <= xPosition[x + 1]) {
+ loc.x = x;
+ break;
+ }
+ }
+
+ // If either coordinate is not valid, return null.
+ if(loc.x == -1 || loc.y == -1) { return null; }
+
+ // Return the crystal identifier.
+ return loc;
+ }
+
+ /**
+ * <b>getCrystalID</b><br/><br/>
+ * <code>public Point <b>getCrystalID</b>(Point panelCoor)</code><br/><br/>
+ * Determines the panel crystal index of the crystal at the given
+ * panel coordinates.
+ * @param panelCoor - The x/y-coordinates on the panel.
+ * @return Returns a <code>Point</code> object containing the panel
+ * crystal indices of the crystal at the given panel coordinates.
+ * Returns <code>null</code> if the coordinates do not map to a crystal.
+ */
+ public Point getCrystalID(Point panelCoor) { return getCrystalID(panelCoor.x, panelCoor.y); }
+
+ /**
+ * <b>getCrystalBounds</b><br/><br/>
+ * <code>public Dimension <b>getCrystalBounds</b>()</code><br/><br/>
+ * Returns calorimeter panel's width and height in crystals.
+ * @return Returns the number of crystals are on the calorimeter
+ * in width and height.
+ */
+ public Dimension getCrystalBounds() { return new Dimension(xBoxes, yBoxes); }
+
+ /**
+ * <b>getNeighbors</b><br/><br/>
+ * <code>public Set<Point> <b>getNeighbors</b>(int cix, int ciy)</code><br/><br/>
+ * Gets the set of valid crystals that immediately surround the
+ * central crystal. Valid crystals must both have valid indices
+ * and also be enabled.
+ * @param cix - The x-index of the central crystal.
+ * @param ciy - The y-index of the central crystal.
+ * @return Returns the neighboring crystals as a <code>Set</code>
+ * of <code>Point</code> objects, with each element containing the
+ * coordinates of a valid neighbor.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y central crystal coordinates do not correspond to a crystal.
+ */
+ public Set<Point> getNeighbors(int cix, int ciy) { return getNeighbors(new Point(cix, ciy)); }
+
+ /**
+ * <b>getNeighbors</b><br/><br/>
+ * <code>public Set<Point> <b>getNeighbors</b>(Point centralCrystal)</code><br/><br/>
+ * Gets the set of valid crystals that immediately surround the
+ * central crystal. Valid crystals must both have valid indices
+ * and also be enabled.
+ * @param centralCrystal - What crystal the neighbors should surround.
+ * @return Returns the neighboring crystals as a <code>Set</code>
+ * of <code>Point</code> objects, with each element containing the
+ * coordinates of a valid neighbor.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y central crystal coordinates do not correspond to a crystal.
+ */
+ public Set<Point> getNeighbors(Point centralCrystal) throws IndexOutOfBoundsException {
+ // Make sure that the root is a valid crystal.
+ if(!validateIndices(centralCrystal)) {
+ throw new IndexOutOfBoundsException(String.format("Invalid central crystal address (%2d, %2d).",
+ centralCrystal.x, centralCrystal.y));
+ }
+
+ // Make a set to store the neighbors in.
+ HashSet<Point> neighborSet = new HashSet<Point>();
+
+ // Check all the crystals in a 3-by-3 square around the root.
+ // If they are valid crystals and they are not disable, then
+ // they are neighbors.
+ for(int xMod = -1; xMod <= 1; xMod++) {
+ for(int yMod = -1; yMod <= 1; yMod++) {
+ // Get the possible neighbor.
+ Point possibleNeighbor = new Point(centralCrystal.x + xMod, centralCrystal.y + yMod);
+
+ // Make sure that the possible neighbor is neither the
+ // root crystal nor invalid.
+ boolean isRoot = centralCrystal.equals(possibleNeighbor);
+ boolean isValid = validateIndices(possibleNeighbor);
+
+ // If the neighbor passes these tests, add it to the set
+ // if it is active.
+ if(!isRoot && isValid) {
+ if(!crystal[possibleNeighbor.x][possibleNeighbor.y].isDisabled()) {
+ neighborSet.add(possibleNeighbor);
+ }
+ }
+ }
+ }
+
+ // Return the neighbor set.
+ return neighborSet;
+ }
+
+ /**
+ * <b>getSelectedCrystal</b><br/><br/>
+ * <code>public Point <b>getSelectedCrystal</b>()</code><br/><br/>
+ * Gives the x/y indices for the currently selected crystal.
+ * @return Returns the x/y indices in a <code>Point</code> object.
+ * If no crystal is currently selected, returns <code>null</code>.
+ */
+ public Point getSelectedCrystal() { return selectedCrystal; }
+
+ /**
+ * <b>isCluster</b><br/><br/>
+ * <code>public boolean <b>isCluster</b></code>(int ix, int iy)<br/><br/>
+ * Determines if the crystal at the given coordinates is a cluster
+ * center or not.
+ * @param ix - The x-index of the crystal.
+ * @param iy - The y-index of the crystal.
+ * @return Returns <code>true</code> if the crystal is a cluster
+ * center and <code>false</code> if it is not or if the indices
+ * are invalid.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public boolean isCrystalCluster(int ix, int iy) throws IndexOutOfBoundsException {
+ if(validateIndices(ix, iy)) { return crystal[ix][iy].isClusterCenter(); }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", ix, iy)); }
+ }
+
+ /**
+ * <b>isCluster</b><br/><br/>
+ * <code>public boolean <b>isCluster</b></code>(Point ixy)<br/><br/>
+ * Determines if the crystal at the given coordinates is a cluster
+ * center or not.
+ * @param ixy - The crystal's x/y-indices.
+ * @return Returns <code>true</code> if the crystal is a cluster
+ * center and <code>false</code> if it is not or if the indices
+ * are invalid.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public boolean isCrystalCluster(Point ixy) throws IndexOutOfBoundsException {
+ return isCrystalCluster(ixy.x, ixy.y);
+ }
+
+ /**
+ * <b>isCrystalDisabled</b><br/><br/>
+ * <code>public boolean <b>isCrystalDisabled</b></code>(int ix, int iy)<br/><br/>
+ * Determines if the crystal at the given coordinates is a active
+ * or not.
+ * @param xCoor - The x-index of the crystal.
+ * @param yCoor - The y-index of the crystal.
+ * @return Returns <code>true</code> if the crystal is active
+ * and <code>false</code> if it is not.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public boolean isCrystalDisabled(int ix, int iy) throws IndexOutOfBoundsException {
+ if(validateIndices(ix, iy)) { return crystal[ix][iy].isDisabled(); }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", ix, iy)); }
+ }
+
+ /**
+ * <b>isCrystalDisabled</b><br/><br/>
+ * <code>public boolean <b>isCrystalDisabled</b></code>(Point ixy)<br/><br/>
+ * Determines if the crystal at the given coordinates is a active
+ * or not.
+ * @param ixy - The crystal's x/y-indices.
+ * @return Returns <code>true</code> if the crystal is active
+ * and <code>false</code> if it is not.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public boolean isCrystalDisabled(Point ixy) throws IndexOutOfBoundsException {
+ return isCrystalDisabled(ixy.x, ixy.y);
+ }
+
+ /**
+ * <b>isScalingLinear</b><br/><br/>
+ * <code>public boolean <b>isScalingLinear</b></code>()<br/><br/>
+ * Indicates whether the crystal colors are mapped linearly.
+ * @return Returns <code>true</code> if the mapping is linear
+ * and <code>false</code> otherwise.
+ */
+ public boolean isScalingLinear() { return scale.isLinear(); }
+
+ /**
+ * <b>isScalingLogarithmic</b><br/><br/>
+ * <code>public boolean <b>isScalingLogarithmic</b></code>()<br/><br/>
+ * Indicates whether the crystal colors are mapped logarithmically.
+ * @return Returns <code>true</code> if the mapping is logarithmic
+ * and <code>false</code> otherwise.
+ */
+ public boolean isScalingLogarithmic() { return scale.isLogarithmic(); }
+
+ /**
+ * <b>isSelectionEnabled</b><br/><br/>
+ * <code>public boolean <b>isSelectionEnabled</b></code>()<br/><br/>
+ * Indicates whether highlighting of the currently selected crystal
+ * is active or not.
+ * @return Returns <code>true</code> if the currently selected
+ * crystal will be highlighted and <code>false</code> otherwise.
+ */
+ public boolean isSelectionEnabled() { return enabledSelection; }
+
+ /**
+ * <b>setClusterColor</b><br/><br/>
+ * <code>public void <b>setClusterColor</b>(Color c)</code><br/><br/>
+ * Sets the color of the cluster center marker.
+ * @param c - The color to be used for cluster center markers. A
+ * value of <code>null</code> will result in seed hit markers being
+ * the inverse color of the crystal in which they appear.
+ **/
+ public void setClusterColor(Color c) { clusterColor = c; }
+
+ /**
+ * <b>setCrystalCluster</b><br/><br/>
+ * <code>public void <b>setCrystalCluster</b>(int ix, int iy, boolean cluster)</code><br/><br/>
+ * Sets whether a crystal is also the location of a cluster center.
+ * @param ix - The x-index of the crystal.
+ * @param iy - The y-index of the crystal.
+ * @param cluster - This should be <code>true</code> if there
+ * is a cluster center and <code>false</code> if there is not.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ **/
+ public void setCrystalCluster(int ix, int iy, boolean cluster) throws IndexOutOfBoundsException {
+ if (validateIndices(ix, iy)) { crystal[ix][iy].setClusterCenter(cluster); }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", ix, iy)); }
+ }
+
+ /**
+ * <b>setCrystalCluster</b><br/><br/>
+ * <code>public void <b>setCrystalCluster</b>(Point ixy, boolean cluster)</code><br/><br/>
+ * Sets whether a crystal is also the location of a seed hit.
+ * @param ixy - The crystal's x/y-indices.
+ * @param cluster - This should be <code>true</code> if there
+ * is a cluster center and <code>false</code> if there is not.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ **/
+ public void setCrystalCluster(Point ixy, boolean cluster) throws IndexOutOfBoundsException {
+ setCrystalCluster(ixy.x, ixy.y, cluster);
+ }
+
+ /**
+ * <b>setCrystalEnabled</b><br/><br/>
+ * <code>public void <b>setCrystalEnabled</b>(int ix, int iy, boolean active)</code><br/><br/>
+ * Sets whether the indicated crystal is enabled or not.
+ * @param ix - The x-index of the crystal.
+ * @param iy - The y-index of the crystal.
+ * @param active - This should be <code>true</code> if the crystal is
+ * active and <code>false</code> if it is not.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ **/
+ public void setCrystalEnabled(int ix, int iy, boolean active) throws IndexOutOfBoundsException {
+ if (validateIndices(ix, iy)) { crystal[ix][iy].setDisabled(!active); }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", ix, iy)); }
+ }
+
+ /**
+ * <b>setCrystalEnabled</b><br/><br/>
+ * <code>public void <b>setCrystalEnabled</b>(Point ixy, boolean active)</code><br/><br/>
+ * Sets whether the indicated crystal is enabled or not.
+ * @param ixy - The crystal's x/y-indices.
+ * @param active - This should be <code>true</code> if the crystal is
+ * active and <code>false</code> if it is not.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ **/
+ public void setCrystalEnabled(Point ixy, boolean active) throws IndexOutOfBoundsException {
+ setCrystalEnabled(ixy.x, ixy.y, active);
+ }
+
+ /**
+ * <b>setCrystalHighlight</b><br/><br/>
+ * <code>public void <b>setCrystalHighlight</b>(int ix, int iy, Color highlight)</code><br/><br/>
+ * @param ix - The x-index of the crystal.
+ * @param iy - The y-index of the crystal.
+ * @param highlight - The color which the indicated crystal should
+ * be highlighted. A value of <code>null</code> indicates that no
+ * highlight should be used.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public void setCrystalHighlight(int ix, int iy, Color highlight) throws IndexOutOfBoundsException {
+ if (validateIndices(ix, iy)) { crystal[ix][iy].setHighlight(highlight); }
+ else { throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", ix, iy)); }
+ }
+
+ /**
+ * <b>setCrystalHighlight</b><br/><br/>
+ * <code>public void <b>setCrystalHighlight</b>(Point ixy, Color highlight)</code><br/><br/>
+ * @param ixy - The crystal's x/y-indices.
+ * @param highlight - The color which the indicated crystal should
+ * be highlighted. A value of <code>null</code> indicates that no
+ * highlight should be used.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public void setCrystalHighlight(Point ixy, Color highlight) throws IndexOutOfBoundsException {
+ setCrystalHighlight(ixy.x, ixy.y, highlight);
+ }
+
+ /**
+ * <b>setCrystalDefaultColor</b><br/><br/>
+ * <code>public void <b>setCrystalDefaultColor</b>(Color c)</code><br/><br/>
+ * Sets the color that crystals with zero energy will display.
+ * @param c - The color to use for zero energy crystals. A value
+ * of <code>null</code> will use the appropriate energy color
+ * map value.
+ */
+ public void setDefaultCrystalColor(Color c) {
+ // Only update the crystals if the default color has changed.
+ if(c != defaultColor) {
+ // Store the new default color.
+ defaultColor = c;
+
+ // Inform the crystals of the change.
+ for(int ix = 0; ix < xBoxes; ix++) {
+ for(int iy = 0; iy < yBoxes; iy++) { crystal[ix][iy].setUseDefaultColor(c != null, false); }
+ }
+
+ // Reset the colors and repaint.
+ resetCrystalColors();
+ repaint();
+ }
+ }
+
+ /**
+ * <b>setScaleEnabled</b><br/><br/>
+ * <code>public void <b>setScaleEnabled</b>(boolean enabled)</code><br/><br/>
+ * Sets whether the scale should be visible or not.
+ * @param enabled - <code>true</code> indicates that the scale should
+ * be visible and <code>false</code> that it should be hidden.
+ **/
+ public void setScaleEnabled(boolean enabled) {
+ if (scalePanel.isVisible() != enabled) {
+ scalePanel.setVisible(enabled);
+ }
+ }
+
+ /**
+ * <b>setScaleMaximum</b><br/><br/>
+ * <code>public void <b>setScaleMaximum</b>(double maximum)</code><br/><br/>
+ * Sets the maximum value of the color mapping scale. Energies above this
+ * value will all be the same maximum color.
+ * @param maximum - The maximum energy to be mapped.
+ **/
+ public void setScaleMaximum(double maximum) {
+ scale.setMaximum(maximum);
+ }
+
+ /**
+ * <b>setScaleMinimum</b><br/><br>
+ * <code>public void <b>setScaleMinimum</b>(double minimum)</code><br/><br/>
+ * Sets the minimum value of the color mapping scale. Energies below this
+ * value will all be the same minimum color.
+ * @param minimum - The minimum energy to be mapped.
+ **/
+ public void setScaleMinimum(double minimum) {
+ scale.setMinimum(minimum);
+ }
+
+ /**
+ * <b>setScalingLinear</b><br/><br/>
+ * <code>public void <b>setScalingLinear</b>()<br/><br/>
+ * Sets the color mapping scale behavior to linear mapping.
+ **/
+ public void setScalingLinear() {
+ scale.setScalingLinear();
+ resetCrystalColors();
+ repaint();
+ }
+
+ /**
+ * <b>setScalingLogarithmic</b><br/><br/>
+ * <code>public void <b>setScalingLogarithmic</b>()</code><br/><br/>
+ * Sets the color mapping scale behavior to logarithmic mapping.
+ **/
+ public void setScalingLogarithmic() {
+ scale.setScalingLogarithmic();
+ resetCrystalColors();
+ repaint();
+ }
+
+ /**
+ * <b>setSelectedCrystal</b><br/><br/>
+ * <code>public void <b>setSelectedCrystal</b></code>(int ix, int iy)<br/><br/>
+ * Sets which crystal is currently selected.
+ * @param ix - The x-index of the crystal.
+ * @param iy - The y-index of the crystal.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ */
+ public void setSelectedCrystal(int ix, int iy) {
+ if (validateIndices(ix, iy)) {
+ if(selectedCrystal != null) { crystal[selectedCrystal.x][selectedCrystal.y].setSelected(false); }
+ crystal[ix][iy].setSelected(true);
+ selectedCrystal = new Point(ix, iy);
+ }
+ else {
+ throw new IndexOutOfBoundsException(String.format("Invalid crystal address (%2d, %2d).", ix, iy));
+ }
+ }
+
+ /**
+ * <b>setSelectedCrystal</b><br/><br/>
+ * <code>public void <b>setSelectedCrystal</b></code>(Point ixy)<br/><br/>
+ * Sets which crystal is currently selected.
+ * @param ixy - The crystal's x/y-indices.
+ * @throws IndexOutOfBoundsException Occurs when either of the given
+ * x/y crystal coordinates do not correspond to a crystal.
+ * @throws NullPointerException Occurs when the argument <code>Point
+ * </code> is <code>null</code>.
+ */
+ public void setSelectedCrystal(Point ixy) throws NullPointerException {
+ if(crystal != null) { setSelectedCrystal(ixy.x, ixy.y); }
+ else { throw new NullPointerException("Crystal ID must be defined."); }
+ }
+
+ /**
+ * <b>setSelectedCrystalHighlight</b><br/><br/>
+ * <code>public void <b>setSelectedCrystalHighlight</b>(Color c)</code><br/><br/>
+ * Sets the color in which selected crystals should be highlighted.
+ * @param c - The new selection highlight color.
+ * @throws IllegalArgumentException Occurs if the selection color
+ * is set to <code>null</code>.
+ */
+ public void setSelectedCrystalHighlight(Color c) throws IllegalArgumentException {
+ // We do not allow null values for the selected crystal color.
+ if(c == null) { throw new IllegalArgumentException("Crystal selection color can not be null."); }
+ else { selectedColor = c; }
+ }
+
+ /**
+ * <b>setSelectionHighlighting</b><br/><br/>
+ * <code>public void <b>setSelectionHighlighting</b>(boolean state)</code><br/><br/>
+ * Sets whether or not the currently selected crystal should be
+ * highlighted or not.
+ * @param state - <code>true</code> indicates that the selected
+ * crystals should be highlighted and <code>false</code> that
+ * it should not.
+ */
+ public void setSelectionHighlighting(boolean state) {
+ if(enabledSelection != state) {
+ enabledSelection = state;
+ if(selectedCrystal != null) { crystal[selectedCrystal.x][selectedCrystal.y].repaint(); }
+ }
+ }
+
+ public void setSize(Dimension d) { setSize(d.width, d.height); }
+
+ public void setSize(int width, int height) {
+ // Run the superclass method.
+ super.setSize(width, height);
+
+ // Resize the scale panel.
+ scalePanel.setLocation(width - scaleWidth, 0);
+ scalePanel.setSize(scaleWidth, height);
+
+ // Determine the width and heights of the calorimeter crystals.
+ if (scalePanel.isVisible()) { width = getWidth() - scaleWidth; }
+ else { width = getWidth(); }
+
+ int boxWidth = width / xBoxes;
+ int widthRem = width % xBoxes;
+ int boxHeight = height / yBoxes;
+ int heightRem = height % yBoxes;
+
+ // Set positioning and sizing variables.
+ int[] crystalRem = { widthRem, heightRem };
+ int curX = 0;
+ int curY = 0;
+
+ // Loop over the rows of crystals.
+ xPosition[0] = 0;
+ for(int x = 0; x < xBoxes; x++) {
+ // Get the appropriate width for a crystal.
+ int crystalWidth = boxWidth;
+ if(crystalRem[0] != 0) {
+ crystalWidth++;
+ crystalRem[0]--;
+ }
+
+ // Note the x-coordinate for this column.
+ xPosition[x + 1] = xPosition[x] + crystalWidth;
+
+ // Loop over the columns of crystals.
+ for(int y = 0; y < yBoxes; y++) {
+ // Get the appropriate height for a crystal.
+ int crystalHeight = boxHeight;
+ if(crystalRem[1] != 0) {
+ crystalHeight++;
+ crystalRem[1]--;
+ }
+
+ // Note the y-coordinate for this row.
+ yPosition[y + 1] = yPosition[y] + crystalHeight;
+
+ // Set the crystal size and location.
+ crystal[x][y].setSize(crystalWidth, crystalHeight);
+ crystal[x][y].setLocation(curX, curY);
+
+ // Increment the current y-coordinate.
+ curY += crystalHeight;
+ }
+
+ // Increment the current x-coordinate and reset the current y-coordinate.
+ curX += crystalWidth;
+ curY = 0;
+
+ // Reset the height remainder for the next column.
+ crystalRem[1] = heightRem;
+ }
+
+ // Calculate the cluster position variables.
+ double ltw = 0.25 * boxWidth;
+ double lth = 0.25 * boxHeight;
+
+ if(ltw > lth) {
+ clusterSpace[0] = (int)Math.round((boxWidth - lth - lth) / 2.0);
+ clusterSpace[1] = (int)Math.round(lth);
+ clusterSpace[2] = (int)Math.round(lth + lth);
+ }
+ else {
+ clusterSpace[0] = (int)Math.round(ltw);
+ clusterSpace[1] = (int)Math.round((boxHeight - ltw - ltw) / 2.0);
+ clusterSpace[2] = (int)Math.round(ltw + ltw);
+ }
+ }
+
+ /**
+ * <b>setSuppressRedraw</b><br/><br/>
+ * <code>public void <b>setSuppressRedraw</b>(boolean state)</code><br/><br/>
+ * Sets whether the panel crystals should repaint automatically
+ * whenever their state changes.
+ * @param state - <code>true</code> indicates that the crystal
+ * panels will repaint automatically, while <code>false</code>
+ * indicates that they will not repaint.
+ */
+ public void setSuppressRedraw(boolean state) { suppress = state; }
+
+ // ================================================================================
+ // === Private Methods ============================================================
+ // ================================================================================
+
+ /**
+ * <b>resetCrystalColors</b><br/><br/>
+ * <code>private void <b>resetCrystalColors</b>()</code><br/><br/>
+ * Forces all crystals to revalidate their colors.
+ */
+ private void resetCrystalColors() {
+ // Force all the crystals to update their colors.
+ for(int ix = 0; ix < xBoxes; ix++) {
+ for(int iy = 0; iy < yBoxes; iy++) { crystal[ix][iy].resetColor(); }
+ }
+ }
+
+ /**
+ * <b>validateIndices</b><br/><br/>
+ * <code>private boolean <b>validateIndices</b>(int ix, int iy)</code><br/><br/>
+ * Indicates whether the given indices corresponds to a valid
+ * crystal or not.
+ * @param ix - The crystal's x index.
+ * @param iy - The crystal's y index.
+ * @return Returns <code>true</code> if the indices are valid
+ * and <code>false</code> if they are not.
+ */
+ private boolean validateIndices(int ix, int iy) {
+ boolean lowX = (ix > -1);
+ boolean highX = (ix < xBoxes);
+ boolean lowY = (iy > -1);
+ boolean highY = (iy < yBoxes);
+
+ return (lowX && highX && lowY && highY);
+ }
+
+ /**
+ * <b>validateIndices</b><br/><br/>
+ * <code>private boolean <b>validateIndices</b>(Point p)</code><br/><br/>
+ * Indicates whether the given indices corresponds to a valid
+ * crystal or not.
+ * @param p - A <code>Point</code> object containing the crystal's
+ * x and y indices.
+ * @return Returns <code>true</code> if the indices are valid
+ * and <code>false</code> if they are not.
+ */
+ private boolean validateIndices(Point p) { return validateIndices(p.x, p.y); }
+
+ // ================================================================================
+ // === Private Internal Classes ===================================================
+ // ================================================================================
+
+ /**
+ * Class <code>Crystal</code> holds all pertinent information needed
+ * to display a calorimeter crystal in the panel. It also handles
+ * drawing itself.
+ * @author Kyle McCarty
+ */
+ private class Crystal extends JPanel {
+ private static final long serialVersionUID = -5666423016127997831L;
+ // The energy stored in the crystal.
+ private double energy = 0.0;
+ // Whether the crystal can store energy.
+ private boolean disabled = false;
+ // Whether the crystal is a cluster center.
+ private boolean cluster = false;
+ // Whether zero-energy crystals should use color mapping.
+ private boolean useDefaultColor = false;
+ // Whether the crystal is selected.
+ private boolean selected = false;
+ // What color the crystal should be highlighted in.
+ private Color highlight = null;
+ // Store associated crystals.
+ private ArrayList<Association> componentList = new ArrayList<Association>();
+
+ /**
+ * <b>Crystal</b><br/><br/>
+ * <code>public <b>Crystal</b>()</code><br/><br/>
+ * Initializes a new calorimeter crystal panel.
+ */
+ public Crystal() {
+ setOpaque(true);
+ resetColor();
+ }
+
+ /**
+ * <b>addAssociation</b><br/><br/>
+ * <code>public void <b>addAssociation</b>(Association a)</code><br/><br/>
+ * Adds a new associated crystal to this crystal.
+ * @param a - The <code>Association</code> object representing
+ * the associated crystal and its highlighting color.
+ */
+ public void addAssociation(Association a) {
+ // Add the association.
+ componentList.add(a);
+
+ // If this crystal is selected, then activate the new crystal.
+ if(selected) { setCrystalHighlight(a.getChildCrystal(), a.getHighlight()); }
+ }
+
+ /**
+ * <b>addEnergy</b><br/><br/>
+ * <code>public void <b>addEnergy</b>(double energy)</code><br/><br/>
+ * Increments the crystal's energy by the given amount.
+ * @param energy - The energy by which the crystal's stored
+ * energy should be increased.
+ */
+ public void addEnergy(double energy) { setEnergy(this.energy + energy); }
+
+ /**
+ * <b>clearAssociations</b><br/><br/>
+ * <code>public void <b>clearAssociations</b>()</code><br/><br/>
+ * Clears all the associated crystal from this crystal.
+ */
+ public void clearAssociations() {
+ // Remove the highlighting from any associated crystals,
+ // if this crystal is selected.
+ if(selected) {
+ for(Association a : componentList) { setCrystalHighlight(a.getChildCrystal(), null); }
+ }
+
+ // Clear the list.
+ componentList.clear();
+ }
+
+ /**
+ * <b>getEnergy</b><br/><br/>
+ * <code>public double <b>getEnergy</b>()</code><br/><br/>
+ * Indicates how much energy is stored in the crystal.
+ * @return Returns the crystal's energy as a <code>double</code>.
+ */
+ public double getEnergy() { return energy; }
+
+ /**
+ * <b>isClusterCenter</b><br/><br/>
+ * <code>public boolean <b>isClusterCenter</b>()</code><br/><br/>
+ * Indicates whether this crystal is also a cluster center.
+ * @return Returns <code>true</code> if the crystal is a cluster
+ * center and <code>false</code> if it is not.
+ */
[truncated at 1000 lines; 350 more skipped]
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/ClusterViewer.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/ClusterViewer.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,448 @@
+package org.hps.monitoring.ecal.ui;
+
+import org.hps.monitoring.ecal.io.EventManager;
+
+import java.awt.Color;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.imageio.ImageIO;
+
+import org.hps.monitoring.ecal.event.Association;
+import org.hps.monitoring.ecal.event.Cluster;
+import org.hps.monitoring.ecal.event.EcalHit;
+
+/**
+ * Class <code>FileViewer</code> is an implementation of the <code>
+ * Viewer</code> abstract class that reads events from a file data
+ * source. Any file type can be used, so long as it has a manager
+ * which implements the <code>EventManager</code> interface.
+ *
+ * @author Kyle McCarty
+ */
+public class ClusterViewer extends ActiveViewer {
+ private static final long serialVersionUID = 17058336873349781L;
+ // Stores whether the background color is set or not.
+ private boolean background = false;
+ // Store the index in the buffer of the displayed event.
+ private int bufferIndex;
+ // Map cluster location to a cluster object.
+ private HashMap<Point, Cluster> clusterMap = new HashMap<Point, Cluster>();
+ // Store the cluster list for the currently displayed event.
+ private List<Cluster> eventClusters;
+ // Store the event energies in a buffer.
+ private LinkedList<Double[][]> eventEnergyBuffer;
+ // Store the event hits in a buffer.
+ private LinkedList<List<EcalHit>> eventHitBuffer;
+ // Store the size of the event window.
+ private int eventWindow;
+ // Additional status display field names for this data type.
+ private static final String[] fieldNames = { "Shared Hits", "Component Hits", "Cluster Energy", "Buffer Index" };
+
+ /**
+ * <b>ClusterViewer</b><br/><br/>
+ * <code>public <b>ClusterViewer</b>()</code><br/><br/>
+ * Constructs a new <code>Viewer</code> for displaying data read
+ * from a file.
+ * @param dataSource - The <code>EventManager</code> responsible
+ * for reading data from a file.
+ * @throws NullPointerException Occurs if the event manager is
+ * <code>null</code>.
+ */
+ public ClusterViewer(EventManager dataSource, int eventWindow) throws NullPointerException {
+ // Pass any additional fields required by the event manager
+ // to the underlying Viewer object to be added to the status
+ // display panel.
+ super(dataSource, fieldNames);
+
+ // Define the event window and initialize the event data.
+ this.eventWindow = eventWindow;
+ eventEnergyBuffer = new LinkedList<Double[][]>();
+ eventHitBuffer = new LinkedList<List<EcalHit>>();
+
+ // Prepare the event buffer to display the first event.
+ try {
+ // Make an empty array. At the start, there are no previous
+ // events to load.
+ Double[][] emptyArray = new Double[46][11];
+ for(int x = 0; x < 46; x++) {
+ for(int y = 0; y < 11; y++) { emptyArray[x][y] = new Double(0.0); }
+ }
+
+ // Populate the eventWindow before section of the buffer
+ // with the empty events.
+ for(int i = 0; i <= eventWindow; i++) {
+ eventEnergyBuffer.addFirst(emptyArray);
+ eventHitBuffer.addFirst(new ArrayList<EcalHit>());
+ }
+
+ // Fill the rest of the array with future events.
+ for(int i = 0; i < eventWindow; i++) {
+ em.nextEvent();
+ eventEnergyBuffer.addFirst(toEnergyArray(em.getHits()));
+ eventHitBuffer.addFirst(em.getHits());
+ }
+ }
+ catch(java.io.IOException e) { System.exit(1); }
+
+ // Make a key listener to change events.
+ addKeyListener(new EcalKeyListener());
+ }
+
+ /**
+ * <b>displayNextEvent</b><br/><br/>
+ * <code>public void <b>displayNextEvent</b>()</code><br/><br/>
+ * Feeds the calorimeter panel the data from the next event.
+ * @throws IOException Occurs when there is an issue with reading the data file.
+ **/
+ public void displayNextEvent() throws IOException { getEvent(true); }
+
+ /**
+ * <b>displayPreviousEvent</b><br/><br/>
+ * <code>public void <b>displayPreviousEvent</b>()</code><br/><br/>
+ * Feeds the calorimeter panel the data from the previous event.
+ * @throws IOException Occurs when there is an issue with reading the data file.
+ **/
+ public void displayPreviousEvent() throws IOException { getEvent(false); }
+
+ public List<Cluster> getClusters() {
+ // Get the set of hits in the middle of the buffer. This is
+ // the "current" event.
+ List<EcalHit> activeEvent = eventHitBuffer.get(eventWindow);
+
+ // Store clusters.
+ ArrayList<Cluster> clusterList = new ArrayList<Cluster>();
+
+ // For each hit, check if it meets the criteria for a cluster.
+ for(EcalHit hit : activeEvent) {
+ // Track whether this hit is a cluster.
+ boolean isCluster = true;
+
+ // Track the current hit's cluster energy.
+ double clusterEnergy = 0.0;
+
+ // Convert the current hit to the proper coordinates.
+ Point hitLoc = toPanelPoint(hit.getLocation());
+
+ // Track which crystals are part of the cluster.
+ HashSet<Point> componentSet = new HashSet<Point>();
+
+ // Get the set of the current hit's neighbors.
+ Set<Point> neighbors = ecalPanel.getNeighbors(hitLoc);
+
+ // Loop through the buffer and perform comparisons.
+ for(Double[][] event : eventEnergyBuffer) {
+ // Increment the cluster energy by the hit's energy at
+ // the current time in the buffer.
+ clusterEnergy += event[hitLoc.x][hitLoc.y];
+
+ // A hit must be larger than itself at all other times
+ // stored in the buffer.
+ if(event[hitLoc.x][hitLoc.y] > hit.getEnergy()) {
+ isCluster = false;
+ break;
+ }
+
+ // A hit must be larger than its immediate neighbors
+ // at all times in the buffer as well.
+ for(Point neighbor : neighbors) {
+ // Increment the cluster energy by the neighbor's
+ // energy at the current time in the buffer.
+ clusterEnergy += event[neighbor.x][neighbor.y];
+
+ // Check that the neighbor's energy is not higher
+ // than the present hit's.
+ if(event[neighbor.x][neighbor.y] > hit.getEnergy()) {
+ isCluster = false;
+ break;
+ }
+
+ // If this neighbor has a non-zero energy, it is
+ // a component of the potential cluster.
+ if(event[neighbor.x][neighbor.y] != 0) { componentSet.add(neighbor); }
+ }
+ }
+
+ // If the current hit did not fail any of the preceding
+ // checks, then it is a cluster and should be added to
+ // the cluster list.
+ if(isCluster) {
+ Cluster cluster = new Cluster(hit.getLocation(), clusterEnergy);
+ for(Point neighbor : componentSet) { cluster.addComponentHit(toEcalPoint(neighbor)); }
+ clusterList.add(cluster);
+ }
+ }
+
+ // Return the list of clusters.
+ return clusterList;
+ }
+
+ protected void updateStatusPanel() {
+ super.updateStatusPanel();
+
+ // Get the currently selected crystal.
+ Point crystal = ecalPanel.getSelectedCrystal();
+
+ // If the active crystal is not null, see if it is a cluster.
+ if(crystal != null) {
+ // Get the cluster associated with this point.
+ Cluster activeCluster = clusterMap.get(crystal);
+
+ // If the cluster is null, we set everything to undefined.
+ if(activeCluster == null) {
+ for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); }
+ }
+
+ // Otherwise, define the fields based on the cluster.
+ else {
+ // Get the shared and component hit counts.
+ setStatusField(fieldNames[0], Integer.toString(activeCluster.getSharedHitCount()));
+ setStatusField(fieldNames[1], Integer.toString(activeCluster.getComponentHitCount()));
+
+ // Format the cluster energy, or account for it if it
+ // doesn't exist.
+ String energy;
+ if(activeCluster.getClusterEnergy() != Double.NaN) {
+ DecimalFormat formatter = new DecimalFormat("0.####E0");
+ energy = formatter.format(activeCluster.getClusterEnergy());
+ }
+ else { energy = "---"; }
+ setStatusField(fieldNames[2], energy);
+ }
+ }
+ // Otherwise, clear the field values.
+ else { for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); } }
+
+ // Write the current buffer index.
+
+ setStatusField(fieldNames[3], Integer.toString(eventWindow - bufferIndex));
+ }
+
+ /**
+ * <b>displayEvent</b><br/><br/>
+ * <code>private void <b>displayEvent</b></code><br/><br/>
+ * Displays the given lists of hits and clusters on the calorimeter
+ * panel.
+ * @param hitList - A list of hits for the current event.
+ * @param clusterList - A list of clusters for the current event.
+ */
+ private void displayEvent(List<EcalHit> hitList, List<Cluster> clusterList) {
+ // Suppress the calorimeter panel.
+ ecalPanel.setSuppressRedraw(true);
+
+ // Display the hits.
+ for (EcalHit h : hitList) {
+ int ix = toPanelX(h.getX());
+ int iy = toPanelY(h.getY());
+ ecalPanel.addCrystalEnergy(ix, iy, h.getEnergy());
+ }
+
+ // Display the clusters.
+ for(Cluster cluster : clusterList) {
+ Point rawCluster = cluster.getClusterCenter();
+ Point clusterCenter = toPanelPoint(rawCluster);
+ ecalPanel.setCrystalCluster(clusterCenter.x, clusterCenter.y, true);
+
+ // Add component hits to the calorimeter panel.
+ for(Point ch : cluster.getComponentHits()) {
+ ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(ch), HIGHLIGHT_CLUSTER_COMPONENT));
+ }
+
+ // Add shared hits to the calorimeter panel.
+ for(Point sh : cluster.getSharedHits()) {
+ ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(sh), HIGHLIGHT_CLUSTER_SHARED));
+ }
+ }
+
+ // Stop suppressing the panel and order it to redraw.
+ ecalPanel.setSuppressRedraw(false);
+ ecalPanel.repaint();
+
+ // Update the status panel to account for the new event.
+ updateStatusPanel();
+ }
+
+ /**
+ * <b>getEvent</b><br/><br/>
+ * <code>private void <b>getEvent</b>(boolean forward)</code><br/><br/>
+ * Reads either the next or the previous event from the event manager.
+ * @param forward - Whether the event data should be read forward
+ * or backward.
+ * @throws IOException Occurs when there is an issue with reading the data file.
+ */
+ private void getEvent(boolean forward) throws IOException {
+ // Clear the calorimeter panel.
+ ecalPanel.clearCrystals();
+
+ // If there is no data source, we can not do anything.
+ if (em == null) { return; }
+
+ // Otherwise, get the next event.
+ if(forward) { em.nextEvent(); }
+ else { em.previousEvent(); }
+
+ // Remove the last buffer event and add the new one.
+ if(forward) {
+ eventEnergyBuffer.removeLast();
+ eventHitBuffer.removeLast();
+ eventEnergyBuffer.addFirst(toEnergyArray(em.getHits()));
+ eventHitBuffer.addFirst(em.getHits());
+ }
+ else {
+ eventEnergyBuffer.removeFirst();
+ eventHitBuffer.removeFirst();
+ eventEnergyBuffer.addLast(toEnergyArray(em.getHits()));
+ eventHitBuffer.addLast(em.getHits());
+ }
+
+ // Determine if any of the hits in the active event are
+ // clusters. The active event is the event in the middle of
+ // the event buffer (i.e. index eventWindow).
+ eventClusters = getClusters();
+
+ // Load the cluster map.
+ clusterMap.clear();
+ for(Cluster c : eventClusters) { clusterMap.put(toPanelPoint(c.getClusterCenter()), c); }
+
+ // Update the displayed buffer index.
+ bufferIndex = eventWindow;
+
+ // Display it.
+ displayEvent(eventHitBuffer.get(eventWindow), eventClusters);
+ }
+
+ private Double[][] toEnergyArray(List<EcalHit> hits) {
+ // Define the energy array.
+ Double[][] energy = new Double[46][11];
+ for(int x = 0; x < energy.length; x++) {
+ for(int y = 0; y < energy[x].length; y++) {
+ energy[x][y] = new Double(0);
+ }
+ }
+
+ // For each hit, place its energy in the array.
+ for(EcalHit hit : hits) {
+ // Get the converted crystal index.
+ Point panelLoc = toPanelPoint(hit.getLocation());
+
+ // Add the energy to the array.
+ energy[panelLoc.x][panelLoc.y] += hit.getEnergy();
+ }
+
+ // Return the resulting array.
+ return energy;
+ }
+
+ /**
+ * The <code>EcalListener</code> class binds keys to actions.
+ * Bound actions include:
+ * [Right Arrow] :: Next event (stand-alone mode only)
+ * [Left Arrow ] :: Previous event (stand-alone mode only)
+ * b :: Toggle color-mapping for 0 energy crystals
+ * h :: Toggle selected crystal highlighting
+ * l :: Toggle logarithmic versus linear scaling
+ * s :: Saves the current display to a file
+ **/
+ private class EcalKeyListener implements KeyListener {
+ public void keyPressed(KeyEvent e) { }
+
+ public void keyReleased(KeyEvent e) {
+ // If right-arrow was pressed, go to the next event.
+ if (e.getKeyCode() == 39) {
+ try { displayNextEvent(); }
+ catch (IOException ex) {
+ System.err.println(ex.getMessage());
+ System.exit(1);
+ }
+ }
+
+ // If left-arrow was pressed, go to the previous event.
+ else if (e.getKeyCode() == 37) {
+ //try { displayPreviousEvent(); }
+ //catch (IOException ex) {
+ // System.err.println(ex.getMessage());
+ // System.exit(1);
+ //}
+ }
+
+ // If the down-arrow was pressed, move down a time step in
+ // the buffer and display it.
+ else if(e.getKeyCode() == 40) {
+ if(bufferIndex == eventHitBuffer.size() - 1) { return; }
+ else {
+ bufferIndex++;
+ ecalPanel.clearCrystals();
+ displayEvent(eventHitBuffer.get(bufferIndex), eventClusters);
+ }
+ }
+
+ // If the up-arrow was pressed, move up a time step in
+ // the buffer and display it.
+ else if(e.getKeyCode() == 38) {
+ if(bufferIndex == 0) { return; }
+ else {
+ bufferIndex--;
+ ecalPanel.clearCrystals();
+ displayEvent(eventHitBuffer.get(bufferIndex), eventClusters);
+ }
+ }
+
+ // 'b' toggles the default white background.
+ else if(e.getKeyCode() == 66) {
+ if(background) { ecalPanel.setDefaultCrystalColor(null); }
+ else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
+ background = !background;
+ }
+
+ // 'h' toggles highlighting the crystal under the cursor.
+ else if(e.getKeyCode() == 72) { ecalPanel.setSelectionHighlighting(!ecalPanel.isSelectionEnabled()); }
+
+ // 'l' toggles linear or logarithmic scaling.
+ else if(e.getKeyCode() == 76) {
+ if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
+ else { ecalPanel.setScalingLinear(); }
+ }
+
+ // 's' saves the panel to a file.
+ else if(e.getKeyCode() == 83) {
+ // Make a new buffered image on which to draw the content pane.
+ BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
+ getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
+
+ // Paint the content pane to image.
+ getContentPane().paint(screenshot.getGraphics());
+
+ // Get the lowest available file name.
+ int fileNum = 0;
+ File imageFile = new File("screenshot_" + fileNum + ".png");
+ while(imageFile.exists()) {
+ fileNum++;
+ imageFile = new File("screenshot_" + fileNum + ".png");
+ }
+
+ // Save the image to a PNG file.
+ try { ImageIO.write(screenshot, "PNG", imageFile); }
+ catch(IOException ioe) {
+ System.err.println("Error saving file \"screenshot.png\".");
+ }
+ System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
+ }
+
+ // Otherwise, print out the key code for the pressed key.
+ else { System.out.printf("Key Code: %d%n", e.getKeyCode()); }
+ }
+
+ public void keyTyped(KeyEvent e) { }
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/FileViewer.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/FileViewer.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,158 @@
+package org.hps.monitoring.ecal.ui;
+
+import org.hps.monitoring.ecal.io.EventManager;
+
+import java.awt.Point;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.List;
+
+import org.hps.monitoring.ecal.event.Association;
+import org.hps.monitoring.ecal.event.Cluster;
+import org.hps.monitoring.ecal.event.EcalHit;
+
+/**
+ * Class <code>FileViewer</code> is an implementation of the <code>
+ * Viewer</code> abstract class that reads events from a file data
+ * source. Any file type can be used, so long as it has a manager
+ * which implements the <code>EventManager</code> interface.
+ *
+ * @author Kyle McCarty
+ */
+public class FileViewer extends ActiveViewer {
+ private static final long serialVersionUID = 17058336873349781L;
+ // Map cluster location to a cluster object.
+ private HashMap<Point, Cluster> clusterMap = new HashMap<Point, Cluster>();
+ // Additional status display field names for this data type.
+ private static final String[] fieldNames = { "Shared Hits", "Component Hits", "Cluster Energy" };
+
+ /**
+ * <b>FileViewer</b><br/><br/>
+ * <code>public <b>FileViewer</b>()</code><br/><br/>
+ * Constructs a new <code>Viewer</code> for displaying data read
+ * from a file.
+ * @param dataSource - The <code>EventManager</code> responsible
+ * for reading data from a file.
+ * @throws NullPointerException Occurs if the event manager is
+ * <code>null</code>.
+ */
+ public FileViewer(EventManager dataSource) throws NullPointerException {
+ // Pass any additional fields required by the event manager
+ // to the underlying Viewer object to be added to the status
+ // display panel.
+ super(dataSource, fieldNames);
+ }
+
+ public void displayNextEvent() throws IOException { getEvent(true); }
+
+ public void displayPreviousEvent() throws IOException { getEvent(false); }
+
+ protected void updateStatusPanel() {
+ super.updateStatusPanel();
+
+ // Get the currently selected crystal.
+ Point crystal = ecalPanel.getSelectedCrystal();
+
+ // If the active crystal is not null, see if it is a cluster.
+ if(crystal != null) {
+ // Get the cluster associated with this point.
+ Cluster activeCluster = clusterMap.get(crystal);
+
+ // If the cluster is null, we set everything to undefined.
+ if(activeCluster == null) {
+ for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); }
+ }
+
+ // Otherwise, define the fields based on the cluster.
+ else {
+ // Get the shared and component hit counts.
+ setStatusField(fieldNames[0], Integer.toString(activeCluster.getSharedHitCount()));
+ setStatusField(fieldNames[1], Integer.toString(activeCluster.getComponentHitCount()));
+
+ // Format the cluster energy, or account for it if it
+ // doesn't exist.
+ String energy;
+ if(activeCluster.getClusterEnergy() != Double.NaN) {
+ DecimalFormat formatter = new DecimalFormat("0.####E0");
+ energy = formatter.format(activeCluster.getClusterEnergy());
+ }
+ else { energy = "---"; }
+ setStatusField(fieldNames[2], energy);
+ }
+ }
+ // Otherwise, clear the field values.
+ else { for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); } }
+ }
+
+ /**
+ * <b>displayEvent</b><br/><br/>
+ * <code>private void <b>displayEvent</b>(List<EcalHit> hitList, List<Cluster> clusterList)</code><br/><br/>
+ * Displays the given lists of hits and clusters on the calorimeter
+ * panel.
+ * @param hitList - A list of hits for the current event.
+ * @param clusterList - A list of clusters for the current event.
+ */
+ private void displayEvent(List<EcalHit> hitList, List<Cluster> clusterList) {
+ // Suppress the calorimeter panel's redrawing.
+ ecalPanel.setSuppressRedraw(true);
+
+ // Display the hits.
+ for (EcalHit h : hitList) {
+ int ix = toPanelX(h.getX());
+ int iy = toPanelY(h.getY());
+ ecalPanel.addCrystalEnergy(ix, iy, h.getEnergy());
+ }
+
+ // Display the clusters.
+ for(Cluster cluster : clusterList) {
+ Point rawCluster = cluster.getClusterCenter();
+ Point clusterCenter = toPanelPoint(rawCluster);
+ ecalPanel.setCrystalCluster(clusterCenter.x, clusterCenter.y, true);
+
+ // Add component hits to the calorimeter panel.
+ for(Point ch : cluster.getComponentHits()) {
+ ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(ch), HIGHLIGHT_CLUSTER_COMPONENT));
+ }
+
+ // Add shared hits to the calorimeter panel.
+ for(Point sh : cluster.getSharedHits()) {
+ ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(sh), HIGHLIGHT_CLUSTER_SHARED));
+ }
+ }
+
+ // Stop suppressing the redraw and order the panel to update.
+ ecalPanel.setSuppressRedraw(false);
+ ecalPanel.repaint();
+
+ // Update the status panel to account for the new event.
+ updateStatusPanel();
+ }
+
+ /**
+ * <b>getEvent</b><br/><br/>
+ * <code>private void <b>getEvent</b>(boolean forward)</code><br/><br/>
+ * Reads either the next or the previous event from the event manager.
+ * @param forward - Whether the event data should be read forward
+ * or backward.
+ * @throws IOException Occurs when there is an issue with reading the data file.
+ */
+ private void getEvent(boolean forward) throws IOException {
+ // Clear the calorimeter panel.
+ ecalPanel.clearCrystals();
+
+ // If there is no data source, we can not do anything.
+ if (em == null) { return; }
+
+ // Otherwise, get the next event.
+ if(forward) { em.nextEvent(); }
+ else { em.previousEvent(); }
+
+ // Load the cluster map.
+ clusterMap.clear();
+ for(Cluster c : em.getClusters()) { clusterMap.put(toPanelPoint(c.getClusterCenter()), c); }
+
+ // Display it.
+ displayEvent(em.getHits(), em.getClusters());
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/OccupancyViewer.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/OccupancyViewer.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,150 @@
+package org.hps.monitoring.ecal.ui;
+
+import java.awt.Dimension;
+import java.awt.Point;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hps.monitoring.ecal.event.EcalHit;
+import org.hps.monitoring.ecal.io.EventManager;
+
+/**
+ * Class <code>OccupancyViewer</code> is an active implementation of
+ * the <code>Viewer</code> class that displays occupancies on each
+ * crystal.
+ *
+ * @author Kyle McCarty
+ */
+public class OccupancyViewer extends ActiveViewer {
+ private static final long serialVersionUID = 3712604287904215617L;
+ // The number of events that have been read so far.
+ private long events = 0;
+ // The total number of hits for each crystal position.
+ private long[][] hits;
+
+ /**
+ * <b>OccupancyViewer</b><br/><br/>
+ * <code>public <b>OccupancyViewer</b>(EventManager em)</code><br/><br/>
+ * Creates a new occupancy display that draws event data from the
+ * indicated data source.
+ * @param em - The data source from which to draw events.
+ */
+ public OccupancyViewer(EventManager em) {
+ // Initialize the super class.
+ super(em);
+
+ // Set the title and scale.
+ setTitle("HPS Calorimeter Occupancies");
+ ecalPanel.setScaleMaximum(1.0);
+
+ // Initialize the hit counts array.
+ Dimension ecalSize = ecalPanel.getCrystalBounds();
+ hits = new long[ecalSize.width][ecalSize.height];
+ }
+
+ public void displayNextEvent() throws IOException { getEvent(true); }
+
+ public void displayPreviousEvent() throws IOException { getEvent(false); }
+
+ /**
+ * <b>resetOccupancies</b><br/><br/>
+ * <code>public void <b>resetOccupancies</b>()</code><br/><br/>
+ * Clears the current occupancy data.
+ */
+ public void resetOccupancies() {
+ // Clear the crystal hit counts.
+ for(int x = 0; x < hits.length; x++) {
+ for(int y = 0; y < hits[0].length; y++) {
+ hits[x][y] = 0;
+ }
+ }
+
+ // Clear the number of events.
+ events = 0;
+ }
+
+ /**
+ * <b>displayEvent</b><br/><br/>
+ * <code>private void <b>displayEvent</b>(List<EcalHit> hitList)</code><br/><br/>
+ * Displays the given lists of hits on the calorimeter panel.
+ * @param hitList - A list of hits for the current event.
+ */
+ private void displayEvent(List<EcalHit> hitList) {
+ // Suppress the calorimeter panel's redrawing.
+ ecalPanel.setSuppressRedraw(true);
+
+ // Display the hits.
+ for (EcalHit h : hitList) {
+ ecalPanel.addCrystalEnergy(h.getX(), h.getY(), h.getEnergy());
+ }
+
+ // Stop suppressing the redraw and order the panel to update.
+ ecalPanel.setSuppressRedraw(false);
+ ecalPanel.repaint();
+
+ // Update the status panel to account for the new event.
+ updateStatusPanel();
+ }
+
+ /**
+ * <b>getEvent</b><br/><br/>
+ * <code>private void <b>getEvent</b>(boolean forward)</code><br/><br/>
+ * Reads either the next or the previous event from the event manager.
+ * @param forward - Whether the event data should be read forward
+ * or backward.
+ * @throws IOException Occurs when there is an issue with reading the data file.
+ */
+ private void getEvent(boolean forward) throws IOException {
+ // Clear the calorimeter panel.
+ ecalPanel.clearCrystals();
+
+ // If there is no data source, we can not do anything.
+ if (em == null) { return; }
+
+ // Get the next event.
+ if(forward) {
+ // Get the next event.
+ em.nextEvent();
+
+ // Increment the event count.
+ events++;
+
+ // For each hit, increment the hit count for the relevant
+ // crystal by one.
+ for(EcalHit hit : em.getHits()) {
+ hits[toPanelX(hit.getX())][toPanelY(hit.getY())]++;
+ }
+ }
+ else {
+ // Get the previous event.
+ em.previousEvent();
+
+ // Decrement the event count.
+ events--;
+
+ // For each hit, decrement the hit count for the relevant
+ // crystal by one.
+ for(EcalHit hit : em.getHits()) {
+ hits[toPanelX(hit.getX())][toPanelY(hit.getY())]--;
+ }
+ }
+
+ // Build a "hit list" from the occupancies.
+ ArrayList<EcalHit> occupancyList = new ArrayList<EcalHit>();
+ for(int x = 0; x < hits.length; x++) {
+ for(int y = 0; y < hits[0].length; y++) {
+ if(hits[x][y] != 0) {
+ // Define the crystal ID and "energy."
+ Point cid = new Point(x, y);
+ double occupancy = ((double) hits[x][y]) / events;
+ EcalHit occupancyHit = new EcalHit(cid, occupancy);
+ occupancyList.add(occupancyHit);
+ }
+ }
+ }
+
+ // Display it the occupancies.
+ displayEvent(occupancyList);
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/PEventViewer.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/PEventViewer.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,167 @@
+package org.hps.monitoring.ecal.ui;
+
+import java.awt.Color;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.imageio.ImageIO;
+
+import org.hps.monitoring.ecal.event.Association;
+import org.hps.monitoring.ecal.event.Cluster;
+import org.hps.monitoring.ecal.event.EcalHit;
+
+/**
+ * Class <code>PEventViewer</code> represents a <code>PassiveViewer
+ * </code> implementation which displays hits and clusters.
+ *
+ * @author Kyle McCarty
+ */
+public class PEventViewer extends PassiveViewer {
+ private static final long serialVersionUID = -7479125553259270894L;
+ // Stores whether the background color is set or not.
+ private boolean background = false;
+ // Stores cluster objects.
+ protected ArrayList<Cluster> clusterList = new ArrayList<Cluster>();
+ // Stores hit objects.
+ protected ArrayList<EcalHit> hitList = new ArrayList<EcalHit>();
+
+ /**
+ * <b>PEventViewer</b><br/><br/>
+ * <code>public <b>PEventViewer</b>(String... fieldValues)</code><br/><br/>
+ * Creates a passive viewer for displaying hits and clusters in
+ * an event.
+ * @param fieldValues - Any additional status fields to display.
+ */
+ public PEventViewer(String... fieldValues) {
+ // Pass the field values to the superclass.
+ super(fieldValues);
+
+ // Set the key bindings.
+ addKeyListener(new EcalKeyListener());
+ }
+
+ public void addHit(EcalHit hit) { hitList.add(hit); }
+
+ public void addCluster(Cluster cluster) { clusterList.add(cluster); }
+
+ /**
+ * <b>clearHits</b><br/><br/>
+ * <code>public void <b>clearHits</b>()</code><br/><br/>
+ * Removes all of the hit data from the viewer.
+ */
+ public void clearHits() { hitList.clear(); }
+
+ /**
+ * <b>clearClusters</b><br/><br/>
+ * <code>public void <b>clearClusters</b>()</code><br/><br/>
+ * Removes all of the cluster data from the viewer.
+ */
+ public void clearClusters() { hitList.clear(); }
+
+ public void resetDisplay() {
+ // Reset the hit and cluster lists.
+ hitList.clear();
+ clusterList.clear();
+ }
+
+ public void updateDisplay() {
+ // Suppress the calorimeter panel's redrawing.
+ ecalPanel.setSuppressRedraw(true);
+
+ // Display the hits.
+ for (EcalHit h : hitList) {
+ int ix = toPanelX(h.getX());
+ int iy = toPanelY(h.getY());
+ ecalPanel.addCrystalEnergy(ix, iy, h.getEnergy());
+ }
+
+ // Display the clusters.
+ for(Cluster cluster : clusterList) {
+ Point rawCluster = cluster.getClusterCenter();
+ Point clusterCenter = toPanelPoint(rawCluster);
+ ecalPanel.setCrystalCluster(clusterCenter.x, clusterCenter.y, true);
+
+ // Add component hits to the calorimeter panel.
+ for(Point ch : cluster.getComponentHits()) {
+ ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(ch), HIGHLIGHT_CLUSTER_COMPONENT));
+ }
+
+ // Add shared hits to the calorimeter panel.
+ for(Point sh : cluster.getSharedHits()) {
+ ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(sh), HIGHLIGHT_CLUSTER_SHARED));
+ }
+ }
+
+ // Stop suppressing the redraw and order the panel to update.
+ ecalPanel.setSuppressRedraw(false);
+ ecalPanel.repaint();
+
+ // Update the status panel to account for the new event.
+ updateStatusPanel();
+ }
+
+ /**
+ * The <code>EcalListener</code> class binds keys to actions.
+ * Bound actions include:
+ * b :: Toggle color-mapping for 0 energy crystals
+ * h :: Toggle selected crystal highlighting
+ * l :: Toggle logarithmic versus linear scaling
+ * s :: Saves the current display to a file
+ **/
+ private class EcalKeyListener implements KeyListener {
+ public void keyPressed(KeyEvent e) { }
+
+ public void keyReleased(KeyEvent e) {
+ // 'b' toggles the default white background.
+ if(e.getKeyCode() == 66) {
+ if(background) { ecalPanel.setDefaultCrystalColor(null); }
+ else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
+ background = !background;
+ }
+
+ // 'h' toggles highlighting the crystal under the cursor.
+ else if(e.getKeyCode() == 72) { ecalPanel.setSelectionHighlighting(!ecalPanel.isSelectionEnabled()); }
+
+ // 'l' toggles linear or logarithmic scaling.
+ else if(e.getKeyCode() == 76) {
+ if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
+ else { ecalPanel.setScalingLinear(); }
+ }
+
+ // 's' saves the panel to a file.
+ else if(e.getKeyCode() == 83) {
+ // Make a new buffered image on which to draw the content pane.
+ BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
+ getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
+
+ // Paint the content pane to image.
+ getContentPane().paint(screenshot.getGraphics());
+
+ // Get the lowest available file name.
+ int fileNum = 0;
+ File imageFile = new File("screenshot_" + fileNum + ".png");
+ while(imageFile.exists()) {
+ fileNum++;
+ imageFile = new File("screenshot_" + fileNum + ".png");
+ }
+
+ // Save the image to a PNG file.
+ try { ImageIO.write(screenshot, "PNG", imageFile); }
+ catch(IOException ioe) {
+ System.err.println("Error saving file \"screenshot.png\".");
+ }
+ System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
+ }
+
+ // Otherwise, print out the key code for the pressed key.
+ else { System.out.printf("Key Code: %d%n", e.getKeyCode()); }
+ }
+
+ public void keyTyped(KeyEvent e) { }
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/POccupancyViewer.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/POccupancyViewer.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,127 @@
+package org.hps.monitoring.ecal.ui;
+
+import java.awt.Dimension;
+import java.awt.Point;
+import java.util.ArrayList;
+
+import org.hps.monitoring.ecal.event.Cluster;
+import org.hps.monitoring.ecal.event.EcalHit;
+
+/**
+ * The class <code>POccupancyViewer</code> is an extension of the <code>
+ * PassiveViewer</code> class for displaying occupancies from a stream.
+ * Like all passive viewers, it is designed to receive instructions on
+ * when to update its display or read data from the stream.
+ *
+ * @author Kyle McCarty
+ */
+public class POccupancyViewer extends PassiveViewer {
+ private static final long serialVersionUID = 3712604287904215617L;
+ // Store the number of hits for each crystal.
+ private long[][] hits;
+ // Store the total number of events read.
+ private long events = 0;
+ // Stores hit objects.
+ protected ArrayList<EcalHit> hitList = new ArrayList<EcalHit>();
+
+ /**
+ * <b>POccupancyViewer</b><br/><br/>
+ * <code>public <b>POccupancyViewer</b>(int updateRate, boolean resetAtUpdate)</code><br/><br/>
+ * Initializes a <code>Viewer</code> window that displays will
+ * occupancies from a data stream.
+ */
+ public POccupancyViewer() {
+ // Set the title and scale.
+ setTitle("HPS Calorimeter Occupancies");
+ ecalPanel.setScaleMaximum(1.0);
+
+ // Initialize the hit counts array.
+ Dimension ecalSize = ecalPanel.getCrystalBounds();
+ hits = new long[ecalSize.width][ecalSize.height];
+ }
+
+ public void addHit(EcalHit hit) {
+ // Get the panel coordinates of the hit.
+ int ix = toPanelX(hit.getX());
+ int iy = toPanelY(hit.getY());
+
+ // Increment the hit count at the indicated location.
+ hits[ix][iy]++;
+ }
+
+ /**
+ * <b>addCluster</b><br/><br/>
+ * <code>public void <b>addCluster</b>(Cluster cluster)</code><br/><br/>
+ * Adds a new cluster to the display.<br/><br/>
+ * <b>Note:</b> This operation is not supported for occupancies.
+ */
+ public void addCluster(Cluster cluster) { }
+
+ /**
+ * <b>removeHit</b><br/><br/>
+ * <code>public void <b>removeHit</b>(EcalHit hit)</code><br/><br/>
+ * Removes a hit from the display.
+ * @param hit - The hit to be removed.
+ */
+ public void removeHit(EcalHit hit) {
+ // Get the panel coordinates of the hit.
+ int ix = toPanelX(hit.getX());
+ int iy = toPanelY(hit.getY());
+
+ // Decrement the hit count at the indicated location.
+ hits[ix][iy]--;
+ }
+
+ public void resetDisplay() { hitList.clear(); }
+
+ /**
+ * <b>incrementEventCount</b><br/><br/>
+ * <code>public void <b>incrementEventCount</b>(int amount)</code><br/><br/>
+ * Increments the number of events represented by the current data
+ * set by the indicated amount. Note that this may be negative to
+ * reduce the number of events.
+ * @param amount - The number of events to add.
+ */
+ public void incrementEventCount(int amount) { events += amount; }
+
+ /**
+ * <b>updateDisplay</b><br/><br/>
+ * <code>public void <b>updateDisplay</b>()</code><br/><br/>
+ * Displays the hits and clusters added by the <code>addHit</code>
+ * and <code>addCluster</code> methods.
+ */
+ public void updateDisplay() {
+ // Build a "hit list" from the occupancies.
+ for(int x = 0; x < hits.length; x++) {
+ for(int y = 0; y < hits[0].length; y++) {
+ // Don't bother performing calculations or building
+ // any objects if there are zero hits.
+ if(hits[x][y] != 0) {
+ // Define the crystal ID and "energy."
+ Point cid = new Point(x, y);
+ double occupancy = ((double) hits[x][y]) / events;
+
+ // Add a "hit" formed from these values.
+ hitList.add(new EcalHit(cid, occupancy));
+ }
+ }
+ }
+
+ // Suppress the calorimeter panel's redrawing.
+ ecalPanel.setSuppressRedraw(true);
+
+ // Display the hits.
+ for (EcalHit h : hitList) {
+ int ix = toPanelX(h.getX());
+ int iy = toPanelY(h.getY());
+ ecalPanel.addCrystalEnergy(ix, iy, h.getEnergy());
+ }
+
+ // Stop suppressing the redraw and order the panel to update.
+ ecalPanel.setSuppressRedraw(false);
+ ecalPanel.repaint();
+
+ // Update the status panel to account for the new event.
+ updateStatusPanel();
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/PassiveViewer.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/PassiveViewer.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,163 @@
+package org.hps.monitoring.ecal.ui;
+
+import java.awt.Color;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import org.hps.monitoring.ecal.event.Cluster;
+import org.hps.monitoring.ecal.event.EcalHit;
+
+/**
+ * Abstract class <code>PassiveViewer</code> represents a <code>Viewer
+ * </code> implementation which updates based on information passed to
+ * it by an external source.
+ *
+ * @author Kyle McCarty
+ */
+public abstract class PassiveViewer extends Viewer {
+ private static final long serialVersionUID = -7479125553259270894L;
+ // Stores whether the background color is set or not.
+ private boolean background = false;
+
+ /**
+ * <b>PassiveViewer</b><br/><br/>
+ * <code>public <b>PassiveViewer</b>(String... fieldValues)</code><br/><br/>
+ * @param fieldValues
+ */
+ public PassiveViewer(String... fieldValues) {
+ // Pass the field values to the superclass.
+ super(fieldValues);
+
+ // Set the key bindings.
+ addKeyListener(new EcalKeyListener());
+ }
+
+ /**
+ * <b>addHit</b><br/><br/>
+ * <code>public void <b>addHit</b>(EcalHit hit)</code><br/><br/>
+ * Adds a new hit to the display.
+ * @param hit - The hit to be added.
+ */
+ public abstract void addHit(EcalHit hit);
+
+ /**
+ * <b>addCluster</b><br/><br/>
+ * <code>public void <b>addCluster</b>(Cluster cluster)</code><br/><br/>
+ * Adds a new cluster to the display.
+ * @param cluster - The cluster to be added.
+ */
+ public abstract void addCluster(Cluster cluster);
+
+ /**
+ * <b>resetDisplay</b><br/><br/>
+ * <code>public void <b>resetDisplay</b>()</code><br/><br/>
+ * Clears any hits or clusters that have been added to the viewer.
+ * Note that this does not automatically update the displayed panel.
+ * <code>updateDisplay</code> must be called separately.
+ */
+ public abstract void resetDisplay();
+
+ /**
+ * <b>setScale</b><br/><br/>
+ * <code>public void <b>setScale</b>(int min, int max)</code><br/><br/>
+ * Sets the upper and lower bounds of for the calorimeter display's
+ * color mapping scale.
+ * @param min - The lower bound.
+ * @param max - The upper bound.
+ */
+ public void setScale(int min, int max) {
+ ecalPanel.setScaleMinimum(min);
+ ecalPanel.setScaleMaximum(max);
+ }
+
+ /**
+ * <b>setScaleMaximum</b><br/><br/>
+ * <code>public void <b>setScaleMaximum</b>(int max)</code><br/><br/>
+ * Sets the upper bound for the calorimeter display's color mapping
+ * scale.
+ * @param max - The upper bound.
+ */
+ public void setScaleMaximum(int max) { ecalPanel.setScaleMaximum(max); }
+
+ /**
+ * <b>setScaleMinimum</b><br/><br/>
+ * <code>public void <b>setScaleMinimum</b>(int min)</code><br/><br/>
+ * Sets the lower bound for the calorimeter display's color mapping
+ * scale.
+ * @param min - The lower bound.
+ */
+ public void setScaleMinimum(int min) { ecalPanel.setScaleMinimum(min); }
+
+ /**
+ * <b>updateDisplay</b><br/><br/>
+ * <code>public void <b>updateDisplay</b>()</code><br/><br/>
+ * Displays the hits and clusters added by the <code>addHit</code>
+ * and <code>addCluster</code> methods.
+ */
+ public abstract void updateDisplay();
+
+ /**
+ * The <code>EcalListener</code> class binds keys to actions.
+ * Bound actions include:
+ * b :: Toggle color-mapping for 0 energy crystals
+ * h :: Toggle selected crystal highlighting
+ * l :: Toggle logarithmic versus linear scaling
+ * s :: Saves the current display to a file
+ **/
+ private class EcalKeyListener implements KeyListener {
+ public void keyPressed(KeyEvent e) { }
+
+ public void keyReleased(KeyEvent e) {
+ // 'b' toggles the default white background.
+ if(e.getKeyCode() == 66) {
+ if(background) { ecalPanel.setDefaultCrystalColor(null); }
+ else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
+ background = !background;
+ }
+
+ // 'h' toggles highlighting the crystal under the cursor.
+ else if(e.getKeyCode() == 72) { ecalPanel.setSelectionHighlighting(!ecalPanel.isSelectionEnabled()); }
+
+ // 'l' toggles linear or logarithmic scaling.
+ else if(e.getKeyCode() == 76) {
+ if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
+ else { ecalPanel.setScalingLinear(); }
+ }
+
+ // 's' saves the panel to a file.
+ else if(e.getKeyCode() == 83) {
+ // Make a new buffered image on which to draw the content pane.
+ BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
+ getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
+
+ // Paint the content pane to image.
+ getContentPane().paint(screenshot.getGraphics());
+
+ // Get the lowest available file name.
+ int fileNum = 0;
+ File imageFile = new File("screenshot_" + fileNum + ".png");
+ while(imageFile.exists()) {
+ fileNum++;
+ imageFile = new File("screenshot_" + fileNum + ".png");
+ }
+
+ // Save the image to a PNG file.
+ try { ImageIO.write(screenshot, "PNG", imageFile); }
+ catch(IOException ioe) {
+ System.err.println("Error saving file \"screenshot.png\".");
+ }
+ System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
+ }
+
+ // Otherwise, print out the key code for the pressed key.
+ else { System.out.printf("Key Code: %d%n", e.getKeyCode()); }
+ }
+
+ public void keyTyped(KeyEvent e) { }
+ }
+}
\ No newline at end of file
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/StatusPanel.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/StatusPanel.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,196 @@
+package org.hps.monitoring.ecal.ui;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+/**
+ * Class <code>StatusPanel</code> displays text in a set of fields.
+ *
+ * @author Kyle McCarty
+ */
+public class StatusPanel extends JPanel {
+ private static final long serialVersionUID = -8353479383875379010L;
+ // The panel that displays behind the status field.
+ private BackPanel background = new BackPanel();
+ // The status fields. The first array index represents which status
+ // field and the second is always of size two, with index 0 mapping
+ // to the label that displays the field name and index 1 mapping to
+ // the label displaying the field value.
+ private JLabel[][] field;
+ // Spacing variables for panel layout.
+ private int leftBuffer = 10;
+ private int upperBuffer = 10;
+
+ /**
+ * <b>NULL_VALUE</b><br/><br/>
+ * <code><b>static final String <b>NULL_VALUE</b></code><br/><br/>
+ * A <code>String</code> representing the default value to be
+ * displayed on the status panel whenever there is no value for
+ * that field.
+ */
+ static final String NULL_VALUE = "---";
+
+ /**
+ * <b>StatusPanel</b><br/><br/>
+ * <code>public <b>StatusPanel</b>(String... fieldName)</code><br/><br/>
+ * Creates a new status panel with display fields with the indicated
+ * names. They will be assigned a field index in the order that they
+ * are given starting with zero.
+ * @param fieldName - The names of the fields to display.
+ */
+ public StatusPanel(String... fieldName) {
+ // Initialize the component.
+ super();
+
+ // Set the layout manager to manual.
+ setLayout(null);
+
+ // Build the text fields.
+ int curZ = 0;
+ field = new JLabel[fieldName.length][2];
+ for(int i = 0; i < field.length; i++) {
+ for(int j = 0; j < field[i].length; j++) {
+ field[i][j] = new JLabel();
+ field[i][j].setOpaque(true);
+ field[i][j].setBackground(Color.WHITE);
+ add(field[i][j]);
+ setComponentZOrder(field[i][j], curZ);
+ curZ++;
+ }
+ field[i][0].setText(fieldName[i] + ": ");
+ field[i][0].setHorizontalAlignment(JLabel.RIGHT);
+ }
+
+ // Start the fields as null by default.
+ clearValues();
+
+ // Build the background panel.
+ add(background);
+ setComponentZOrder(background, curZ);
+ }
+
+ /**
+ * <b>clearValues</b><br/><br/>
+ * <code>public void <b>clearValues</b>()</code><br/><br/>
+ * Sets all of the fields on the status display to the null value.
+ */
+ public void clearValues() {
+ for(int i = 0; i < field.length; i++) {
+ field[i][1].setText(NULL_VALUE);
+ }
+ }
+
+ /**
+ * <b>setFieldValue</b><br/><br/>
+ * Sets the value of the indicated field.
+ * @param index - The field's index.
+ * @param value - The new value to display.
+ * @throws IndexOutOfBoundsException Occurs when the field index
+ * is neither more than the existing number of fields or is negative.
+ */
+ public void setFieldValue(int index, String value) throws IndexOutOfBoundsException {
+ if(index >= 0 && index < field.length) {
+ if(value == null) { field[index][1].setText(NULL_VALUE); }
+ else { field[index][1].setText(value); }
+ }
+ else { throw new IndexOutOfBoundsException("Invalid field index."); }
+ }
+
+ public void setSize(int width, int height) {
+ super.setSize(width, height);
+ resize();
+ }
+
+ public void setSize(Dimension d) {
+ super.setSize(d);
+ resize();
+ }
+
+ /**
+ *<b>getNextX</b><br/><br/>
+ * <code>private int <b>getNextX</b>(Component c)</code><br/><br/>
+ * Finds the x-coordinate immediately after the component.
+ * @param c - The component of which to find the end.
+ * @return Returns the x-coordinate at the end of the component.
+ */
+ private final static int getNextX(Component c) { return getNextX(c, 0); }
+
+ /**
+ /**
+ *<b>getNextX</b><br/><br/>
+ * <code>private int <b>getNextX</b>(Component c, int buffer)</code><br/><br/>
+ * Finds the x-coordinate after the component with a given buffer.
+ * @param c - The component of which to find the end.
+ * @param buffer - The extra space after the component to be included.
+ * @return Returns the x-coordinate at the end of the component,
+ * with a buffer length.
+ */
+ private final static int getNextX(Component c, int buffer) {
+ return c.getX() + c.getWidth() + buffer;
+ }
+
+ /**
+ * <b>resize</b><br/><br/>
+ * <code>private void <b>resize</b>()</code><br/><br/>
+ * Updates the layout of the component to the panel's current size.
+ */
+ private void resize() {
+ // Define the width an height as convenience variables.
+ int width = getWidth();
+ int height = getHeight();
+
+ // Size the background panel.
+ background.setBounds(0, 0, width, height);
+
+ // Size and place the text labels.
+ if(field.length != 0) {
+ int labelHeight = (height - (int)(upperBuffer + 5)) / 3;
+ int labelRem = (height - upperBuffer - 8) % field.length;
+ int curX = leftBuffer;
+ int curY = (int)(upperBuffer + 2);
+ for(int i = 0; i < field.length; i++) {
+ // Determine the appropriate field height.
+ int thisHeight = labelHeight;
+ if(labelRem > 0) {
+ thisHeight++;
+ labelRem--;
+ }
+
+ // Place the field.
+ field[i][0].setBounds(curX, curY, 130, thisHeight);
+ field[i][1].setBounds(getNextX(field[i][0]), curY, 75, thisHeight);
+
+ // If we have written three labels, then start a new column.
+ if(i % 3 == 2) {
+ curX = getNextX(field[i][1], 10);
+ curY = (int)(upperBuffer + 2);
+ }
+
+ // Otherwise just increment the current height.
+ else { curY += thisHeight; }
+ }
+ }
+ }
+
+ /**
+ * Class <code>BackPanel</code> simply renders the background panel
+ * for the status panel.
+ */
+ private class BackPanel extends JPanel {
+ private static final long serialVersionUID = 4997805650267243080L;
+
+ public void paint(Graphics g) {
+ // Render the panel background.
+ g.setColor(Color.WHITE);
+ g.fillRect(0, upperBuffer, getWidth(), getHeight() - upperBuffer);
+ g.setColor(Color.GRAY);
+ g.drawRect(0, upperBuffer, getWidth() - 1, getHeight() - upperBuffer - 1);
+ g.setColor(Color.LIGHT_GRAY);
+ g.drawRect(1, upperBuffer + 1, getWidth() - 3, getHeight() - upperBuffer - 3);
+ }
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/Viewer.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/ui/Viewer.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,445 @@
+package org.hps.monitoring.ecal.ui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+
+import javax.swing.JFrame;
+
+import org.hps.monitoring.ecal.util.CrystalEvent;
+import org.hps.monitoring.ecal.util.CrystalListener;
+
+/**
+ * The abstract class <code>Viewer</code> handles initialization of the
+ * calorimeter panel with the proper settings and provides a window for
+ * it to live in. Subclasses of <code>Viewer</code> should implement a
+ * means for events to be fed to the calorimeter display.
+ *
+ * @author Kyle McCarty
+ **/
+public abstract class Viewer extends JFrame {
+ // Java-suggested variable.
+ private static final long serialVersionUID = -2022819652687941812L;
+ // A map of field names to field indices.
+ private final HashMap<String, Integer> fieldMap = new HashMap<String, Integer>();
+ // A list of crystal listeners attached to the viewer.
+ private ArrayList<CrystalListener> listenerList = new ArrayList<CrystalListener>();
+ // The default field names.
+ private static final String[] defaultFields = { "x Index", "y Index", "Cell Value" };
+
+ /**
+ * <b><statusPanel/b><br/><br/>
+ * <code>protected final StatusPanel <b>statusPanel</b></code><br/><br/>
+ * The component responsible for displaying status information
+ * about the currently selected crystal.
+ */
+ protected final StatusPanel statusPanel;
+
+ /**
+ * <b>ecalPanel</b><br/><br/>
+ * <code>protected final CalorimeterPanel <b>ecalPanel</b></code><br/><br/>
+ * The panel displaying the calorimeter crystals and scale.
+ */
+ protected final CalorimeterPanel ecalPanel = new CalorimeterPanel(46, 11);
+
+ /**
+ * <b>HIGHLIGHT_CLUSTER_COMPONENT</b><br/><br/>
+ * <code>public static final Color <b>HIGHLIGHT_CLUSTER_COMPONENT</b></code><br/><br/>
+ * The default color for highlighting cluster components.
+ */
+ public static final Color HIGHLIGHT_CLUSTER_COMPONENT = Color.RED;
+
+ /**
+ * <b>HIGHLIGHT_CLUSTER_SHARED</b><br/><br/>
+ * <code>public static final Color <b>HIGHLIGHT_CLUSTER_SHARED</b></code><br/><br/>
+ * The default color for highlighting cluster shared hits.
+ */
+ public static final Color HIGHLIGHT_CLUSTER_SHARED = Color.YELLOW;
+
+ /**
+ * <b>Viewer</b><br/><br/>
+ * <code>public <b>Viewer</b>(String... statusFields)</code><br/><br/>
+ * Initializes the viewer window and calorimeter panel.
+ * @param statusFields - Additional fields to display in the status
+ * panel. This can not be <code>null</code>.
+ * @throws NullPointerException Occurs if any of the additional field
+ * arguments are <code>null</code>.
+ **/
+ public Viewer(String... statusFields) throws NullPointerException {
+ // Initialize the underlying JPanel.
+ super();
+
+ // Define the status panel fields and map them to indices.
+ String[] fields = new String[statusFields.length + 3];
+ for(int i = 0; i < defaultFields.length; i++) {
+ fields[i] = defaultFields[i];
+ fieldMap.put(defaultFields[i], i);
+ }
+ for(int i = 0; i < statusFields.length; i++) {
+ int index = i + 3;
+ fields[index] = statusFields[i];
+ fieldMap.put(statusFields[i], index);
+ }
+
+ // Generate the status panel.
+ statusPanel = new StatusPanel(fields);
+
+ // Set the scaling settings.
+ ecalPanel.setScaleMinimum(0.0001);
+ ecalPanel.setScaleMaximum(3000);
+ ecalPanel.setScalingLogarithmic();
+
+ // Disable the crystals in the calorimeter panel along the beam gap.
+ for (int i = -23; i < 24; i++) {
+ ecalPanel.setCrystalEnabled(toPanelX(i), 5, false);
+ if (i > -11 && i < -1) {
+ ecalPanel.setCrystalEnabled(toPanelX(i), 4, false);
+ ecalPanel.setCrystalEnabled(toPanelX(i), 6, false);
+ }
+ }
+
+ // Make a mouse motion listener to monitor mouse hovering.
+ getContentPane().addMouseListener(new EcalMouseListener());
+ getContentPane().addMouseMotionListener(new EcalMouseMotionListener());
+
+ // Add the panels.
+ add(ecalPanel);
+ add(statusPanel);
+
+ // Define viewer panel properties.
+ setTitle("HPS Ecal Event Display");
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ setPreferredSize(new Dimension(1060, 600));
+ setMinimumSize(new Dimension(1060, 525));
+ setLayout(null);
+
+ // Add a listener to update everything when the window changes size
+ addComponentListener(new ResizeListener());
+ }
+
+ /**
+ * <b>addCrystalListener</b><br/><br/>
+ * <code>public void <b>addCrystalListener</b>(CrystalListener cl)</code><br/><br/>
+ * Adds the specified crystal listener to receive crystal events
+ * from this component when the calorimeter panel's crystal status
+ * is changed. If listener <code>cl</code> is <code>null</code>,
+ * no exception is thrown and no action is performed.
+ * @param cl - The listener to add.
+ */
+ public void addCrystalListener(CrystalListener cl) {
+ if(cl != null) { listenerList.add(cl); }
+ }
+
+ /**
+ * <b>toEcalPoint</b><br/><br/>
+ * <code>public Point <b>toEcalPoint</b>(Point panelPoint)</code><br/><br/>
+ * Converts the calorimeter panel's coordinate pair to the LCSim
+ * coordinate system.
+ * @param panelPoint - A calorimeter panel coordinate pair..
+ * @return Returns the coordinate pair in LCSim's coordinate system
+ * as an <code>int</code>.
+ **/
+ public static final Point toEcalPoint(Point panelPoint) {
+ // Convert the point coordinates.
+ int ix = toEcalX(panelPoint.x);
+ int iy = toEcalY(panelPoint.y);
+
+ // Return the new point.
+ return new Point(ix, iy);
+ }
+
+ /**
+ * <b>toEcalX</b><br/><br/>
+ * <code>public int <b>toEcalX</b>(int panelX)</code><br/><br/>
+ * Converts the panel x-coordinate to the calorimeter's
+ * coordinate system.
+ * @param panelX - A panel x-coordinate.
+ * @return Returns the x-coordinate in the calorimeter's
+ * coordinate system as an <code>int</code>.
+ */
+ public static final int toEcalX(int panelX) {
+ if(panelX > 22) { return panelX - 22; }
+ else { return panelX - 23; }
+ }
+
+ /**
+ * <b>toEcalY</b><br/><br/>
+ * <code>public int <b>toEcalY</b>(int panelY)</code><br/><br/>
+ * Converts the panel y-coordinate to the calorimeter's
+ * coordinate system.
+ * @param panelY - A panel y-coordinate.
+ * @return Returns the y-coordinate in the calorimeter's
+ * coordinate system as an <code>int</code>.
+ */
+ public static final int toEcalY(int panelY) { return 5 - panelY; }
+
+ /**
+ * <b>toPanelPoint</b><br/><br/>
+ * <code>public Point <b>toPanelPoint</b>(Point ecalPoint)</code><br/><br/>
+ * Converts the LCSim coordinate pair to the calorimeter panel's
+ * coordinate system.
+ * @param ecalPoint - An LCSim calorimeter coordinate pair..
+ * @return Returns the coordinate pair in the calorimeter panel's
+ * coordinate system as an <code>int</code>.
+ **/
+ public static final Point toPanelPoint(Point ecalPoint) {
+ // Convert the point coordinates.
+ int ix = toPanelX(ecalPoint.x);
+ int iy = toPanelY(ecalPoint.y);
+
+ // Return the new point.
+ return new Point(ix, iy);
+ }
+
+ /**
+ * <b>toPanelX</b><br/><br/>
+ * <code>public int <b>toPanelX</b>(int ecalX)</code><br/><br/>
+ * Converts the LCSim x-coordinate to the calorimeter panel's
+ * coordinate system.
+ * @param ecalX - An LCSim calorimeter x-coordinate.
+ * @return Returns the x-coordinate in the calorimeter panel's
+ * coordinate system as an <code>int</code>.
+ **/
+ public static final int toPanelX(int ecalX) {
+ if (ecalX <= 0) { return ecalX + 23; }
+ else { return ecalX + 22; }
+ }
+
+ /**
+ * <b>toPanelY</b><br/><br/>
+ * <code>public int <b>toPanelY</b>(int ecalY)</code><br/><br/>
+ * Converts the LCSim y-coordinate to the calorimeter panel's
+ * coordinate system.
+ * @param ecalY - An LCSim calorimeter y-coordinate.
+ * @return Returns the y-coordinate in the calorimeter panel's
+ * coordinate system as an <code>int</code>.
+ **/
+ public static final int toPanelY(int ecalY) { return 5 - ecalY; }
+
+ /**
+ * <b>removeCrystalListener</b><br/><br/>
+ * <code>public void <b>removeCrystalListener</b>(CrystalListener cl)</code><br/><br/>
+ * Removes the specified crystal listener so that it no longer
+ * receives crystal events from this component. This method performs
+ * no function, nor does it throw an exception, if the listener
+ * specified by the argument was not previously added to this
+ * component. If listener <code>cl</code> is <code>null</code>, no
+ * exception is thrown and no action is performed.
+ * @param cl - The listener to remove.
+ */
+ public void removeCrystalListener(CrystalListener cl) {
+ if(cl != null) { listenerList.remove(cl); }
+ }
+
+ public void setSize(int width, int height) {
+ super.setSize(width, height);
+ resize();
+ }
+
+ public void setSize(Dimension d) {
+ setSize(d.width, d.height);
+ }
+
+ /**
+ * <b>setStatusField</b><br/><br/>
+ * <code>public void <b>setStatusField</b>(String fieldName, String value)</code><br/><br/>
+ * Sets the value of the indicated status field on the calorimeter
+ * display.
+ * @param fieldName - The name of the field to set.
+ * @param value - The value to display in relation to the field.
+ * @throws NoSuchElementException Occurs if an invalid field name
+ * is provided for argument <code>fieldName</code>.
+ */
+ public final void setStatusField(String fieldName, String value) throws NoSuchElementException {
+ // Get the index for the indicated field.
+ Integer index = fieldMap.get(fieldName);
+
+ // If it is null, the field does not exist.
+ if(index == null) { throw new NoSuchElementException("Field \"" + fieldName + "\" does not exist."); }
+
+ // Otherwise, set the field.
+ else { statusPanel.setFieldValue(index, value); }
+ }
+
+ /**
+ * <b>updateStatusPanel</b><br/><br/>
+ * <code>protected void <b>updateStatusPanel</b>()</code><br/><br/>
+ * Updates the information on the status panel to match that of
+ * the calorimeter panel's currently selected crystal.
+ */
+ protected void updateStatusPanel() {
+ // Get the currently selected crystal.
+ Point crystal = ecalPanel.getSelectedCrystal();
+
+ // If the crystal is null, there is no selection.
+ if(crystal == null || ecalPanel.isCrystalDisabled(crystal.x, crystal.y)) { statusPanel.clearValues(); }
+
+ // Otherwise, write the crystal's data to the panel.
+ else {
+ setStatusField(defaultFields[0], String.valueOf(toEcalX(crystal.x)));
+ setStatusField(defaultFields[1], String.valueOf(toEcalY(crystal.y)));
+ DecimalFormat formatter = new DecimalFormat("0.####E0");
+ String energy = formatter.format(ecalPanel.getCrystalEnergy(crystal.x, crystal.y));
+ setStatusField(defaultFields[2], energy);
+ }
+ }
+
+ /**
+ * <b>resize</b><br/><br/>
+ * <code>private void <b>resize</b>()</code><br/><br/>
+ * Handles proper resizing of the window and its components.
+ **/
+ private void resize() {
+ // Define the size constants.
+ int statusHeight = 125;
+
+ // Size and position the calorimeter display.
+ ecalPanel.setLocation(0, 0);
+ ecalPanel.setSize(getContentPane().getWidth(), getContentPane().getHeight() - statusHeight);
+
+ // Size and position the status panel.
+ statusPanel.setLocation(0, ecalPanel.getHeight());
+ statusPanel.setSize(getContentPane().getWidth(), statusHeight);
+ }
+
+ /**
+ * The <code>EcalMouseListener</code> handles removing highlighting
+ * and crystal field information when the cursor leaves the window.
+ * It also triggers crystal click events.
+ */
+ private class EcalMouseListener implements MouseListener {
+ public void mouseClicked(MouseEvent e) {
+ // If there is a selected crystal, trigger a crystal click event.
+ if(ecalPanel.getSelectedCrystal() != null) {
+ // Get the selected crystal.
+ Point crystal = ecalPanel.getSelectedCrystal();
+
+ // Construct a crystal event.
+ CrystalEvent ce = new CrystalEvent(Viewer.this, crystal);
+
+ // Loop through all the crystal listeners and trigger them.
+ for(CrystalListener cl : listenerList) { cl.crystalClicked(ce); }
+ }
+ }
+
+ public void mouseEntered(MouseEvent e) { }
+
+ public void mouseExited(MouseEvent e) {
+ ecalPanel.clearSelectedCrystal();
+ statusPanel.clearValues();
+ }
+
+ public void mousePressed(MouseEvent e) { }
+
+ public void mouseReleased(MouseEvent e) { }
+ }
+
+ /**
+ * The <code>EcalMouseMotionListener</code> handles updating of
+ * the highlighted crystal and status panel information when the
+ * mouse moves over the window. Additionally triggers crystal
+ * activation and deactivation events.
+ */
+ private class EcalMouseMotionListener implements MouseMotionListener {
+ public void mouseDragged(MouseEvent arg0) { }
+
+ public void mouseMoved(MouseEvent e) {
+ // Get the panel coordinates.
+ int x = e.getX();
+ int y = e.getY();
+
+ // Get the crystal index for these coordinates.
+ Point crystal = ecalPanel.getCrystalID(x, y);
+
+ // If either of the crystal indices are negative, then
+ // the mouse is not in a crystal and the selection should
+ // be cleared.
+ boolean validCrystal = (crystal != null);
+
+ // Get the currently selected calorimeter crystal.
+ Point curCrystal = ecalPanel.getSelectedCrystal();
+
+ // Perform event comparison checks.
+ boolean[] nullCrystal = { !validCrystal, curCrystal == null };
+ boolean[] disabledCrystal = { true, true };
+ if(!nullCrystal[0]) { disabledCrystal[0] = ecalPanel.isCrystalDisabled(crystal); }
+ if(!nullCrystal[1]) { disabledCrystal[1] = ecalPanel.isCrystalDisabled(curCrystal); }
+ boolean sameCrystal = true;
+ if(validCrystal) { sameCrystal = crystal.equals(curCrystal); }
+
+ // If the crystals are the same, there are no events to throw.
+ if(!sameCrystal) {
+ // If the new crystal is non-null and enabled, throw an event.
+ if(!nullCrystal[0] && !disabledCrystal[0]) { throwActivationEvent(crystal); }
+
+ // If the old crystal is non-null and enabled, throw an event.
+ if(!nullCrystal[1] && !disabledCrystal[1]) { throwDeactivationEvent(curCrystal); }
+ }
+
+ // If the crystal is valid, then set the selected crystal
+ // to the current one.
+ if(validCrystal) { ecalPanel.setSelectedCrystal(crystal); }
+
+ // Otherwise, clear the selection.
+ else { ecalPanel.clearSelectedCrystal(); }
+
+ // Update the status panel.
+ updateStatusPanel();
+ }
+
+ /**
+ * <b>throwActivationEvent</b><br/><br/>
+ * <code>private void <b>throwActivationEvent</b>()</code><br/><br/>
+ * Triggers crystal activation events on all listeners for
+ * this component.
+ * @param activatedCrystal - The panel coordinates for the
+ * activated crystal.
+ */
+ private void throwActivationEvent(Point activatedCrystal) {
+ // Create a crystal event.
+ CrystalEvent ce = new CrystalEvent(Viewer.this, activatedCrystal);
+
+ // Throw the event with every listener.
+ for(CrystalListener cl : listenerList) { cl.crystalActivated(ce); }
+ }
+
+ /**
+ * <b>throwDeactivationEvent</b><br/><br/>
+ * <code>private void <b>throwDeactivationEvent</b>()</code><br/><br/>
+ * Triggers crystal deactivation events on all listeners for
+ * this component.
+ * @param deactivatedCrystal - The panel coordinates for the
+ * deactivated crystal.
+ */
+ private void throwDeactivationEvent(Point deactivatedCrystal) {
+ // Create a crystal event.
+ CrystalEvent ce = new CrystalEvent(Viewer.this, deactivatedCrystal);
+
+ // Throw the event with every listener.
+ for(CrystalListener cl : listenerList) { cl.crystalDeactivated(ce); }
+ }
+ }
+
+ /**
+ * The <code>ResizeListener</code> class ensures that the components remain
+ * at the correct size and location when the window is resized.
+ **/
+ private class ResizeListener implements ComponentListener {
+ public void componentResized(ComponentEvent e) { resize(); }
+
+ public void componentHidden(ComponentEvent e) { }
+
+ public void componentMoved(ComponentEvent e) { }
+
+ public void componentShown(ComponentEvent e) { }
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/BooleanMap.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/BooleanMap.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,217 @@
+package org.hps.monitoring.ecal.util;
+
+import java.awt.Color;
+
+/**
+ * Class <code>BooleanMap</code> defines an implementation of the <code>
+ * ColorMap</code> interface which maps values to colors based on whether
+ * or not the values pass a boolean comparison.
+ *
+ * @author Kyle McCarty
+ */
+public final class BooleanMap implements ColorMap<Double> {
+ // The color to display for values which pass the boolean check.
+ private Color activeColor = new Color(255, 50, 50);
+ // The color to display for values that fail the boolean check.
+ private Color inactiveColor = Color.WHITE;
+ // The critical value against which the boolean check is performed.
+ private double value = 0.0;
+ // The type of this boolean scale.
+ private final BooleanType boolType;
+
+ /**
+ * <b>BooleanMap</b><br/><br/>
+ * <code>public <b>BooleanMap</b>(BooleanType type, double comparisonValue)</code><br/><br/>
+ * Defines a <code>ColorScale</code> which maps values to colors
+ * based on a boolean comparison.
+ * @param type - The type of boolean comparison to perform.
+ * @param comparisonValue - The value against which the comparison
+ * should be made.
+ */
+ public BooleanMap(BooleanType type, double comparisonValue) {
+ // Make sure the comparison type is not null.
+ if(type == null) { throw new IllegalArgumentException("Boolean comparison type can not be null."); }
+
+ // Define the critical value and the boolean type.
+ value = comparisonValue;
+ boolType = type;
+ }
+
+ /**
+ * <b>BooleanMap</b><br/><br/>
+ * <code>public <b>BooleanMap</b>(BooleanType type, double comparisonValue,
+ * Color activeColor)</code><br/><br/>
+ * Defines a <code>ColorScale</code> which maps values to colors
+ * based on a boolean comparison.
+ * @param type - The type of boolean comparison to perform.
+ * @param comparisonValue - The value against which the comparison
+ * should be made.
+ * @param activeColor - The color in which values that pass the
+ * comparison should be displayed.
+ */
+ public BooleanMap(BooleanType type, double comparisonValue, Color activeColor) {
+ // Set the critical value and the boolean type.
+ this(type, comparisonValue);
+
+ // Set the active color.
+ this.activeColor = activeColor;
+ }
+
+ /**
+ * <b>BooleanMap</b><br/><br/>
+ * <code>public <b>BooleanMap</b>(BooleanType type, double comparisonValue,
+ * Color activeColor, Color inactiveColor)</code><br/><br/>
+ * Defines a <code>ColorScale</code> which maps values to colors
+ * based on a boolean comparison.
+ * @param type - The type of boolean comparison to perform.
+ * @param comparisonValue - The value against which the comparison
+ * should be made.
+ * @param activeColor - The color in which values that pass the
+ * comparison should be displayed.
+ * @param inactiveColor - The color in which values that fail the
+ * comparison should be displayed.
+ */
+ public BooleanMap(BooleanType type, double comparisonValue, Color activeColor, Color inactiveColor) {
+ // Set the critical value and the boolean type.
+ this(type, comparisonValue);
+
+ // Set the active and inactive colors.
+ this.activeColor = activeColor;
+ this.inactiveColor = inactiveColor;
+ }
+
+ public Color getColor(Double value) {
+ // If the argument is null, treat it is zero.
+ if(value == null) { value = 0.0; }
+
+ // If it passes the boolean comparison, return the active color.
+ if(passes(value)) { return activeColor; }
+
+ // Otherwise, return the inactive color.
+ else { return inactiveColor; }
+ }
+
+ /**
+ * <b>getActiveColor</b><br/><br/>
+ * <code>public Color <b>getActiveColor</b>()</code><br/><br/>
+ * Gets the color used by the scale for values which pass the
+ * boolean comparison.
+ * @return Returns the color as a <code>Color</code> object.
+ */
+ public Color getActiveColor() { return activeColor; }
+
+ /**
+ * <b>getBooleanType</b><br/><br/>
+ * <code>public BooleanType <b>getBooleanType</b>()</code><br/><br/>
+ * Indicates what type of boolean comparison is performed by this
+ * scale.
+ * @return Returns the type of comparison as a <code>BooleanType
+ * </code> enumerable.
+ */
+ public BooleanType getBooleanType() { return boolType; }
+
+ /**
+ * <b>getComparisonValue</b><br/><br/>
+ * <code>public double <b>getComparisonValue</b>()</code><br/><br/>
+ * Gets the value against which the boolean comparisons are
+ * performed.
+ * @return Returns the value which is compared against.
+ */
+ public double getComparisonValue() { return value; }
+
+ /**
+ * <b>getInactiveColor</b><br/><br/>
+ * <code>public Color <b>getInactiveColor</b>()</code><br/><br/>
+ * Gets the color used by the scale for values which fail the
+ * boolean comparison.
+ * @return Returns the color as a <code>Color</code> object.
+ */
+ public Color getInactiveColor() { return inactiveColor; }
+
+ /**
+ * <b>setComparisonValue</b><br/><br/>
+ * <code>public void <b>setComparisonValue</b>(double value)</code><br/><br/>
+ * Sets the value against which the boolean comparison is performed.
+ * @param value - The value to compare against.
+ */
+ public void setComparisonValue(double value) { this.value = value; }
+
+ /**
+ * <b>passes</b><br/><br/>
+ * <code>private boolean <b>passes</b>(double d)</code><br/><br/>
+ * Determines whether a given external value passes the boolean
+ * check or not.
+ * @param d - The external value to compare.
+ * @return Returns <code>true</code> if the value passes the boolean
+ * check and <code>false</code> if it does not.
+ */
+ private boolean passes(double d) {
+ // Perform the appropriate comparison. Note that the default
+ // case is included to satisfy the compiler -- it should not
+ // ever actually be used.
+ switch(boolType) {
+ case EQUAL_TO:
+ return d == value;
+ case NOT_EQUAL_TO:
+ return d != value;
+ case GREATER_THAN:
+ return d > value;
+ case LESS_THAN:
+ return d < value;
+ case GREATER_THAN_OR_EQUAL_TO:
+ return d >= value;
+ case LESS_THAN_OR_EQUAL_TO:
+ return d<= value;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Enumerable <code>BooleanType</code> defines the type of boolean
+ * comparison that is to be performed by the scale.
+ */
+ public enum BooleanType {
+ /**
+ * <b>EQUAL_TO</b><br/><br/>
+ * Performs the boolean check:<br/><br/>
+ * <code>[External Value] == [Comparison Value]</code>
+ */
+ EQUAL_TO,
+
+ /**
+ * <b>NOT_EQUAL_TO</b><br/><br/>
+ * Performs the boolean check:<br/><br/>
+ * <code>[External Value] != [Comparison Value]</code>
+ */
+ NOT_EQUAL_TO,
+
+ /**
+ * <b>GREATER_THAN</b><br/><br/>
+ * Performs the boolean check:<br/><br/>
+ * <code>[External Value] > [Comparison Value]</code>
+ */
+ GREATER_THAN,
+
+ /**
+ * <b>LESS_THAN</b><br/><br/>
+ * Performs the boolean check:<br/><br/>
+ * <code>[External Value] < [Comparison Value]</code>
+ */
+ LESS_THAN,
+
+ /**
+ * <b>GREATER_THAN_OR_EQUAL_TO</b><br/><br/>
+ * Performs the boolean check:<br/><br/>
+ * <code>[External Value] >= [Comparison Value]</code>
+ */
+ GREATER_THAN_OR_EQUAL_TO,
+
+ /**
+ * <b>LESS_THAN_OR_EQUAL_TO</b><br/><br/>
+ * Performs the boolean check:<br/><br/>
+ * <code>[External Value] <= [Comparison Value]</code>
+ */
+ LESS_THAN_OR_EQUAL_TO
+ };
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/ColorMap.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/ColorMap.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,19 @@
+package org.hps.monitoring.ecal.util;
+
+import java.awt.Color;
+
+/**
+ * Interface <code>ColorMap</code> maps some value to a color.
+ *
+ * @author Kyle McCarty
+ */
+public interface ColorMap<T> {
+ /**
+ * <b>getColor</b><br/><br/>
+ * <code>public Color <b>getColor</b>(T value)</code><br/><br/>
+ * Determines the color representing the indicated value.
+ * @param value - The value to relate to a color.
+ * @return Returns a <code>Color</code> object associated with the argument value.
+ **/
+ public Color getColor(T value);
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/ColorScale.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/ColorScale.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,156 @@
+package org.hps.monitoring.ecal.util;
+
+/**
+ * The abstract class <code>ColorScale</code> contains shared methods
+ * for implementations of the <code>ColorMap</code> interface which
+ * map based on position in a range of values.
+ *
+ * @author Kyle McCarty
+ **/
+public abstract class ColorScale implements ColorMap<Double> {
+ // Indicates if linear or logarithmic scaling should be used.
+ protected boolean linear = true;
+ // The minimum value for color scaling.
+ protected double min = 0;
+ // The maximum value for color scaling.
+ protected double max = 1;
+ // A scale variable used for mapping logarithmic values.
+ protected double scale = 1.0;
+ // An efficiency variable used for logarithmic mapping.
+ protected double lMin = 0.0;
+ // An efficiency variable used for logarithmic mapping.
+ protected double lMax = 1.0;
+
+ /**
+ * <b>getMaximum</b><br/><br/>
+ * <code>public double <b>getMaximum</b>()</code><br/><br/>
+ * Gets the highest value for which color scaling is performed.
+ * @return Returns the maximum value for color scaling as a <code>double</code>.
+ **/
+ public double getMaximum() { return max; }
+
+ /**
+ * <b>getMinimum</b><br/><br/>
+ * <code>public double <b>getMinimum</b>()</code><br/><br/>
+ * Gets the lowest value for which color scaling is performed.
+ * @return Returns the minimum value for color scaling as a <code>double</code>.
+ **/
+ public double getMinimum() { return min; }
+
+ /**
+ * <b>getScaledMaximum</b><br/><br/>
+ * <code>public double <b>getScaledMaximum</b>()</code><br/><br/>
+ * Gets the highest for which color scaling is performed, scaled
+ * based on scale type.
+ * @return Returns the maximum value for color scaling as a <code>
+ * double</code>, if scaling is linear, and the logarithm of this
+ * value, if scaling is logarithmic.
+ */
+ public double getScaledMaximum() {
+ if(linear) { return max; }
+ else { return lMax; }
+ }
+
+ /**
+ * <b>getScaledMinimum</b><br/><br/>
+ * <code>public double <b>getScaledMinimum</b>()</code><br/><br/>
+ * Gets the lowest for which color scaling is performed, scaled
+ * based on scale type.
+ * @return Returns the minimum value for color scaling as a <code>
+ * double</code>, if scaling is linear, and the logarithm of this
+ * value, if scaling is logarithmic.
+ */
+ public double getScaledMinimum() {
+ if(linear) { return min; }
+ else { return lMin; }
+ }
+
+ /**
+ * <b>isLinear</b><br/><br/>
+ * <code>public boolean <b>isLinear</b>()</code><br/><br/>
+ * Indicates whether this color mapping is linear or not.
+ * @return Returns <code>true</code> if this is a linear mapping and <code>false
+ * </code> otherwise.
+ **/
+ public boolean isLinear() { return linear; }
+
+ /**
+ * <b>isLogairthmic</b><br/><br/>
+ * <code>public boolean <b>isLogairthmic</b>()</code><br/><br/>
+ * Indicates whether this color mapping is logarithmic or not.
+ * @return Returns <code>true</code> if this is a logarithmic mapping and <code>
+ * false</code> if it is not.
+ **/
+ public boolean isLogarithmic() { return !linear; }
+
+ /**
+ * <b>setMaximum</b><br/><br/>
+ * <code>public void <b>setMaximum</b>(double maximum)</code><br/><br/>
+ * Sets the value over which no color scaling will be performed.
+ * @param maximum - The highest value for which color scaling will be performed.
+ **/
+ public void setMaximum(double maximum) {
+ max = maximum;
+ revalidate();
+ }
+
+ /**
+ * <b>setMinimum</b><br/><br/>
+ * <code>public void <b>setMinimum</b>(double minimum)</code><br/><br/>
+ * Sets the value under which no color scaling will be performed.
+ * @param minimum - The lowest value for which color scaling will be performed.
+ **/
+ public void setMinimum(double minimum) {
+ min = minimum;
+ revalidate();
+ }
+
+ /**
+ * <b>setScalingLinear</b><br/><br/>
+ * <code>public void <b>setScalingLinear</b>()</code><br/><br/>
+ * Sets the scaling behavior to linear.
+ **/
+ public void setScalingLinear() {
+ linear = true;
+ revalidate();
+ }
+
+ /**
+ * <b>setScalingLogarithmic</b><br/><br/>
+ * <code>public void <b>setScalingLogarithmic</b>()</code><br/><br/>
+ * Sets the scaling behavior to logarithmic.
+ **/
+ public void setScalingLogarithmic() {
+ linear = false;
+ revalidate();
+ }
+
+ /**
+ * <b>revalidate</b><br/><br/>
+ * <code>protected void <b>revalidate</b>()</code><br/><br/>
+ * Makes any necessary changes whenever a critical value is changed.
+ **/
+ protected void revalidate() {
+ // Ensure that the minimum is not zero in the case of log scaling.
+ if (!linear && min == 0) {
+ if (max < 0.01) { min = max / 100.0; }
+ else { min = 0.01; }
+ }
+
+ // We only need to revalidate if we are using a logarithmic scale.
+ if (!linear) {
+ // Determine the scaling variable for logarithmic results.
+ double temp = min;
+ int steps = 0;
+ while (temp < 1) {
+ temp = temp * 10;
+ steps++;
+ }
+ scale = Math.pow(10, steps);
+
+ // Revalidate the logarithmic variables.
+ lMax = Math.log10(scale * max);
+ lMin = Math.log10(scale * min);
+ }
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/CrystalEvent.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/CrystalEvent.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,54 @@
+package org.hps.monitoring.ecal.util;
+
+import java.awt.AWTEvent;
+import java.awt.Point;
+
+import org.hps.monitoring.ecal.ui.Viewer;
+
+/**
+ * Class <code>CrystalEvent</code> represents some event that occurred
+ * with respect to a crystal. It is thrown when a crystal either gains
+ * or loses focus or is clicked. Crystal ID indices are always with
+ * respect to the panel coordinate system.
+ *
+ * @author Kyle McCarty
+ */
+public class CrystalEvent extends AWTEvent {
+ private static final long serialVersionUID = 77198267255387212L;
+ // Stores the location of the triggering crystal.
+ private final Point crystal;
+ // The AWTEvent id for this event.
+ private static final int AWT_ID = AWTEvent.RESERVED_ID_MAX + 10;
+
+ /**
+ * <b>CrystalEvent</b><br/><br/>
+ * <code>public <b>CrystalEvent</b>(Viewer parent, Point triggerCrystal)</code><br/><br/>
+ * Creates a crystal event for the indicated crystal and triggering
+ * component.
+ * @param source - The triggering component.
+ * @param triggerCrystal - The crystal associated with the event.
+ * @throws IllegalArgumentException Occurs if the associated crystal
+ * is <code>null</code>.
+ */
+ public CrystalEvent(Viewer source, Point triggerCrystal) throws IllegalArgumentException {
+ // Run the superclass constructor.
+ super(source, AWT_ID);
+
+ // Make sure that the trigger crystal is not null.
+ if(triggerCrystal == null) {
+ throw new IllegalArgumentException("Crystal events can not occur with respect to non-exstant crystals.");
+ }
+
+ // Define the event parameters.
+ crystal = triggerCrystal;
+ }
+
+ /**
+ * <b>getCrystalID</b><br/><br/>
+ * <code>public Point <b>getCrystalID</b>()</code><br/><br/>
+ * Indicates the panel indices at which the crystal is located.
+ * @return Returns the crystal's panel indices as a <code>Point
+ * </code> object.
+ */
+ public Point getCrystalID() { return crystal; }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/CrystalListener.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/CrystalListener.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,37 @@
+package org.hps.monitoring.ecal.util;
+
+import java.util.EventListener;
+
+/**
+ * Interface <code>CrystalListener</code> receives events from a <code>
+ * Viewer</code> component regarding crystals. These include whenever
+ * a crystal is activated (i.e. it becomes highlighted), deactivated
+ * (i.e. it is no longer highlighted), and clicked.
+ *
+ * @author Kyle McCarty
+ */
+public interface CrystalListener extends EventListener {
+ /**
+ * <b>crystalActivated</b><br/><br/>
+ * <code>public void <b>crystalActivated</b>(CrystalEvent e)</code><br/><br/>
+ * Invoked when a crystal becomes highlighted.
+ * @param e - An object describing the event.
+ */
+ public void crystalActivated(CrystalEvent e);
+
+ /**
+ * <b>crystalDeactivated</b><br/><br/>
+ * <code>public void <b>crystalDeactivated</b>(CrystalEvent e)</code><br/><br/>
+ * Invoked when a crystal ceases to be highlighted.
+ * @param e - An object describing the event.
+ */
+ public void crystalDeactivated(CrystalEvent e);
+
+ /**
+ * <b>crystalClicked</b><br/><br/>
+ * <code>public void <b>crystalClicked</b>(CrystalEvent e)</code><br/><br/>
+ * Invoked when a crystal is clicked
+ * @param e - An object describing the event.
+ */
+ public void crystalClicked(CrystalEvent e);
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/GradientScale.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/GradientScale.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,121 @@
+package org.hps.monitoring.ecal.util;
+import java.awt.Color;
+
+/**
+ * The class <code>GradientScale</code> is an implementation of the abstract
+ * class <code>ColorScale</code> which represents a simple gradient from one
+ * color to another. It will map any argument value that exceeds its maximum
+ * value to the hot color and any argument value that is below its minimum value
+ * to its cold color. All other argument values will be mapped somewhere between
+ * the cold and hot colors using either a linear or logarithmic scale.
+ *
+ * @author Kyle McCarty
+ **/
+public final class GradientScale extends ColorScale {
+ // The color associated with the maximum value.
+ private Color hotColor = Color.WHITE;
+ // The color associated with the minimum value.
+ private Color coldColor = Color.BLACK;
+ // Efficiency variable holding the rgb difference between the two
+ // colors. This is used to prevent recalculation when mapping values.
+ private int[] drgb = { 255, 255, 255 };
+
+ public Color getColor(Double value) {
+ // If the argument is null, treat it as zero.
+ if(value == null) { value = 0.0; }
+
+ // If the value is less than the minimum, return the cold color.
+ if (value < min) { return coldColor; }
+
+ // If the value is greater than the maximum, return the hot color.
+ if (value > max) { return hotColor; }
+
+ // Otherwise, calculate how far along the gradient the value is.
+ double percent;
+ if (linear) { percent = (value - min) / (max - min); }
+ else {
+ double lValue = Math.log10(scale * value);
+ percent = (lValue - lMin) / (lMax - lMin);
+ }
+
+ // Scale the color.
+ int dr = (int) Math.round(percent * drgb[0]);
+ int dg = (int) Math.round(percent * drgb[1]);
+ int db = (int) Math.round(percent * drgb[2]);
+
+ // Return the result.
+ return new Color(coldColor.getRed() + dr, coldColor.getGreen() + dg, coldColor.getBlue() + db);
+ }
+
+ /**
+ * <b>setColdColor</b><br/><br/>
+ * <code>public void <b>setColdColor</b>(Color c)</code><br/><br/>
+ * Sets the color associated with the minimum value.
+ * @param c - The color to use.
+ **/
+ public void setColdColor(Color c) {
+ coldColor = c;
+ revalidateColor();
+ }
+
+ /**
+ * <b>setHotColor</b><br/><br/>
+ * <code>public void <b>setHotColor</b>(Color c)</code><br/><br/>
+ * Sets the color associated with the maximum value.
+ * @param c - The new color to use.
+ **/
+ public void setHotColor(Color c) {
+ hotColor = c;
+ revalidateColor();
+ }
+
+ /**
+ * <b>makeGreyScale</b><br/><br/>
+ * <code>public static GradientScale <b>makeGreyScale</b>(double minimum, double maximum)</code><br/><br/>
+ * Creates a color scale that ranges from black (cold) to white (hot) with
+ * the indicated maximum and minimum.
+ * @param minimum - The lowest value for color scaling.
+ * @param maximum - The highest value for color scaling.
+ * @return Returns a <code>GradientScale</code> that maps to grey scale over
+ * the indicated range.
+ **/
+ public static GradientScale makeGreyScale(double minimum, double maximum) {
+ GradientScale gs = new GradientScale();
+ gs.setMinimum(minimum);
+ gs.setMaximum(maximum);
+
+ return gs;
+ }
+
+ /**
+ * <b>makeHeatScale</b><br/><br>
+ * <code>public static GradientScale <b>makeHeatScale</b>(double minimum, double maximum)</code><br/><br/>
+ * Creates a color scale that ranges from black (cold) to red (hot) with the
+ * indicated maximum and minimum.
+ * @param minimum - The lowest value for color scaling.
+ * @param maximum - The highest value for color scaling.
+ * @return Returns a <code>GradientScale</code> that maps to a black- to-red
+ * gradient over the indicated range.
+ **/
+ public static GradientScale makeHeatScale(double minimum, double maximum) {
+ GradientScale hs = new GradientScale();
+ hs.setHotColor(Color.RED);
+ hs.setColdColor(Color.BLACK);
+ hs.setMinimum(minimum);
+ hs.setMaximum(maximum);
+
+ return hs;
+ }
+
+ /**
+ * <b>revalidateColor</b><br/><br/>
+ * <code>private void <b>revalidateColor</b>()</code><br/><br/>
+ * Calculates the differences between the hot and cold colors and sets the
+ * class related class variables.
+ **/
+ private void revalidateColor() {
+ drgb[0] = hotColor.getRed() - coldColor.getRed();
+ drgb[1] = hotColor.getGreen() - coldColor.getGreen();
+ drgb[2] = hotColor.getBlue() - coldColor.getBlue();
+ }
+}
java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util
--- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/MultiGradientScale.java (rev 0)
+++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/util/MultiGradientScale.java 2014-03-17 23:13:16 UTC (rev 307)
@@ -0,0 +1,150 @@
+package org.hps.monitoring.ecal.util;
+import java.awt.Color;
+import java.util.ArrayList;
+
+/**
+ * The class <code>MultiGradientScale</code> is an implementation of
+ * <code>ColorScale</code> that maps values to a color over several different
+ * individual <code>GradientScale</code> objects to allow for multi-color
+ * mapping.
+ *
+ * @author Kyle McCarty
+ **/
+public final class MultiGradientScale extends ColorScale {
+ // Stores the colors in the map.
+ private ArrayList<Color> colorList = new ArrayList<Color>();
+ // Stores the component mapping scales.
+ private ArrayList<GradientScale> scaleList = new ArrayList<GradientScale>();
+
+ /**
+ * <b>addColor</b><br/><br/>
+ * <code>public void <b>addColor</b>(Color c)</code><br/><br/>
+ * Adds a new color to the mapping scale. The first color will be the
+ * coldest with subsequent colors being more and more hot.
+ * @param c - The color to add.
+ **/
+ public void addColor(Color c) {
+ colorList.add(c);
+ revalidate();
+ }
+
+ public Color getColor(Double value) {
+ // If the value is null, treat it as zero.
+ if(value == null) { value = 0.0; }
+
+ // Get the number of colors and scales.
+ int colors = colorList.size();
+ int scales = scaleList.size();
+
+ // If there are no colors or scales, give black.
+ if (colors == 0 && scales == 0) { return Color.BLACK; }
+
+ // If there are no scales, but there is a color, give that.
+ if (scales == 0 && colors == 1) { return colorList.get(0); }
+
+ // Scale the value if logarithmic.
+ double sValue;
+ if (linear) { sValue = value; }
+ else { sValue = Math.log10(scale * value); }
+
+ // Otherwise, determine which scale should get the value.
+ for (GradientScale s : scaleList) {
+ if (sValue < s.getMaximum()) {
+ return s.getColor(sValue);
+ }
+ }
+
+ // If it didn'tappear in the list, it is the hottest color.
+ return colorList.get(colors - 1);
+ }
+
+ /**
+ * <b>removeColor</b><br/><br/>
+ * <code>public boolean <b>removeColor</b>(int colorIndex)</code><br/><br/>
+ * Removes the nth color from the mapping scale.
+ * @param colorIndex - The index of the color to be removed.
+ * @return Returns <code>true</code> if the color was removed and
+ * <code>false</code> if it was not.
+ **/
+ public boolean removeColor(int colorIndex) {
+ // Only remove the value if the index is valid.
+ if (colorIndex >= 0 && colorIndex < colorList.size()) {
+ colorList.remove(colorIndex);
+ revalidate();
+ return true;
+ }
+ else { return false; }
+ }
+
+ /**
+ * <b>makeRainboowScale</b><br/><br/>
+ * <code>public static <b>makeRainbowScale</b>(double minimum, double maximum)</code><br/><br>
+ * Creates a <code>MultiGradientScale</code> that maps values from purple,
+ * to blue, to cyan, to green, to yellow, and to red at the hottest.
+ * @param minimum - The lowest mapped value.
+ * @param maximum - The highest mapped value.
+ * @return Returns the rainbow color mapping scale.
+ **/
+ public static MultiGradientScale makeRainbowScale(double minimum, double maximum) {
+ int str = 165;
+ Color purple = new Color(55, 0, 55);
+ Color blue = new Color(0, 0, str);
+ Color cyan = new Color(0, str, str);
+ Color green = new Color(0, str, 0);
+ Color yellow = new Color(str, str, 0);
+ Color red = new Color(str, 0, 0);
+
+ MultiGradientScale mgs = new MultiGradientScale();
+ mgs.addColor(purple);
+ mgs.addColor(blue);
+ mgs.addColor(cyan);
+ mgs.addColor(green);
+ mgs.addColor(yellow);
+ mgs.addColor(red);
+ mgs.setMinimum(minimum);
+ mgs.setMaximum(maximum);
+
+ return mgs;
+ }
+
+ protected void revalidate() {
+ // Handle the default logarithmic revalidation.
+ super.revalidate();
+
+ // Redistribute the lists.
+ scaleList.clear();
+
+ // We need at least colors to make a scale - otherwise, the
+ // special cases handle the color.
+ int colors = colorList.size();
+ if (colors < 2) { return; }
+
+ // Otherwise, define the list variables.
+ double sStep;
+ double sMin;
+ if (linear) {
+ sStep = (max - min) / (colors - 1);
+ sMin = min;
+ }
+ else {
+ sStep = (lMax - lMin) / (colors - 1);
+ sMin = lMin;
+ }
+ double sMax = sMin + sStep;
+
+ // Generate a list of scales.
+ for (int i = 0; i < (colors - 1); i++) {
+ // Make and add a scale.
+ GradientScale s = new GradientScale();
+ s.setMinimum(sMin);
+ s.setMaximum(sMax);
+ s.setColdColor(colorList.get(i));
+ s.setHotColor(colorList.get(i + 1));
+ scaleList.add(s);
+
+ // Update the min/max.
+ sMin = sMax;
+ sMax += sStep;
+ }
+ }
+}
SVNspam 0.1