package org.geogebra.common.euclidian.draw;
import org.geogebra.common.awt.GArea;
import org.geogebra.common.awt.GEllipse2DDouble;
import org.geogebra.common.awt.GGraphics2D;
import org.geogebra.common.awt.GLine2D;
import org.geogebra.common.awt.GRectangle;
import org.geogebra.common.euclidian.BoundingBox;
import org.geogebra.common.euclidian.Drawable;
import org.geogebra.common.euclidian.EuclidianStatic;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.euclidian.GeneralPathClipped;
import org.geogebra.common.factories.AwtFactory;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.arithmetic.Inequality;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.plugin.EuclidianStyleConstants;
import org.geogebra.common.plugin.Operation;
/**
* @author Zbynek
*
*/
public class DrawInequality1Var extends Drawable {
/** ratio of dot radius and line thickness */
public static final double DOT_RADIUS = 1;
private Inequality ineq;
private GeneralPathClipped[] gp;
private GLine2D[] lines;
private GEllipse2DDouble[] circle;
private boolean varIsY;
private double maxBound = 1000000;
private double minBound = -1000000;
/**
* Creates new drawable inequality
*
* @param view
* view
* @param geo
* the top-level function (e.g x>1 && x<3)
* @param ineq
* inequality
* @param varIsY
* true if this is inequality in Y
*/
public DrawInequality1Var(Inequality ineq, EuclidianView view,
GeoElement geo, boolean varIsY) {
super();
this.ineq = ineq;
this.geo = geo;
this.view = view;
this.varIsY = varIsY;
setBoundary(this.ineq);
}
// set min or max boundary for inequality
private void setBoundary(Inequality ineq2) {
if (ineq2.getOperation().equals(Operation.GREATER)
|| ineq2.getOperation().equals(Operation.GREATER_EQUAL)) {
ExpressionNode expr = ineq2.getNormalExpression();
if (expr.getOperation().equals(Operation.MINUS)) {
if (expr.getLeft() instanceof FunctionVariable
&& isNumber(expr.getRight())) {
double min = expr.getRight().evaluateDouble();
minBound = min;
}
if (expr.getLeft() instanceof ExpressionNode
&& isVariableNegated((ExpressionNode) expr.getLeft())
&& isNumber(expr.getRight())) {
double max = expr.getRight().evaluateDouble();
maxBound = -max;
}
if (isNumber(expr.getLeft())
&& expr.getRight() instanceof ExpressionNode
&& isVariableNegated(
(ExpressionNode) expr.getRight())) {
double min = expr.getLeft().evaluateDouble();
minBound = -min;
}
if (isNumber(expr.getLeft())
&& expr.getRight() instanceof FunctionVariable) {
double max = expr.getLeft().evaluateDouble();
maxBound = max;
}
}
if (expr.getOperation().equals(Operation.PLUS)) {
if (expr.getLeft() instanceof FunctionVariable
&& isNumber(expr.getRight())) {
double min = expr.getRight().evaluateDouble();
minBound = -min;
}
if (expr.getLeft() instanceof ExpressionNode
&& isVariableNegated((ExpressionNode) expr.getLeft())
&& isNumber(expr.getRight())) {
double max = expr.getRight().evaluateDouble();
maxBound = max;
}
}
}
if (ineq2.getOperation().equals(Operation.LESS)
|| ineq2.getOperation().equals(Operation.LESS_EQUAL)) {
ExpressionNode expr = ineq2.getNormalExpression();
if (expr.getOperation().equals(Operation.MINUS)) {
if (isNumber(expr.getLeft())
&& expr.getRight() instanceof FunctionVariable) {
double max = expr.getLeft().evaluateDouble();
maxBound = max;
}
if (expr.getLeft() instanceof FunctionVariable
&& isNumber(expr.getRight())) {
double min = expr.getRight().evaluateDouble();
minBound = min;
}
}
if (expr.getOperation().equals(Operation.PLUS)) {
if (isNumber(expr.getLeft())
&& expr.getRight() instanceof FunctionVariable) {
double min = expr.getLeft().evaluateDouble();
minBound = -min;
}
}
}
}
private static boolean isNumber(ExpressionValue expr) {
return expr instanceof NumberValue
&& !(expr instanceof FunctionVariable);
}
// e.g. -x
private static boolean isVariableNegated(ExpressionNode expr) {
if (expr.getOperation().equals(Operation.MULTIPLY)
&& Kernel.isEqual(expr.getLeft().evaluateDouble(), -1)
&& expr.getRight() instanceof FunctionVariable) {
return true;
}
return false;
}
public boolean isGrtLessEqual() {
return this.ineq.getOperation().equals(Operation.GREATER_EQUAL)
|| this.ineq.getOperation().equals(Operation.LESS_EQUAL);
}
public boolean isMinBoundSet() {
return !Kernel.isEqual(minBound, -1000000);
}
public double getMaxBound() {
return maxBound;
}
public double getMinBound() {
return minBound;
}
@Override
public void draw(GGraphics2D g2) {
if (lines == null) {
return;
}
int i = 0;
while (i < lines.length && lines[i] != null) {
if (geo.doHighlighting()) {
g2.setPaint(geo.getSelColor());
g2.setStroke(selStroke);
g2.draw(lines[i]);
}
if (geo.getLineThickness() > 0) {
g2.setPaint(getObjectColor());
g2.setStroke(
EuclidianStatic.getStroke(geo.getLineThickness() / 2.0f,
((GeoElement) ineq.getFunBorder()).lineType));
g2.draw(lines[i]);
}
// TODO: draw label
i++;
}
if (circle == null) {
return;
}
while (i < circle.length && circle[i] != null) {
if (geo.doHighlighting()) {
g2.setPaint(geo.getSelColor());
g2.setStroke(selStroke);
g2.draw(circle[i]);
}
if (geo.getLineThickness() > 0) {
g2.setPaint(getObjectColor());
g2.setStroke(
EuclidianStatic.getStroke(geo.getLineThickness() / 2.0f,
EuclidianStyleConstants.LINE_TYPE_FULL));
g2.draw(circle[i]);
if (!ineq.isStrict()) {
g2.fill(circle[i]);
}
}
// TODO: draw label
i++;
}
}
@Override
public GeoElement getGeoElement() {
return geo;
}
@Override
public boolean hit(int x, int y, int hitThreshold) {
for (int i = 0; i < gp.length; i++) {
if (gp[i] != null && gp[i].contains(x, y)) {
return true;
}
}
return false;
}
@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() {
// get x-coords of the lines
if (varIsY) {
GeoPoint[] roots = ineq.getZeros();
double[] x = new double[roots.length + 2];
x[0] = view.getHeight() + 10;
int numOfX = 1;
for (int i = 0; i < roots.length; i++) {
if (roots[i].x < view.toRealWorldCoordY(-10)
&& roots[i].x > view
.toRealWorldCoordY(view.getHeight() + 10)) {
x[numOfX++] = view.toScreenCoordY(roots[i].x);
}
}
x[numOfX++] = -10;
if (numOfX > 2 && x[numOfX - 2] > 0
&& x[numOfX - 2] < view.getHeight()) {
yLabel = (int) x[numOfX - 2] - 5;
} else {
yLabel = 10;
}
xLabel = (int) view.getXZero() + 6;
initGP(numOfX);
int j = ineq.getFunBorder().value(
view.toRealWorldCoordY(view.getHeight() + 10)) <= 0 ? 1 : 0;
GArea a = AwtFactory.getPrototype().newArea();
for (int i = 0; 2 * i + j + 1 < numOfX; i++) {
gp[i] = new GeneralPathClipped(view);
gp[i].moveTo(-10, x[2 * i + j]);
gp[i].lineTo(view.getWidth() + 10, x[2 * i + j]);
gp[i].lineTo(view.getWidth() + 10, x[2 * i + j + 1]);
gp[i].lineTo(-10, x[2 * i + j + 1]);
gp[i].lineTo(-10, x[2 * i + j]);
gp[i].closePath();
lines[2 * i] = AwtFactory.getPrototype().newLine2D();
lines[2 * i].setLine(-10, x[2 * i + j], view.getWidth() + 10,
x[2 * i + j]);
lines[2 * i + 1] = AwtFactory.getPrototype().newLine2D();
lines[2 * i + 1].setLine(-10, x[2 * i + j + 1],
view.getWidth() + 10, x[2 * i + j + 1]);
a.add(AwtFactory.getPrototype().newArea(gp[i]));
}
setShape(a);
} else {
GeoPoint[] roots = ineq.getZeros();
double[] x = new double[roots.length + 2];
x[0] = -10;
int numOfX = 1;
for (int i = 0; i < roots.length; i++) {
if (roots[i].x > view.toRealWorldCoordX(-10)
&& roots[i].x < view
.toRealWorldCoordX(view.getWidth() + 10)) {
x[numOfX++] = view.toScreenCoordX(roots[i].x);
}
}
x[numOfX++] = view.getWidth() + 10;
if (numOfX > 2 && x[numOfX - 2] > 0
&& x[numOfX - 2] < view.getHeight()) {
xLabel = (int) x[numOfX - 2] - 10;
} else {
xLabel = 10;
}
yLabel = (int) view.getYZero() + 15;
initGP(numOfX);
GArea a = AwtFactory.getPrototype().newArea();
int circleCount = 0;
if ((geo instanceof GeoFunction)
&& ((GeoFunction) geo).showOnAxis()) {
circle = new GEllipse2DDouble[numOfX];
for (int i = 0; i < numOfX; i++) {
if (x[i] < 0) {
continue;
}
if (x[i] > view.getWidth()) {
break;
}
circle[circleCount] = AwtFactory.getPrototype()
.newEllipse2DDouble();
double radius = geo.getLineThickness() * DOT_RADIUS;
circle[circleCount].setFrame(x[i] - radius,
view.toScreenCoordY(0) - radius, 2 * radius,
2 * radius);
circleCount++;
}
} else {
int j = ineq.getFunBorder()
.value(view.toRealWorldCoordX(-10)) <= 0 ? 1 : 0;
for (int i = 0; 2 * i + j + 1 < numOfX; i++) {
gp[i] = new GeneralPathClipped(view);
gp[i].moveTo(x[2 * i + j], -10);
gp[i].lineTo(x[2 * i + j], view.getHeight() + 10);
gp[i].lineTo(x[2 * i + j + 1], view.getHeight() + 10);
gp[i].lineTo(x[2 * i + j + 1], -10);
gp[i].lineTo(x[2 * i + j], -10);
gp[i].closePath();
lines[2 * i] = AwtFactory.getPrototype().newLine2D();
lines[2 * i].setLine(x[2 * i + j], -10, x[2 * i + j],
view.getHeight() + 10);
lines[2 * i + 1] = AwtFactory.getPrototype().newLine2D();
lines[2 * i + 1].setLine(x[2 * i + 1 + j], -10,
x[2 * i + 1 + j], view.getHeight() + 10);
a.add(AwtFactory.getPrototype().newArea(gp[i]));
}
}
setShape(a);
}
updateStrokes(geo);
}
private void initGP(int numOfX) {
if (gp == null) {
gp = new GeneralPathClipped[numOfX / 2];
lines = new GLine2D[numOfX];
}
}
/**
* Set all lines to null
*/
public void ignoreLines() {
for (int i = 0; i < lines.length; i++) {
lines[i] = null;
}
}
/**
* @return inequality
*/
public Inequality getIneq() {
return this.ineq;
}
@Override
public BoundingBox getBoundingBox() {
// TODO Auto-generated method stub
return null;
}
@Override
public void updateBoundingBox() {
// TODO Auto-generated method stub
}
}