/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation.
*/
package org.geogebra.common.euclidian.draw;
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.EuclidianStatic;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.euclidian.plot.CurvePlotter;
import org.geogebra.common.euclidian.plot.CurvePlotter.Gap;
import org.geogebra.common.euclidian.plot.GeneralPathClippedForCurvePlotter;
import org.geogebra.common.kernel.AlgoCasCellInterface;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.arithmetic.Command;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.cas.AlgoIntegralFunctions;
import org.geogebra.common.kernel.geos.GeoCasCell;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoNumeric;
/**
* Draws definite Integral of a GeoFunction
*
* @author Markus Hohenwarter
*/
public class DrawIntegralFunctions extends Drawable {
private GeoNumeric n;
private GeoFunction f, g;
private NumberValue a, b;
private GeneralPathClippedForCurvePlotter gp;
private boolean isVisible, labelVisible;
private boolean isCasObject;
/**
* Creates drawable for integral between two functions
*
* @param view
* view
* @param n
* integral between functions
* @param casObject
* true if n was created from a GeoCasCell
*/
public DrawIntegralFunctions(EuclidianView view, GeoNumeric n,
boolean casObject) {
this.view = view;
this.n = n;
geo = n;
isCasObject = casObject;
n.setDrawable(true);
init();
update();
}
private void init() {
if (isCasObject) {
initFromCasObject();
return;
}
AlgoIntegralFunctions algo = (AlgoIntegralFunctions) n
.getDrawAlgorithm();
f = algo.getF();
g = algo.getG();
a = algo.getA();
b = algo.getB();
}
private void initFromCasObject() {
AlgoCasCellInterface algo = (AlgoCasCellInterface) n.getDrawAlgorithm();
GeoCasCell cell = algo.getCasCell();
Command cmd = cell.getInputVE().getTopLevelCommand();
Kernel kernel = cmd.getKernel();
f = new GeoFunction(kernel.getConstruction(),
new Function(cmd.getArgument(0).wrap().replaceCasCommands()));
g = new GeoFunction(kernel.getConstruction(),
new Function(cmd.getArgument(1).wrap().replaceCasCommands()));
a = new MyDouble(cmd.getKernel(), cmd.getArgument(2).wrap()
.replaceCasCommands().evaluateDouble());
b = new MyDouble(cmd.getKernel(), cmd.getArgument(3).wrap()
.replaceCasCommands().evaluateDouble());
}
@Override
final public void update() {
isVisible = geo.isEuclidianVisible();
if (!isVisible) {
return;
}
labelVisible = geo.isLabelVisible();
updateStrokes(n);
if (n.isAlgoMacroOutput() || isCasObject) {
init();
}
// init gp
double aRW = Math.min(a.getDouble(), b.getDouble());
double bRW = Math.max(a.getDouble(), b.getDouble());
double clipX = view.toRealWorldCoordX(EuclidianStatic.CLIP_DISTANCE)
- view.toRealWorldCoordX(0);
// double clipY = view.toRealWorldCoordY(0)
// - view.toRealWorldCoordY(EuclidianStatic.CLIP_DISTANCE);
// for DrawParametricCurve.plotCurve to work with special values,
// these changes are needed (also filter out out of screen integrals)
// see TRAC-1036
aRW = Math.max(aRW, view.getXmin() - clipX);
if (aRW > view.getXmax() + clipX) {
// make invisible to prevent NPE on draw
isVisible = false;
return;
}
bRW = Math.min(bRW, view.getXmax() + clipX);
if (bRW < view.getXmin() - clipX) {
isVisible = false;
return;
}
// init first point of gp as (ax, ay)
double ax = view.toClippedScreenCoordX(aRW);
double ay = view.toClippedScreenCoordY(f.value(aRW));
// plot area between f and g
if (gp == null) {
gp = new GeneralPathClippedForCurvePlotter(view);
}
gp.reset();
gp.moveTo(ax, ay);
CurvePlotter.plotCurve(f, aRW, bRW, view, gp, false, Gap.LINE_TO);
CurvePlotter.plotCurve(g, bRW, aRW, view, gp, false, Gap.LINE_TO);
gp.closePath();
// 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) {
int bx = view.toClippedScreenCoordX(bRW);
xLabel = (int) Math.round((ax + bx) / 2);
aRW = view.toRealWorldCoordX(xLabel);
double y = (f.value(aRW) + g.value(aRW)) / 2;
yLabel = view.toClippedScreenCoordY(y);
labelDesc = geo.getLabelDescription();
addLabelOffset();
}
}
@Override
final public void draw(GGraphics2D g2) {
if (isVisible) {
if (geo.doHighlighting()) {
g2.setPaint(n.getSelColor());
g2.setStroke(selStroke);
g2.draw(gp);
}
if (gp != null) {
fill(g2, gp); // fill using default/hatching/image as
} // appropriate
if (objStroke.getLineWidth() > 0) {
g2.setPaint(getObjectColor());
g2.setStroke(objStroke);
g2.draw(gp);
}
if (labelVisible) {
g2.setFont(view.getFontConic());
g2.setPaint(geo.getLabelColor());
drawLabel(g2);
}
}
}
@Override
final 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
final public boolean isInside(GRectangle rect) {
return false;
}
/**
* Returns the bounding box of this DrawPoint in screen coordinates.
*/
@Override
final public GRectangle getBounds() {
if (!geo.isDefined() || !geo.isEuclidianVisible()) {
return null;
}
return gp.getBounds();
}
@Override
public GeoElement getGeoElement() {
return geo;
}
@Override
public void setGeoElement(GeoElement geo) {
this.geo = geo;
}
@Override
public BoundingBox getBoundingBox() {
// TODO Auto-generated method stub
return null;
}
@Override
public void updateBoundingBox() {
// TODO Auto-generated method stub
}
}