package org.geogebra.common.euclidian.draw;
import java.util.ArrayList;
import java.util.Iterator;
import org.geogebra.common.awt.GGraphics2D;
import org.geogebra.common.awt.GRectangle;
import org.geogebra.common.euclidian.BoundingBox;
import org.geogebra.common.euclidian.Drawable;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.euclidian.GeneralPathClipped;
import org.geogebra.common.kernel.algos.AlgoBoxPlot;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.util.debug.Log;
/**
* Drawable representation of box plots
*
*/
public class DrawBoxPlot extends Drawable {
private boolean isVisible, labelVisible;
private double[] coords = new double[2];
private GeneralPathClipped gp;
private GeoNumeric sum;
private AlgoBoxPlot algo;
private NumberValue a, b;
private double OUTLIER_SIZE = 4;
/**
* @param view
* view
* @param n
* number (boxplot)
*/
public DrawBoxPlot(EuclidianView view, GeoNumeric n) {
this.view = view;
sum = n;
geo = n;
n.setDrawable(true);
init();
update();
}
private void init() {
algo = (AlgoBoxPlot) geo.getDrawAlgorithm();
a = algo.getA();
b = algo.getB();
}
@Override
public void draw(GGraphics2D g2) {
if (isVisible) {
try {
if (geo.doHighlighting()) {
g2.setPaint(sum.getSelColor());
g2.setStroke(selStroke);
g2.draw(gp);
}
} catch (Exception e) {
Log.debug(e.getMessage());
}
try {
fill(g2, gp); // fill using default/hatching/image as
// appropriate
} catch (Exception e) {
e.printStackTrace();
}
try {
if (geo.getLineThickness() > 0) {
g2.setPaint(getObjectColor());
g2.setStroke(objStroke);
g2.draw(gp);
}
} catch (Exception e) {
Log.debug(e.getMessage());
}
if (labelVisible) {
g2.setFont(view.getFontConic());
g2.setPaint(geo.getLabelColor());
drawLabel(g2);
}
}
}
@Override
public GeoElement getGeoElement() {
return geo;
}
@Override
public boolean hit(int x, int y, int hitThreshold) {
return gp != null
&& (gp.contains(x, y) || gp.intersects(x, y, hitThreshold));
}
@Override
public boolean intersectsRectangle(GRectangle rect) {
return gp != null && gp.intersects(rect);
}
@Override
public boolean isInside(GRectangle rect) {
// TODO Auto-generated method stub
return false;
}
@Override
public void setGeoElement(GeoElement geo) {
this.geo = geo;
}
@Override
public void update() {
isVisible = geo.isEuclidianVisible();
if (!isVisible) {
return;
}
if (!geo.getDrawAlgorithm().equals(geo.getParentAlgorithm())) {
init();
}
labelVisible = geo.isLabelVisible();
updateStrokes(sum);
if (gp == null) {
gp = new GeneralPathClipped(view);
}
// init gp
gp.reset();
double yOff = a.getDouble();
double yScale = b.getDouble();
// plot upper/lower sum rectangles
double[] leftBorder = algo.getLeftBorders();
coords[0] = leftBorder[0];
coords[1] = -yScale + yOff;
view.toScreenCoords(coords);
gp.moveTo(coords[0], coords[1]);
coords[0] = leftBorder[0];
coords[1] = yScale + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
coords[0] = leftBorder[0];
coords[1] = 0 + yOff;
view.toScreenCoords(coords);
gp.moveTo(coords[0], coords[1]);
coords[0] = leftBorder[1];
coords[1] = 0 + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
coords[0] = leftBorder[1];
coords[1] = yScale + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
coords[0] = leftBorder[3];
coords[1] = yScale + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
coords[0] = leftBorder[3];
coords[1] = -yScale + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
coords[0] = leftBorder[1];
coords[1] = -yScale + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
coords[0] = leftBorder[1];
coords[1] = 0 + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
coords[0] = leftBorder[3];
coords[1] = 0 + yOff;
view.toScreenCoords(coords);
gp.moveTo(coords[0], coords[1]);
coords[0] = leftBorder[4];
coords[1] = 0 + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
coords[0] = leftBorder[4];
coords[1] = yScale + yOff;
view.toScreenCoords(coords);
gp.moveTo(coords[0], coords[1]);
coords[0] = leftBorder[4];
coords[1] = -yScale + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
coords[0] = leftBorder[2];
coords[1] = yScale + yOff;
view.toScreenCoords(coords);
gp.moveTo(coords[0], coords[1]);
coords[0] = leftBorder[2];
coords[1] = -yScale + yOff;
view.toScreenCoords(coords);
gp.lineTo(coords[0], coords[1]);
ArrayList<Double> outliers = algo.getOutliers();
if (outliers != null) {
Iterator<Double> it = outliers.iterator();
while (it.hasNext()) {
coords[0] = it.next().doubleValue();
coords[1] = yOff;
view.toScreenCoords(coords);
// draw cross
gp.moveTo(coords[0] - OUTLIER_SIZE, coords[1] - OUTLIER_SIZE);
gp.lineTo(coords[0] + OUTLIER_SIZE, coords[1] + OUTLIER_SIZE);
gp.moveTo(coords[0] - OUTLIER_SIZE, coords[1] + OUTLIER_SIZE);
gp.lineTo(coords[0] + OUTLIER_SIZE, coords[1] - OUTLIER_SIZE);
}
}
// gp on screen?
if (!gp.intersects(0, 0, view.getWidth(), view.getHeight())) {
isVisible = false;
// don't return here to make sure that getBounds() works for
// offscreen points too
}
if (labelVisible) {
xLabel = (int) coords[0];
yLabel = (int) coords[1] - view.getFontSize();
labelDesc = geo.getLabelDescription();
addLabelOffset();
}
}
/**
* Returns the bounding box of this Drawable in screen coordinates.
*/
@Override
final public GRectangle getBounds() {
if (!geo.isDefined() || !geo.isEuclidianVisible()) {
return null;
}
return gp.getBounds();
}
@Override
public BoundingBox getBoundingBox() {
// TODO Auto-generated method stub
return null;
}
@Override
public void updateBoundingBox() {
// TODO Auto-generated method stub
}
}