package gui.views.plots;
import gui.bsvComponents.BSVSlider;
import gui.bsvComponents.DragDropList;
import gui.main.EventController;
import gui.settings.Settings;
import gui.views.ViewUtils;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javax.media.opengl.GL2;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.jogamp.common.nio.Buffers;
import controller.DataHub;
import controller.ElementData;
import controller.Feature;
import controller.SelectionController;
import controller.SubspaceController;
import db.DatabaseAccessException;
/**
* This class implements the indicator plot. It uses OpenGL for fast rendering.
*/
public class IndicatorPlot extends GLPlot {
private static final long serialVersionUID = -7905616496981074075L;
/**
* Sets the size of the white borders around the plot.
*/
private static final int BORDER_SIZE = 60;
/**
* Sets the width of the lines indicating the feature values.
*/
private static final float LINES_WIDTH = 2.f;
/**
* Sets steps of alpha slider.
*/
private static final int ALPHA_SLIDER_STEPS = 50;
/**
* Controls calculation of alpha slider.
*/
private static final float ALPHA_SLIDER_BASE = 1.08f;
/**
* Sets influence of number of elements to alpha slider.
*/
private static final float ALPHA_SLIDER_ELEMENT_BASE = 500.f;
/**
* Controls alpha of non selected objects.
*/
private static final float NON_SELECTED_ALPHA = 0.1f;
/**
* Controls alpha of non selected elements depending on number of shown elements.
*/
private static final float NON_SELECTED_ALPHA_BASE = 4.f;
/**
* Sets initial count of features.
*/
private static final int INITIAL_FEATURE_COUNT = 10;
/**
* Controls alpha of selection bars.
*/
private static final float SELECTION_ALPHA = 0.35f;
/**
* Controls alpha of active selection bar.
*/
private static final float SELECTION_ALPHA_ACTIVE = 0.7f;
/**
* Controls width of selection bar width.
*/
private static final float SELECTION_WIDTH = 20.f;
/**
* Action that updates view after resorting features.
*/
private AbstractAction updateSortingAction;
/**
* Action that does autosort.
*/
private AbstractAction autoSortAction;
/**
* Action that does clear selection.
*/
private AbstractAction clearSelectionAction;
/**
* Vertex buffer that stores the location of the points.
*
* Is used for fast redrawing and represents a buffer on graphics card memory. It is not used global but is a member
* variable to avoid garbage collection and buffer freeing.
*/
private FloatBuffer vbuffer;
/**
* Color buffer that stores the color and transparency of points. See {@link #vbuffer} for more details.
*/
private FloatBuffer cbuffer;
/**
* Buffer that stores ids.
*/
private IntBuffer ibuffer;
/**
* Stores the number of elements to draw.
*/
private int elementCount;
/**
* Stores the number of features to draw.
*/
private int featureCount;
/**
* Stores the names of the features.
*/
private String[] featureNames;
/**
* Stores the minimum value of every feature.
*/
private float[] min;
/**
* Stores the maximum value of every feature.
*/
private float[] range;
/**
* Stores the alpha for the lines.
*/
private float lineAlpha;
/**
* Stores OpenGL id of our shader program.
*/
private int shaderProgram;
/**
* Stores pointer for shader alpha parameter.
*/
private int shaderUniformAlpha;
/**
* Drag and drop list used for sorting features.
*/
private DragDropList sortList;
/**
* Scroll pane for drag and drop list.
*/
private JScrollPane sortListScrollPane;
/**
* Button to update shown features.
*/
private JButton sortButton;
/**
* Stores features to draw.
*/
private Feature[] features;
/**
* Controller for user interaction.
*/
private InteractionController icontroller;
/**
* Constructs a indicator plot using a DataHub, a SelectionController and a SubspaceController.
*
* @param dataHub
* a preinitialized DataHub.
* @param selectionController
* a preinitialized SelectionController.
* @param subspaceController
* a preinitialized SubspaceController.
*/
public IndicatorPlot(DataHub dataHub, SelectionController selectionController, SubspaceController subspaceController) {
super(dataHub, selectionController, subspaceController);
}
@Override
protected void createUI() {
// init actions
this.updateSortingAction = new UpdateSortingAction();
this.autoSortAction = new AutoSortAction();
this.clearSelectionAction = new ClearSelectionAction();
this.icontroller = new InteractionController();
this.setInteractionHandler(this.icontroller);
this.lineAlpha = 1.f;
JSlider alphaSlider = new BSVSlider(0, ALPHA_SLIDER_STEPS, ALPHA_SLIDER_STEPS);
alphaSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
if (!source.getValueIsAdjusting()) {
// scale exponential
if (source.getValue() > 0) {
float elementFactor = (float) Math.max(1.f, Math.log(elementCount)
/ Math.log(ALPHA_SLIDER_ELEMENT_BASE));
lineAlpha = (float) (Math.pow(ALPHA_SLIDER_BASE, (source.getValue() - ALPHA_SLIDER_STEPS)
* elementFactor));
} else {
lineAlpha = 0.f;
}
rerender();
}
}
});
JPanel alphaPanel = new JPanel(new GridLayout(0, 1));
alphaPanel.setBorder(BorderFactory.createTitledBorder(Settings.getInstance().getResourceBundle().getString(
"indicatorLineAlpha")));
alphaPanel.setPreferredSize(new Dimension(100, (int) alphaSlider.getPreferredSize().getHeight()));
alphaPanel.add(alphaSlider);
JPanel sortPanel = new JPanel(new GridLayout(0, 1));
sortPanel.setLayout(new BoxLayout(sortPanel, BoxLayout.Y_AXIS));
sortPanel.setBorder(BorderFactory.createTitledBorder(Settings.getInstance().getResourceBundle().getString(
"indicatorFeaturePanel")));
this.sortList = new DragDropList(new Feature[0]);
this.sortListScrollPane = new JScrollPane(this.sortList);
this.sortListScrollPane.setPreferredSize(new Dimension(160, (int) this.sortListScrollPane.getMaximumSize()
.getHeight()));
sortPanel.add(this.sortListScrollPane);
Dimension buttonSize = new Dimension(28, 28);
this.sortButton = new JButton(this.updateSortingAction);
this.sortButton.setToolTipText(Settings.getInstance().getResourceBundle().getString("indicatorSortUpdate"));
// load image or fall back to text
try {
this.sortButton.setIcon(new ImageIcon(ImageIO.read(this.getClass().getResourceAsStream(
"/indicator_update.png"))));
} catch (IOException e) {
this.sortButton.setText(Settings.getInstance().getResourceBundle().getString("indicatorSortUpdate"));
}
// size of button
this.sortButton.setPreferredSize(buttonSize);
this.sortButton.setMaximumSize(buttonSize);
JButton autoButton = new JButton(this.autoSortAction);
autoButton.setText(Settings.getInstance().getResourceBundle().getString("indicatorAutosort"));
JPanel updatePanel = new JPanel(new GridLayout(0, 1));
updatePanel.setBackground(Color.WHITE);
updatePanel.add(this.sortButton);
updatePanel.add(autoButton);
sortPanel.add(updatePanel);
JPanel controlPanel = new JPanel(new GridLayout(0, 1));
controlPanel.setBorder(BorderFactory.createTitledBorder(Settings.getInstance().getResourceBundle().getString(
"indicatorControlPanel")));
JButton clearselectionButton = new JButton(this.clearSelectionAction);
clearselectionButton.setText(Settings.getInstance().getResourceBundle().getString("indicatorClearselection"));
controlPanel.add(clearselectionButton);
this.addToSidebar(alphaPanel);
this.addToSidebar(sortPanel);
this.addToSidebar(controlPanel);
}
@Override
protected void registerShortcuts() {
EventController.getInstance().setAction(this.clearSelectionAction, "eventResetSelection");
}
@Override
protected void unregisterShortcuts() {
EventController.getInstance().removeAction("eventResetSelection");
}
@Override
protected void setFeatures(Feature[] features) {
this.features = features;
this.sortList = new DragDropList(this.features);
this.sortList.addSelectionInterval(0, Math.max(0, Math.min(this.features.length, INITIAL_FEATURE_COUNT) - 1));
this.sortListScrollPane.setViewportView(this.sortList);
this.sortButton.doClick();
}
@Override
protected void init(GL2 gl) {
gl.glEnable(GL2.GL_LINE_SMOOTH);
int f = this.setupShader(gl, GL2.GL_FRAGMENT_SHADER, "/indicatorplot_fragmet.glsl");
this.shaderProgram = gl.glCreateProgram();
gl.glAttachShader(shaderProgram, f);
gl.glLinkProgram(shaderProgram);
gl.glValidateProgram(shaderProgram);
this.shaderUniformAlpha = gl.glGetUniformLocation(this.shaderProgram, "alpha");
}
@Override
protected void rescale(GL2 gl) {
this.icontroller.setShapeX(this.getShapeX() + BORDER_SIZE);
this.icontroller.setShapeY(this.getShapeY() + BORDER_SIZE);
this.icontroller.setShapeWidth(this.getShapeWidth() - 2 * BORDER_SIZE);
this.icontroller.setShapeHeight(this.getShapeHeight() - 2 * BORDER_SIZE);
}
@Override
protected void draw(GL2 gl) {
float stepSize = (float) (this.getShapeWidth() - 2 * BORDER_SIZE) / (float) (this.featureCount - 1);
float axisLength = this.getShapeHeight() - 2 * BORDER_SIZE;
// draw lines
if ((this.elementCount > 0) && (this.featureCount > 1)) {
gl.glUseProgram(this.shaderProgram);
gl.glUniform1f(this.shaderUniformAlpha, lineAlpha);
float scaleFactorX = this.getShapeWidth() - 2 * BORDER_SIZE;
float scaleFactorY = this.getShapeHeight() - 2 * BORDER_SIZE;
gl.glPushMatrix();
gl.glTranslatef(BORDER_SIZE, BORDER_SIZE, 0.f);
gl.glScalef(scaleFactorX, scaleFactorY, 1.f);
gl.glLineWidth(LINES_WIDTH);
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL2.GL_COLOR_ARRAY);
gl.glDrawArrays(GL2.GL_LINES, 0, this.elementCount * (this.featureCount - 1) * 2);
gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL2.GL_COLOR_ARRAY);
gl.glLineWidth(1.f);
gl.glPopMatrix();
gl.glUseProgram(0);
}
// draw selection
gl.glPushMatrix();
gl.glTranslatef(BORDER_SIZE, BORDER_SIZE, 0.f);
for (int i = 0; i < this.featureCount; i++) {
if (this.icontroller.isSelection(i)) {
if (this.icontroller.getActiveAxis() == i) {
gl.glColor4f(0.f, 0.f, 0.f, SELECTION_ALPHA_ACTIVE);
} else {
gl.glColor4f(0.f, 0.f, 0.f, SELECTION_ALPHA);
}
gl.glBegin(GL2.GL_POLYGON);
gl.glVertex2f(-SELECTION_WIDTH / 2.f, this.icontroller.getSelectionMin(i) * axisLength);
gl.glVertex2f(-SELECTION_WIDTH / 2.f, this.icontroller.getSelectionMax(i) * axisLength);
gl.glVertex2f(SELECTION_WIDTH / 2.f, this.icontroller.getSelectionMax(i) * axisLength);
gl.glVertex2f(SELECTION_WIDTH / 2.f, this.icontroller.getSelectionMin(i) * axisLength);
gl.glEnd();
}
gl.glTranslatef(stepSize, 0.f, 0.f);
}
gl.glColor4f(0.f, 0.f, 0.f, 1.f);
gl.glPopMatrix();
// draw axis
if (this.featureCount > 1) {
for (int i = 0; i < this.featureCount; i++) {
this.drawAxis(gl, i * stepSize + BORDER_SIZE, BORDER_SIZE, Math.round(axisLength),
this.featureNames[i], this.min[i], this.min[i] + this.range[i], false);
}
}
}
@Override
protected void dispose(GL2 gl) {
this.cleanupVBO(gl);
}
@Override
public String getName() {
return Settings.getInstance().getResourceBundle().getString("indicatorplot");
}
@Override
protected synchronized void processData() throws DatabaseAccessException, InterruptedException {
final Feature[] features = this.features;
final boolean selection = this.selectionController.isSomethingSelected();
this.featureCount = features.length;
this.featureNames = new String[this.featureCount];
this.min = new float[this.featureCount];
this.range = new float[this.featureCount];
for (int i = 0; i < this.featureCount; i++) {
this.featureNames[i] = features[i].getName();
this.min[i] = features[i].getMinValue();
this.range[i] = features[i].getMaxValue() - this.min[i];
if (this.range[i] == 0) {
this.range[i] = 1;
}
}
ElementData[] data = this.dataHub.getData();
this.elementCount = data.length;
if ((this.elementCount > 0) && (this.featureCount > 1)) {
this.vbuffer = Buffers.newDirectFloatBuffer(this.elementCount * (this.featureCount - 1) * 2 * 2);
this.cbuffer = Buffers.newDirectFloatBuffer(this.elementCount * (this.featureCount - 1) * 2 * 4);
this.ibuffer = Buffers.newDirectIntBuffer(this.elementCount);
final float nonSelectedAlphaFactor = (float) (NON_SELECTED_ALPHA * Math.min(1.f, (Math
.log(NON_SELECTED_ALPHA_BASE) / Math.log(this.elementCount))));
final float deltaX = 1.f / (this.featureCount - 1);
for (int i = 0; i < this.elementCount; i++) {
float alphaFactor = 1.f;
if (selection) {
alphaFactor = this.selectionController.isSelected(data[i].getId()) ? 1.f : nonSelectedAlphaFactor;
}
Color color = ViewUtils.calcColor(data[i]);
final float red = color.getRed() / 255.f;
final float green = color.getGreen() / 255.f;
final float blue = color.getBlue() / 255.f;
final float alpha = color.getAlpha() / 255.f * alphaFactor;
for (int j = 0; j < this.featureCount - 1; j++) {
for (int k = 0; k < 2; k++) {
this.vbuffer.put((j + k) * deltaX);
this.vbuffer.put((data[i].getValue(features[j + k]) - this.min[j + k]) / this.range[j + k]);
// color
this.cbuffer.put(red);
this.cbuffer.put(green);
this.cbuffer.put(blue);
this.cbuffer.put(alpha);
}
}
this.ibuffer.put(data[i].getId());
}
this.vbuffer.rewind();
this.cbuffer.rewind();
this.ibuffer.rewind();
}
this.icontroller.setAxisCount(this.featureCount);
}
@Override
protected synchronized void uploadData(GL2 gl) {
this.cleanupVBO(gl);
int nVBO = 2;
this.vbo = new int[nVBO];
gl.glGenBuffers(nVBO, vbo, 0);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, this.vbo[0]);
gl.glBufferData(GL2.GL_ARRAY_BUFFER, this.vbuffer.limit() * Buffers.SIZEOF_FLOAT, this.vbuffer,
GL2.GL_STATIC_DRAW);
gl.glVertexPointer(2, GL2.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, this.vbo[1]);
gl.glBufferData(GL2.GL_ARRAY_BUFFER, this.cbuffer.limit() * Buffers.SIZEOF_FLOAT, this.cbuffer,
GL2.GL_STATIC_DRAW);
gl.glColorPointer(4, GL2.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
}
@Override
protected void draw(Graphics2D g2d) {
float stepSize = (float) (this.getShapeWidth() - 2 * BORDER_SIZE) / (float) (this.featureCount - 1);
float axisLength = this.getShapeHeight() - 2 * BORDER_SIZE;
// draw lines
float scaleFactorX = this.getShapeWidth() - 2 * BORDER_SIZE;
float scaleFactorY = this.getShapeHeight() - 2 * BORDER_SIZE;
g2d.setStroke(new BasicStroke(LINES_WIDTH));
for (int i = 0; i < this.elementCount; i++) {
g2d.setColor(new Color(this.cbuffer.get(i * (this.featureCount - 1) * 2 * 4), this.cbuffer.get(i
* (this.featureCount - 1) * 2 * 4 + 1), this.cbuffer.get(i * (this.featureCount - 1) * 2 * 4 + 2),
this.cbuffer.get(i * (this.featureCount - 1) * 2 * 4 + 3) * this.lineAlpha));
int coordElementOffset = i * (this.featureCount - 1) * 2 * 2;
for (int j = 0; j < this.featureCount - 1; j++) {
int k = coordElementOffset + j * 2 * 2;
g2d.drawLine(Math.round(this.vbuffer.get(k) * scaleFactorX) + BORDER_SIZE, Math.round(this.vbuffer
.get(k + 1)
* scaleFactorY)
+ BORDER_SIZE, Math.round(this.vbuffer.get(k + 2) * scaleFactorX) + BORDER_SIZE, Math
.round(this.vbuffer.get(k + 3) * scaleFactorY)
+ BORDER_SIZE);
}
}
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke());
// draw selection
g2d.setColor(new Color(0.f, 0.f, 0.f, SELECTION_ALPHA));
for (int i = 0; i < this.featureCount; i++) {
if (this.icontroller.isSelection(i)) {
g2d.fillRect(Math.round(i * stepSize - SELECTION_WIDTH / 2.f + BORDER_SIZE), Math
.round(this.icontroller.getSelectionMin(i) * axisLength + BORDER_SIZE), Math
.round(SELECTION_WIDTH), Math.round((this.icontroller.getSelectionMax(i) - this.icontroller
.getSelectionMin(i))
* axisLength));
}
}
g2d.setColor(Color.BLACK);
// draw axis
if (this.featureCount > 1) {
for (int i = 0; i < this.featureCount; i++) {
this.drawAxis(g2d, i * stepSize + BORDER_SIZE, BORDER_SIZE, Math.round(axisLength),
this.featureNames[i], this.min[i], this.min[i] + this.range[i], false);
}
}
}
/**
* Cleanup GL memory.
*
* @param gl
* the render context.
*/
private void cleanupVBO(GL2 gl) {
if (this.vbo != null) {
gl.glDeleteBuffers(this.vbo.length, this.vbo, 0);
this.vbo = null;
}
}
/**
* Class that handles user interaction with the scatterplot.
*/
private class InteractionController extends MouseAdapter implements InteractionHandler {
/**
* Controls minimum selection size.
*/
private static final float SELECTION_SIZE_MIN = 0.01f;
/**
* Controls threshold of selection update when moving the mouse.
*/
private static final int SELECTION_THRESHOLD = 10;
/**
* Stores if user interaction is paused.
*/
private boolean pause = false;
/**
* Stores last used y mouse coordinates.
*/
private int lastY;
/**
* Stores begin of active selection.
*/
private float selectionBegin;
/**
* Stores axis count.
*/
private int axisCount;
/**
* Stores number of active axis, {@code -1} if there is no active axis.
*/
private int activeAxis;
/**
* Stores selection minimum of a specified axis.
*/
private float[] selectionMin;
/**
* Stores selection maximum of a specified axis.
*/
private float[] selectionMax;
/**
* Stores if there is a selection of a specified axis.
*/
private boolean[] selection;
/**
* Flags that next selection reset should be ignored.
*/
private boolean ignoreNextSelectionReset = false;
/**
* Stores x position of drawing shape.
*/
private int shapeX;
/**
* Stores y position of drawing shape.
*/
private int shapeY;
/**
* Stores width of drawing shape.
*/
private int shapeWidth;
/**
* Stores height of drawing shape.
*/
private int shapeHeight;
/**
* Sets x position of drawing shape.
*
* @param x
* x position of drawing shape.
*/
public void setShapeX(int x) {
this.shapeX = x;
}
/**
* Sets y position of drawing shape.
*
* @param y
* y position of drawing shape.
*/
public void setShapeY(int y) {
this.shapeY = y;
}
/**
* Sets drawing shape width.
*
* @param width
* width of drawing shape.
*/
public void setShapeWidth(int width) {
this.shapeWidth = width;
}
/**
* Sets drawing shape height.
*
* @param height
* height of drawing shape.
*/
public void setShapeHeight(int height) {
this.shapeHeight = height;
}
/**
* Reset selection of all axis (not the selection controller).
*/
public void resetSelection() {
if (this.ignoreNextSelectionReset) {
this.ignoreNextSelectionReset = false;
} else {
this.selectionMin = new float[this.axisCount];
this.selectionMax = new float[this.axisCount];
this.selection = new boolean[this.axisCount];
this.activeAxis = -1;
}
}
/**
* Sets number of axis.
*
* @param c
* number of axis.
*/
public void setAxisCount(int c) {
this.axisCount = c;
this.resetSelection();
}
/**
* Get number of active axis of user interaction.
*
* @return number of the active axis.
*/
public int getActiveAxis() {
return this.activeAxis;
}
/**
* Checks if a specified axis has a selection.
*
* @param axis
* number of axis.
* @return {@code true} if there is a selection, {@code false} otherwise.
*/
public boolean isSelection(int axis) {
return this.selection[axis];
}
/**
* Gets selection minimum for a specified axis.
*
* @param axis
* number of axis.
* @return selection minimum.
*/
public float getSelectionMin(int axis) {
return this.selectionMin[axis];
}
/**
* Gets selection maximum for a specified axis.
*
* @param axis
* number of axis.
* @return selection maximum.
*/
public float getSelectionMax(int axis) {
return this.selectionMax[axis];
}
@Override
public void pause() {
this.pause = true;
}
@Override
public void resume() {
this.pause = false;
}
@Override
public void mousePressed(MouseEvent e) {
if (!this.pause) {
float mX = (float) (e.getX() - this.shapeX) / (float) this.shapeWidth;
int axis = Math.round(mX * (this.axisCount - 1));
if ((axis >= 0) && (axis < this.axisCount)) {
float mY = 1.f - (float) (e.getY() - this.shapeY) / (float) this.shapeHeight;
this.activeAxis = axis;
this.selectionBegin = mY;
this.selection[this.activeAxis] = true;
this.selectionMin[this.activeAxis] = Math.max(0.f, Math.min(1.f, this.selectionBegin));
this.selectionMax[this.activeAxis] = Math.max(0.f, Math.min(1.f, this.selectionBegin));
this.lastY = e.getY();
}
rerender();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (!this.pause) {
if (this.activeAxis != -1) {
if (this.selectionMax[this.activeAxis] - this.selectionMin[this.activeAxis] >= SELECTION_SIZE_MIN) {
this.updateSelection(e.getY());
} else {
this.selection[this.activeAxis] = false;
}
}
this.activeAxis = -1;
int[] ids = new int[elementCount];
int idPos = 0;
for (int i = 0; i < elementCount; i++) {
boolean inSelection = true;
for (int j = 0; (j < featureCount) && inSelection; j++) {
if (this.selection[j]) {
int offset = i * 2 * (featureCount - 1) * 2;
int pos = offset + 2 * j * 2 + 1;
if (j == featureCount - 1) {
pos -= 2;
}
float value = vbuffer.get(pos);
if (!((value >= this.selectionMin[j]) && (value <= this.selectionMax[j]))) {
inSelection = false;
}
}
}
if (inSelection) {
ids[idPos++] = ibuffer.get(i);
}
}
this.ignoreNextSelectionReset = true;
selectionController.reselect(ids);
}
}
@Override
public void mouseDragged(MouseEvent e) {
if (!this.pause && this.activeAxis != -1) {
if (Math.abs(e.getY() - this.lastY) > SELECTION_THRESHOLD) {
this.updateSelection(e.getY());
this.lastY = e.getY();
rerender();
}
}
}
/**
* Update selection of active bar.
*
* @param y
* current mouse y coordinate.
*/
private void updateSelection(int y) {
float mY = 1.f - (float) (y - this.shapeY) / (float) this.shapeHeight;
this.selectionMin[this.activeAxis] = Math.max(0.f, Math.min(1.f, Math.min(this.selectionBegin, mY)));
this.selectionMax[this.activeAxis] = Math.max(0.f, Math.min(1.f, Math.max(this.selectionBegin, mY)));
}
}
/**
* Update plot after resorting.
*/
private class UpdateSortingAction extends AbstractAction {
private static final long serialVersionUID = -2426970417360022433L;
@Override
public void actionPerformed(ActionEvent e) {
Object[] elements = sortList.getElements();
int n = 0;
Feature[] tmp = new Feature[elements.length];
for (int i = 0; i < elements.length; i++) {
if (sortList.isSelectedIndex(i)) {
tmp[n] = (Feature) elements[i];
n++;
}
}
features = new Feature[n];
System.arraycopy(tmp, 0, features, 0, n);
update(null, null);
}
}
/**
* Do autosort.
*/
private class AutoSortAction extends AbstractAction {
private static final long serialVersionUID = 5140891617940951040L;
@Override
public void actionPerformed(ActionEvent e) {
try {
Object[] elements = sortList.getElements();
LinkedList<Feature> featuresOut = new LinkedList<Feature>();
LinkedList<Feature> featuresNorm = new LinkedList<Feature>();
for (int i = 0; i < elements.length; i++) {
Feature f = (Feature) elements[i];
if (f.isOutlier()) {
featuresOut.add(f);
} else {
featuresNorm.add(f);
}
}
Feature[] toSort = new Feature[featuresNorm.size()];
featuresNorm.toArray(toSort);
toSort = ViewUtils.autoSort(toSort, dataHub);
featuresNorm = new LinkedList<Feature>(Arrays.asList(toSort));
LinkedList<Feature> result = new LinkedList<Feature>();
if (featuresOut.size() == 1) {
result.addAll(featuresOut);
result.addAll(featuresNorm);
} else {
result.addAll(featuresNorm);
result.addAll(featuresOut);
}
Feature[] resultArray = new Feature[result.size()];
result.toArray(resultArray);
setFeatures(resultArray);
} catch (DatabaseAccessException ex) {
ex.printStackTrace();
}
}
}
/**
* Clear selection.
*/
private class ClearSelectionAction extends AbstractAction {
private static final long serialVersionUID = 7398990770316996231L;
@Override
public void actionPerformed(ActionEvent e) {
icontroller.resetSelection();
selectionController.reset();
}
}
}