/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.ui.filters.plugin;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JPanel;
/**
*
* @author Mathieu Bastian
*/
public class JQuickHistogram {
private int constraintHeight = 0;
private int constraintWidth = 0;
private JPanel panel;
private boolean inclusive = true;
//Data
private List<Double> data;
private Double minValue;
private Double maxValue;
private Double minRange;
private Double maxRange;
public JQuickHistogram() {
clear();
}
public void clear() {
data = new ArrayList<Double>();
minValue = Double.MAX_VALUE;
maxValue = Double.NEGATIVE_INFINITY;
}
public void addData(Object data) {
if (data instanceof Double) {
addData((Double) data);
} else if (data instanceof Integer) {
addData(((Integer) data).doubleValue());
} else if (data instanceof Float) {
addData(((Float) data).doubleValue());
} else if (data instanceof Long) {
addData(((Long) data).doubleValue());
}
}
public void addData(Double data) {
this.data.add(data);
minValue = Math.min(minValue, data);
maxValue = Math.max(maxValue, data);
minRange = minValue;
maxRange = maxValue;
}
public void sortData() {
Collections.sort(data);
}
public void setLowerBound(Double lowerBound) {
this.minRange = lowerBound;
}
public void setUpperBound(Double upperBound) {
this.maxRange = upperBound;
}
public JPanel getPanel() {
if (panel == null) {
panel = new JQuickHistogramPanel(this);
}
return panel;
}
public void setConstraintHeight(int constraintHeight) {
this.constraintHeight = constraintHeight;
panel.setPreferredSize(new Dimension(constraintWidth, constraintHeight));
panel.setMinimumSize(new Dimension(constraintWidth, constraintHeight));
}
public void setConstraintWidth(int constraintWidth) {
this.constraintWidth = constraintWidth;
panel.setPreferredSize(new Dimension(constraintWidth, constraintHeight));
panel.setMinimumSize(new Dimension(constraintWidth, constraintHeight));
}
public int countValues() {
return data.size();
}
public int countInRange() {
int res = 0;
for (int i = 0; i < data.size(); i++) {
Double d = data.get(i);
if ((inclusive && d >= minRange && d <= maxRange) || (!inclusive && d > minRange && d < maxRange)) {
res++;
}
}
return res;
}
public double getAverage() {
double res = 0;
for (int i = 0; i < data.size(); i++) {
double d = data.get(i);
res += d;
}
return res /= data.size();
}
public double getAverageInRange() {
double res = 0;
int c = 0;
for (int i = 0; i < data.size(); i++) {
double d = data.get(i);
if ((inclusive && d >= minRange && d <= maxRange) || (!inclusive && d > minRange && d < maxRange)) {
res += d;
c++;
}
}
return res /= c;
}
public double getMedian() {
return data.get((data.size() + 1) / 2);
}
public double getMedianInRange() {
int median = (countInRange() + 1) / 2;
for (int i = 0; i < data.size(); i++) {
double d = data.get(i);
if ((inclusive && d >= minRange && d <= maxRange) || (!inclusive && d > minRange && d < maxRange)) {
if (median-- == 0) {
return d;
}
}
}
return -1.;
}
private static class JQuickHistogramPanel extends JPanel {
private Color fillColor = new Color(0xCFD2D3);
private Color fillInRangeColor = new Color(0x3B4042);
private JQuickHistogram histogram;
private int currentHeight = 0;
private int currentWidth = 0;
public JQuickHistogramPanel(JQuickHistogram histogram) {
this.histogram = histogram;
setOpaque(false);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
setCurrentDimension();
Graphics2D g2d = (Graphics2D) g;
g2d.translate(0, currentHeight);
g2d.scale(1, -1);
drawHisto(g2d);
g2d.dispose();
}
private void drawHisto(Graphics2D g2d) {
if (histogram.minRange == null || histogram.maxRange == null) {
return;
}
int dataSize = histogram.data.size();
if (dataSize < currentWidth) {
int rectWidth = (int) (currentWidth / (float) dataSize);
int leftover = currentWidth - rectWidth * dataSize;
int xPosition = 0;
for (int i = 0; i < dataSize; i++) {
Double data = histogram.data.get(i);
int rectangleWidth = rectWidth + (leftover > 0 ? 1 : 0);
leftover--;
int rectangleHeight = (int) ((data - histogram.minValue) / (histogram.maxValue - histogram.minValue) * currentHeight);
if (data >= histogram.minRange && data <= histogram.maxRange) {
g2d.setColor(fillInRangeColor);
} else {
g2d.setColor(fillColor);
}
g2d.fillRect(xPosition, 0, rectangleWidth, rectangleHeight);
xPosition += rectangleWidth;
}
} else {
int xPosition = 0;
int sizeOfSmallSublists = dataSize / currentWidth;
int sizeOfLargeSublists = sizeOfSmallSublists + 1;
int numberOfLargeSublists = dataSize % currentWidth;
int numberOfSmallSublists = currentWidth - numberOfLargeSublists;
int numberOfElementsHandled = 0;
for (int i = 0; i < currentWidth; i++) {
int size = i < numberOfSmallSublists ? sizeOfSmallSublists : sizeOfLargeSublists;
double average = 0.0;
for (int j = 0; j < size; j++) {
Double d = histogram.data.get(numberOfElementsHandled++);
average += d.doubleValue();
}
average /= size;
int rectangleHeight = (int) ((average - histogram.minValue) / (histogram.maxValue - histogram.minValue) * currentHeight);
if (average >= histogram.minRange && average <= histogram.maxRange) {
g2d.setColor(fillInRangeColor);
} else {
g2d.setColor(fillColor);
}
g2d.fillRect(xPosition, 0, 1, rectangleHeight);
xPosition++;
}
}
}
private void setCurrentDimension() {
currentHeight = (histogram.constraintHeight > 0 ? histogram.constraintHeight : getHeight());
currentWidth = (histogram.constraintWidth > 0 ? histogram.constraintWidth : getWidth());
}
}
}