/* SelectionHighlight.java created 2011-02-13
*
*/
package org.signalml.app.view.montage.filters.charts.elements;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import org.signalml.app.view.montage.filters.charts.FrequencyRangeSelection;
/**
* This class is capable of handling and rendering selections made on
* a {@link FrequencyResponseChartPanel}.
*
* @author Piotr Szachewicz
*/
public class SelectionHighlightRenderer {
/**
* A string representing a property which is changed whenever
* the selection on the chart is changed.
*/
public static final String SELECTION_CHANGED_PROPERTY = "selectionChangedProperty";
/**
* A {@link PropertyChangeSupport} which handles notifying all its
* listeners whenever the selection changed.
*/
private PropertyChangeSupport pcSupport = new PropertyChangeSupport(this);
/**
* The color used the render the selection which is currently being made.
*/
private static Color draggingColor = new Color(0.5F, 0.5F, 0.5F, 0.5F);
/**
* The color used to render the selection after it has been made.
*/
private static Color normalColor = new Color(0.55F, 1.0F, 0.55F, 0.5F);
/**
* The chart panel on which the selections are being made.
*/
private FrequencyResponseChartPanel chartPanel;
/**
* The currently selected frequency range.
*/
private FrequencyRangeSelection frequencyRangeSelection;
/**
* The currently made selection in pixels.
*/
private PixelRangeSelection pixelRangeSelection;
/**
* Constructor.
* @param chartPanel the chart panel on which the selections will be made.
*/
public SelectionHighlightRenderer(FrequencyResponseChartPanel chartPanel) {
this.chartPanel = chartPanel;
pixelRangeSelection = new PixelRangeSelection();
}
/**
* Returns a {@link Rectangle2D} which represents the boundaries
* of the chart.
* @return a rectangle representing the chart area
*/
protected Rectangle2D getChartArea() {
return chartPanel.getScreenDataArea();
}
/**
* Returns the maximum chart frequency.
* @return the maximum chart frequency
*/
protected double getMaximumChartFrequency() {
double maximumFrequency = chartPanel.getMaximumChartFrequency();
return maximumFrequency;
}
/**
* Returns the maximum x-position (in pixels) which is still in the
* chart.
* @return maximum x-position in the chart
*/
protected int getMaximumChartPosition() {
return (int) Math.ceil(getChartArea().getX() + getChartArea().getWidth());
}
/**
* Returns the minimum x-position (in pixels) which is still in the chart.
* @return the minimum x-position in the chart
*/
protected int getMinimumChartPosition() {
return (int) Math.floor(getChartArea().getX());
}
/**
* If the given position is bigger the the maximum position on the chart,
* then it returns the maximum chart position. If it is smaller, it
* returns the minimum chart position. If none of above is true, it
* returns the given position.
* @param xPosition the x-position to be constrained
* @return the constrained x-position
*/
protected int constrainPositionWithRegardToChartSize(int xPosition) {
if (xPosition > getMaximumChartPosition()) {
xPosition = getMaximumChartPosition();
} else if (xPosition < getMinimumChartPosition()) {
xPosition = getMinimumChartPosition();
}
return xPosition;
}
/**
* Returns the selection which is currently highlighed on the chart.
* @return the highlighted frequency range
*/
public FrequencyRangeSelection getFrequencyRangeSelection() {
return convertPixelSelectionToFrequencyRangeSelection(pixelRangeSelection);
}
/**
* Sets a frequency range to be highlighted on the plot.
* @param frequencyRangeSelection the frequency range to be highlighted
*/
public void setFrequencyRangeSelection(FrequencyRangeSelection frequencyRangeSelection) {
if (pixelRangeSelection.isDragging()) {
return;
}
this.frequencyRangeSelection = frequencyRangeSelection;
pixelRangeSelection = convertFrequencyRangeSelectionToPixelSelection(frequencyRangeSelection);
fireSelectionChanged();
}
/**
* Converts a selection given in the frequency domain to one given
* in the pixel domain.
* @param frequencyRangeSelection selection to be converted
* @return the result of the conversion
*/
private PixelRangeSelection convertFrequencyRangeSelectionToPixelSelection(FrequencyRangeSelection frequencyRangeSelection) {
double startFrequency = frequencyRangeSelection.getLowerFrequency();
double endFrequency = frequencyRangeSelection.getHigherFrequency();
int startPosition = convertFrequencyToPosition(startFrequency);
int endPosition = convertFrequencyToPosition(endFrequency);
PixelRangeSelection pixelSelection = new PixelRangeSelection(startPosition, endPosition);
return pixelSelection;
}
/**
* Converts a selection in the pixel domain to a selection in the
* frequency domain.
* @param pixelSelection selection to be converted
* @return the result of the conversion
*/
private FrequencyRangeSelection convertPixelSelectionToFrequencyRangeSelection(PixelRangeSelection pixelSelection) {
int lowerPosition = pixelSelection.getLowerPosition();
int higherPosition = pixelSelection.getHigherPosition();
double lowerFrequency = convertPositionToFrequency(lowerPosition);
double higherFrequency = convertPositionToFrequency(higherPosition);
return new FrequencyRangeSelection(lowerFrequency, higherFrequency);
}
/**
* Converts the given point (pixel) to an appropriate frequency.
* @param a point to be converted
* @return the frequency which corresponds to the given point on the
* chart
*/
protected double convertPointToFrequency(Point p) {
return convertPositionToFrequency(p.x);
}
/**
* Converts the given x-position to an appropriate frequency.
* @param xPosition the x-position to be converted
* @return the frequency which corresponds to the given x-position
* on the chart
*/
protected double convertPositionToFrequency(double xPosition) {
int xMin = getMinimumChartPosition();
int xMax = getMaximumChartPosition();
if (xPosition < xMin) {
return 0;
}
if (xPosition > xMax) {
return getMaximumChartFrequency();
}
double freq = getMaximumChartFrequency() * (((double)(xPosition - xMin)) / ((double)(xMax - xMin)));
return ((double) Math.round(freq * 4)) / 4.0;
}
/**
* Converts the given frequency to an appropriate x-position on the chart.
* @param frequency the frequency to be converted
* @return an x-position which corresponds to the given frequency
* on the chart
*/
protected int convertFrequencyToPosition(double frequency) {
int xMin = getMinimumChartPosition();
double perHz = getNumberOfPixelsPerHertz();
return (int) Math.round(xMin + frequency * perHz);
}
/**
* Returns the number of pixels which falls on one hertz on the chart.
* @return number of pixels per hertz
*/
protected double getNumberOfPixelsPerHertz() {
int xMax = getMaximumChartPosition();
int xMin = getMinimumChartPosition();
double perHz = ((double)(xMax - xMin)) / getMaximumChartFrequency();
return perHz;
}
/**
* This method should be called by the chart panel to which this
* renderer is connected (the one given in the constructor) whenever
* a mousePressed event has happened in that chart panel.
* @param ev an object describing the mouse event that has happened
*/
public void mousePressed(MouseEvent ev) {
int xPosition = ev.getPoint().x;
if (xPosition >= getMinimumChartPosition() && xPosition <= getMaximumChartPosition()) {
pixelRangeSelection.startDragging(ev.getPoint().x);
frequencyRangeSelection = convertPixelSelectionToFrequencyRangeSelection(pixelRangeSelection);
fireSelectionChanged();
}
}
/**
* This method should be called by the chart panel to which this
* renderer is connected (the one given in the constructor) whenever
* a mouseReleased event has happened in that chart panel.
* @param ev an object describing the mouse event that has happened
*/
public void mouseReleased(MouseEvent ev) {
int xPosition = ev.getPoint().x;
xPosition = constrainPositionWithRegardToChartSize(xPosition);
pixelRangeSelection.stopDragging(xPosition);
frequencyRangeSelection = convertPixelSelectionToFrequencyRangeSelection(pixelRangeSelection);
fireSelectionChanged();
}
/**
* This method should be called by the chart panel to which this
* renderer is connected (the one given in the constructor) whenever
* a mouseDragged event has happened in that chart panel.
* @param ev an object describing the mouse event that has happened
*/
public void mouseDragged(MouseEvent ev) {
int xPosition = ev.getPoint().x;
xPosition = constrainPositionWithRegardToChartSize(xPosition);
pixelRangeSelection.dragTo(xPosition);
frequencyRangeSelection = convertPixelSelectionToFrequencyRangeSelection(pixelRangeSelection);
fireSelectionChanged();
}
/**
* This method should be called by the chart panel to which this renderer
* is connected whenever the chart scale changes in order to keep the
* same frequency range selected.
*/
public void updateSelectionToScaleChange() {
if (frequencyRangeSelection != null) {
pixelRangeSelection = convertFrequencyRangeSelectionToPixelSelection(frequencyRangeSelection);
fireSelectionChanged();
}
}
/**
* Paints the selections made.
* @param gOrig
*/
public void paintComponent(Graphics gOrig) {
if (!pixelRangeSelection.isVisible()) {
return;
}
Graphics2D g = (Graphics2D) gOrig;
Rectangle2D area = getChartArea();
int selectionHighlightStart = pixelRangeSelection.getLowerPosition();
int selectionHighlightEnd = pixelRangeSelection.getHigherPosition();
if (pixelRangeSelection.isDragging()) {
g.setColor(draggingColor);
} else {
g.setColor(normalColor);
}
g.fillRect(selectionHighlightStart, (int) area.getY(), selectionHighlightEnd - selectionHighlightStart, (int) area.getHeight());
}
/**
* Adds a new listener which will be notified whenever the selection
* have been changed.
* @param listener a listener to be added
*/
public void addSelectionChangedListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(SELECTION_CHANGED_PROPERTY, listener);
}
/**
* Removes a new listener which will be notified whenever the selection
* have been changed.
* @param listener a listener to be removed
*/
public void removeSelectionChangedListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(SELECTION_CHANGED_PROPERTY, listener);
}
/**
* Notifies all listeners that the selection has changed.
*/
protected void fireSelectionChanged() {
pcSupport.firePropertyChange(SELECTION_CHANGED_PROPERTY, null, frequencyRangeSelection);
}
}