/*
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.gui.view.functioninspector;
import java.util.ArrayList;
import org.apache.commons.math3.analysis.UnivariateFunction;
import org.geogebra.common.awt.GColor;
import org.geogebra.common.awt.GPoint;
import org.geogebra.common.euclidian.EuclidianView;
import org.geogebra.common.gui.menubar.OptionsMenu;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.advanced.AlgoCurvature;
import org.geogebra.common.kernel.advanced.AlgoOsculatingCircle;
import org.geogebra.common.kernel.algos.AlgoDependentFunction;
import org.geogebra.common.kernel.algos.AlgoDependentNumber;
import org.geogebra.common.kernel.algos.AlgoDependentPoint;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoJoinPointsSegment;
import org.geogebra.common.kernel.algos.AlgoPointOnPath;
import org.geogebra.common.kernel.algos.AlgoRoots;
import org.geogebra.common.kernel.algos.AlgoRootsPolynomial;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.ExpressionNodeConstants.StringType;
import org.geogebra.common.kernel.arithmetic.Function;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.kernel.arithmetic.MyVecNode;
import org.geogebra.common.kernel.cas.AlgoDerivative;
import org.geogebra.common.kernel.cas.AlgoIntegralDefinite;
import org.geogebra.common.kernel.cas.AlgoLengthFunction;
import org.geogebra.common.kernel.cas.AlgoTangentFunctionPoint;
import org.geogebra.common.kernel.commands.EvalInfo;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoElementSpreadsheet;
import org.geogebra.common.kernel.geos.GeoFunction;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoNumberValue;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoText;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.optimization.ExtremumFinderI;
import org.geogebra.common.main.App;
import org.geogebra.common.main.GeoGebraColorConstants;
import org.geogebra.common.main.Localization;
import org.geogebra.common.plugin.EuclidianStyleConstants;
import org.geogebra.common.plugin.Operation;
public class FunctionInspectorModel {
public interface IFunctionInspectorListener {
void updateXYTable(boolean isTable);
void updateInterval(ArrayList<String> property,
ArrayList<String> value);
void setXYValueAt(Double value, int row, int col);
Object getXYValueAt(int row, int col);
void addTableColumn(String name);
void setGeoName(String name);
void changeTableSelection();
void updateHighAndLow(boolean isAscending, boolean isLowSelected);
void setStepText(String text);
void setStepVisible(boolean isVisible);
GColor getColor(Colors id);
int getSelectedXYRow();
void changedNumberFormat();
}
// ggb fields
private App app;
private Kernel kernel;
private Construction cons;
private EuclidianView activeEV;
private IFunctionInspectorListener listener;
protected final Localization loc;
public enum Colors {
GEO, GEO2, EVEN_ROW, GRID
}
// column types
private static final int COL_DERIVATIVE = 0;
private static final int COL_DERIVATIVE2 = 1;
private static final int COL_DIFFERENCE = 2;
private static final int COL_CURVATURE = 3;
// list to store column types of dynamically appended columns
private ArrayList<Integer> extraColumnList;
// Geos
private GeoElement tangentLine, oscCircle, xSegment, ySegment;
private GeoElement functionInterval, integralGeo, lengthGeo, areaGeo;
private GeoFunction derivative, derivative2, selectedGeo;
private GeoPoint testPoint, lowPoint, highPoint, minPoint, maxPoint;
private GeoList pts;
private ArrayList<GeoElement> intervalTabGeoList, pointTabGeoList,
hiddenGeoList;
private GeoElementND[] rootGeos;
// stores lists of column data from the point panel table
private ArrayList<Double[]> xyTableCopyList = new ArrayList<Double[]>();
private double xMin, xMax, start = -1, step = 0.1;
// private boolean isChangingValue;
private int pointCount = 9;
private ArrayList<String> property = new ArrayList<String>();
private ArrayList<String> value = new ArrayList<String>();
// store number values for copy
private ArrayList<Double[]> value2 = new ArrayList<Double[]>();
private String[] columnNames;
/**
* Default number format
*/
private int printFigures = -1;
private int printDecimals = 4;
/***************************************************************
* Constructs a FunctionInspecor
*
* @param app
* @param selectedGeo
*/
public FunctionInspectorModel(App app, GeoFunction selectedGeo,
IFunctionInspectorListener listener) {
this.app = app;
loc = app.getLocalization();
kernel = app.getKernel();
this.listener = listener;
cons = kernel.getConstruction();
extraColumnList = new ArrayList<Integer>();
// lists of all geos we create
intervalTabGeoList = new ArrayList<GeoElement>();
pointTabGeoList = new ArrayList<GeoElement>();
hiddenGeoList = new ArrayList<GeoElement>();
activeEV = app.getActiveEuclidianView();
// load selected function
this.selectedGeo = selectedGeo;
columnNames = new String[4];
setColumnNames();
}
public void setColumnNames() {
columnNames[COL_DERIVATIVE] = loc.getPlain("fncInspector.Derivative");
columnNames[COL_DERIVATIVE2] = loc.getPlain("fncInspector.Derivative2");
columnNames[COL_CURVATURE] = loc.getPlain("fncInspector.Curvature");
columnNames[COL_DIFFERENCE] = loc.getPlain("fncInspector.Difference");
}
public String getColumnName(int col) {
return col < columnNames.length ? columnNames[col] : "-";
}
public String getColumnNameForCopy(int col) {
if (col == 0) {
return "x";
} else if (col == 1) {
return "y(x)";
} else {
int col2 = extraColumnList.get(col - 2);
return col2 < columnNames.length ? columnNames[col2] : "-";
}
}
public String getTitleString() {
if (selectedGeo == null) {
return loc.getMenu("SelectObject");
}
return selectedGeo.getAlgebraDescriptionDefault();
}
// =====================================
// Update
// =====================================
public void updatePoints(boolean isTangent, boolean isOscCircle,
boolean isXYSegments, boolean isTable) {
tangentLine.setEuclidianVisible(isTangent);
tangentLine.update();
oscCircle.setEuclidianVisible(isOscCircle);
oscCircle.update();
xSegment.setEuclidianVisible(isXYSegments);
xSegment.update();
ySegment.setEuclidianVisible(isXYSegments);
ySegment.update();
pts.setEuclidianVisible(isTable);
pts.updateRepaint();
listener.setStepVisible(isTable);
listener.updateXYTable(isTable);
}
/**
* Updates the tab panels and thus the entire GUI. Also updates the active
* EV to hide/show temporary GeoElements associated with the
* FunctionInspector (e.g. points, integral)
*/
public void updateGeos(boolean isInterval) {
for (GeoElement geo : intervalTabGeoList) {
geo.setEuclidianVisible(isInterval);
geo.update();
}
for (GeoElement geo : pointTabGeoList) {
geo.setEuclidianVisible(!isInterval);
geo.update();
}
activeEV.repaint();
}
public GeoPoint getLowPoint() {
return lowPoint;
}
public GeoPoint getHighPoint() {
return highPoint;
}
/**
* Updates the interval table. The max, min, roots, area etc. for the
* current interval are calculated and put into the IntervalTable model.
*/
public void updateIntervalTable() {
property.clear();
value.clear();
value2.clear();
// prepare algos and other objects needed for the calcs
// =======================================================
double[] coords = new double[3];
lowPoint.getCoords(coords);
xMin = coords[0];
highPoint.getCoords(coords);
xMax = coords[0];
ExtremumFinderI ef = kernel.getExtremumFinder();
UnivariateFunction fun = selectedGeo.getUnivariateFunctionY();
// get the table
double integral = ((GeoNumeric) integralGeo).getDouble();
double area = ((GeoNumeric) areaGeo).getDouble();
double mean = integral / (xMax - xMin);
double length = ((GeoNumeric) lengthGeo).getDouble();
double yMin = selectedGeo.value(xMin);
double yMax = selectedGeo.value(xMax);
double xMinInt = ef.findMinimum(xMin, xMax, fun, 5.0E-8);
double xMaxInt = ef.findMaximum(xMin, xMax, fun, 5.0E-8);
double yMinInt = selectedGeo.value(xMinInt);
double yMaxInt = selectedGeo.value(xMaxInt);
if (yMin < yMinInt) {
yMinInt = yMin;
xMinInt = xMin;
}
if (yMax > yMaxInt) {
yMaxInt = yMax;
xMaxInt = xMax;
}
minPoint.setCoords(xMinInt, yMinInt, 1.0);
// minPoint.setEuclidianVisible(!(minPoint.isEqual(lowPoint) ||
// minPoint.isEqual(highPoint)));
minPoint.update();
maxPoint.setCoords(xMaxInt, yMaxInt, 1.0);
// maxPoint.setEuclidianVisible(!(maxPoint.isEqual(lowPoint) ||
// maxPoint.isEqual(highPoint)));
maxPoint.update();
// set the property/value pairs
// =================================================
property.add(loc.getCommand("Min"));
value.add("(" + format(xMinInt) + " , " + format(yMinInt) + ")");
Double[] min = { xMinInt, yMinInt };
value2.add(min);
property.add(loc.getCommand("Max"));
value.add("(" + format(xMaxInt) + " , " + format(yMaxInt) + ")");
Double[] max = { xMaxInt, yMaxInt };
value2.add(max);
property.add(null);
value.add(null);
value2.add(null);
// calculate roots
ExpressionNode low = new ExpressionNode(kernel, lowPoint,
Operation.XCOORD, null);
ExpressionNode high = new ExpressionNode(kernel, highPoint,
Operation.XCOORD, null);
AlgoDependentNumber xLow = new AlgoDependentNumber(cons, low, false);
cons.removeFromConstructionList(xLow);
AlgoDependentNumber xHigh = new AlgoDependentNumber(cons, high, false);
cons.removeFromConstructionList(xHigh);
AlgoElement roots;
if (selectedGeo.isPolynomialFunction(false)) {
roots = new AlgoRootsPolynomial(cons, selectedGeo);
} else {
roots = new AlgoRoots(cons, selectedGeo,
(GeoNumeric) xLow.getOutput(0),
(GeoNumeric) xHigh.getOutput(0));
}
cons.removeFromConstructionList(roots);
rootGeos = roots.getGeoElements();
property.add(loc.getCommand("Root"));
int count = 0;
double root = Double.NaN;
// count how many roots in range
for (int i = 0; i < rootGeos.length; i++) {
GeoPoint p = ((GeoPoint) rootGeos[i]);
if (p.isDefined()) {
double rt = p.inhomX;
if (Kernel.isGreaterEqual(rt, xMin)
&& Kernel.isGreaterEqual(xMax, rt)) {
root = rt;
count++;
}
}
}
StringTemplate tpl = StringTemplate.defaultTemplate;
switch (count) {
case 0:
value.add(loc.getPlain("fncInspector.NoRoots"));
value2.add(null);
break;
case 1:
value.add(kernel.format(root, tpl));
Double[] r = { root };
value2.add(r);
break;
default:
value.add(loc.getPlain("fncInspector.MultipleRoots"));
value2.add(null);
}
property.add(null);
value.add(null);
value2.add(null);
property.add(loc.getCommand("Integral"));
value.add(format(integral));
Double[] in = { integral };
value2.add(in);
property.add(loc.getCommand("Area"));
value.add(format(area));
Double[] a = { area };
value2.add(a);
property.add(loc.getCommand("Mean"));
value.add(format(mean));
Double[] m = { mean };
value2.add(m);
property.add(loc.getCommand("Length"));
value.add(format(length));
Double[] l = { length };
value2.add(l);
listener.updateInterval(property, value);
}
public String format(Double x) {
if (x == null) {
return "";
}
StringTemplate highPrecision;
// override the default decimal place setting
if (getPrintDecimals() >= 0) {
highPrecision = StringTemplate.printDecimals(StringType.GEOGEBRA,
getPrintDecimals(), false);
} else {
highPrecision = StringTemplate.printFigures(StringType.GEOGEBRA,
getPrintFigures(), false);
}
String result = app.getKernel().format(x, highPrecision);
return result;
}
/**
* Updates the XYTable with the coordinates of the current sample points and
* any related values (e.g. derivative, difference)
*/
public void updateXYTable(int rowCount, boolean isTable) {
// String lbl = selectedGeo.getLabel();
GeoFunction f = selectedGeo;
// init the copy array
xyTableCopyList.clear();
Double[] xArray = new Double[rowCount];
Double[] yArray = new Double[rowCount];
if (isTable) {
double x = start - step * (pointCount - 1) / 2;
double y;
for (int i = 0; i < rowCount; i++) {
y = f.value(x);
listener.setXYValueAt(x, i, 0);
listener.setXYValueAt(y, i, 1);
((GeoPoint) pts.get(i)).setCoords(x, y, 1);
// collect x, y points into the copy arrays
xArray[i] = x;
yArray[i] = y;
x = x + step;
}
pts.updateRepaint();
} else {
double x = start;
double y = f.value(x);
listener.setXYValueAt(x, 0, 0);
listener.setXYValueAt(y, 0, 1);
// collect x, y points into the copy arrays
xArray[0] = x;
yArray[0] = y;
}
xyTableCopyList.add(xArray);
xyTableCopyList.add(yArray);
// update any extra columns added by the user (these will show
// derivatives, differences etc.)
updateExtraColumns(rowCount);
}
/**
* Updates any extra columns added by the user to the XYTable.
*/
private void updateExtraColumns(int rowCount) {
if (extraColumnList.size() == 0) {
return;
}
for (int column = 2; column < extraColumnList.size() + 2; column++) {
Double[] copyArray = new Double[rowCount];
int columnType = extraColumnList.get(column - 2);
switch (columnType) {
case COL_DERIVATIVE:
for (int row = 0; row < rowCount; row++) {
String str = (String) listener.getXYValueAt(row, 0);
if (!"".equals(str)) {
double x = Double.parseDouble(str);
double d = derivative.value(x);// evaluateExpression(derivative.getLabel()
// + "(" + x + ")");
listener.setXYValueAt(d, row, column);
copyArray[row] = d;
}
}
break;
case COL_DERIVATIVE2:
for (int row = 0; row < rowCount; row++) {
String str = (String) listener.getXYValueAt(row, 0);
if (!"".equals(str)) {
double x = Double.parseDouble(str);
double d2 = derivative2.value(x);// evaluateExpression(derivative2.getLabel()
// + "(" + x + ")");
listener.setXYValueAt(d2, row, column);
copyArray[row] = d2;
}
}
break;
case COL_CURVATURE:
for (int row = 0; row < rowCount; row++) {
String str1 = (String) listener.getXYValueAt(row, 0);
String str2 = (String) listener.getXYValueAt(row, 1);
if (!"".equals(str1) && !"".equals(str2)) {
double x = Double.parseDouble(str1);
double y = Double.parseDouble(str2);
MyVecNode vec = new MyVecNode(kernel,
new MyDouble(kernel, x),
new MyDouble(kernel, y));
ExpressionNode point = new ExpressionNode(kernel, vec,
Operation.NO_OPERATION, null);
point.setForcePoint();
AlgoDependentPoint pointAlgo = new AlgoDependentPoint(
cons, point, false);
cons.removeFromConstructionList(pointAlgo);
AlgoCurvature curvature = new AlgoCurvature(cons,
(GeoPoint) pointAlgo.getOutput(0), selectedGeo);
cons.removeFromConstructionList(curvature);
double c = ((GeoNumeric) curvature.getOutput(0))
.getDouble();
// double c = evaluateExpression(
// "Curvature[ (" + x + "," + y + ")," +
// selectedGeo.getLabel() + "]");
listener.setXYValueAt(c, row, column);
copyArray[row] = c;
}
}
break;
case COL_DIFFERENCE:
for (int row = 1; row < rowCount; row++) {
String prevValue = (String) listener.getXYValueAt(row - 1,
column - 1);
String xValue = (String) listener.getXYValueAt(row,
column - 1);
if (!prevValue.isEmpty() && !xValue.isEmpty()) {
double prev = Double.parseDouble(prevValue);
double x = Double.parseDouble(xValue);
listener.setXYValueAt(x - prev, row, column);
copyArray[row] = x - prev;
} else {
listener.setXYValueAt(null, row, column);
copyArray[row] = null;
}
}
break;
}
xyTableCopyList.add(copyArray);
}
}
public void addColumn(int columnType) {
extraColumnList.add(columnType);
listener.addTableColumn(getColumnName(columnType));
}
public void removeColumn() {
extraColumnList.remove(extraColumnList.size() - 1);
}
public void applyStep(double value) {
step = value;
}
public void applyHigh(double value) {
double y = selectedGeo.value(value);
highPoint.setCoords(value, y, 1);
highPoint.updateCascade();
highPoint.updateRepaint();
}
public void applyLow(double value) {
double y = selectedGeo.value(value);
lowPoint.setCoords(value, y, 1);
lowPoint.updateCascade();
lowPoint.updateRepaint();
}
// ====================================================
// View Implementation
// ====================================================
public boolean isValid() {
return !(selectedGeo == null || testPoint == null || lowPoint == null
|| highPoint == null);
}
public void update(GeoElement geo, boolean isPoints) {
if (selectedGeo.equals(geo)) {
listener.setGeoName(
selectedGeo.toString(StringTemplate.defaultTemplate));
}
else if (isPoints && testPoint.equals(geo)) {
double[] coords = new double[3];
testPoint.getCoords(coords);
this.start = coords[0];
listener.changeTableSelection();
return;
}
else if (!isPoints && (lowPoint.equals(geo) || highPoint.equals(geo))) {
listener.updateHighAndLow(lowPoint.x > highPoint.x,
lowPoint.equals(geo));
return;
}
}
// ====================================================
// Geo Selection Listener
// ====================================================
private double getStartX() {
GPoint mouse = activeEV.getEuclidianController().getMouseLoc();
int mouseX = mouse == null ? activeEV.getWidth() / 2
: activeEV.getEuclidianController().getMouseLoc().getX();
return activeEV.toRealWorldCoordX(mouseX);
}
/**
* Sets the function to be inspected and updates the entire GUI
*
* @param geo
* The function to be inspected
*/
public void insertGeoElement(GeoElement geo) {
clearGeoList();
selectedGeo = (GeoFunction) geo;
listener.setGeoName(getTitleString());
start = getStartX();
// initial step = EV grid step
step = 0.25 * kernel.getApplication().getActiveEuclidianView()
.getGridDistances()[0];
listener.setStepText("" + step);
defineDisplayGeos();
double x = getInitialX() - 4 * step;
double y = selectedGeo.value(x);
lowPoint.setCoords(x, y, 1);
x = getInitialX() + 4 * step;
y = selectedGeo.value(x);
highPoint.setCoords(x, y, 1);
lowPoint.updateCascade();
highPoint.updateCascade();
activeEV = app.getActiveEuclidianView();
}
public void stepStartForward() {
start += step;
}
public void stepStartBackward() {
start -= step;
}
// ====================================================
// Update/Create Display Geos
// ====================================================
private void defineDisplayGeos() {
// remove all geos
clearGeoList();
GeoFunction f = selectedGeo;
// create XY table geos
// ========================================
// test point
AlgoPointOnPath pAlgo = new AlgoPointOnPath(cons, f,
(activeEV.getXmin() + activeEV.getXmax()) / 2, 0);
cons.removeFromConstructionList(pAlgo);
testPoint = (GeoPoint) pAlgo.getOutput(0);
testPoint.setObjColor(listener.getColor(Colors.GEO));
testPoint.setPointSize(4);
testPoint.setLayer(f.getLayer() + 1);
pointTabGeoList.add(testPoint);
// X segment
ExpressionNode xcoord = new ExpressionNode(kernel, testPoint,
Operation.XCOORD, null);
MyVecNode vec = new MyVecNode(kernel, xcoord,
new MyDouble(kernel, 0.0));
ExpressionNode point = new ExpressionNode(kernel, vec,
Operation.NO_OPERATION, null);
point.setForcePoint();
AlgoDependentPoint pointAlgo = new AlgoDependentPoint(cons, point,
false);
cons.removeFromConstructionList(pointAlgo);
AlgoJoinPointsSegment seg1 = new AlgoJoinPointsSegment(cons, testPoint,
(GeoPoint) pointAlgo.getOutput(0), null, false);
// cons.removeFromConstructionList(seg1);
xSegment = seg1.getOutput(0);
xSegment.setSelectionAllowed(false);
xSegment.setObjColor(listener.getColor(Colors.GEO));
xSegment.setLineThickness(3);
xSegment.setLineType(EuclidianStyleConstants.LINE_TYPE_DASHED_SHORT);
xSegment.setEuclidianVisible(true);
xSegment.setFixed(true);
pointTabGeoList.add(xSegment);
// Y segment
ExpressionNode ycoord = new ExpressionNode(kernel, testPoint,
Operation.YCOORD, null);
MyVecNode vecy = new MyVecNode(kernel, new MyDouble(kernel, 0.0),
ycoord);
ExpressionNode pointy = new ExpressionNode(kernel, vecy,
Operation.NO_OPERATION, null);
pointy.setForcePoint();
AlgoDependentPoint pointAlgoy = new AlgoDependentPoint(cons, pointy,
false);
cons.removeFromConstructionList(pointAlgoy);
AlgoJoinPointsSegment seg2 = new AlgoJoinPointsSegment(cons, testPoint,
(GeoPoint) pointAlgoy.getOutput(0), null, false);
// cons.removeFromConstructionList(seg2);
ySegment = seg2.getOutput(0);
ySegment.setSelectionAllowed(false);
ySegment.setObjColor(listener.getColor(Colors.GEO));
ySegment.setLineThickness(3);
ySegment.setLineType(EuclidianStyleConstants.LINE_TYPE_DASHED_SHORT);
ySegment.setEuclidianVisible(true);
ySegment.setFixed(true);
pointTabGeoList.add(ySegment);
// tangent line
AlgoTangentFunctionPoint tangent = new AlgoTangentFunctionPoint(cons,
testPoint, f);
cons.removeFromConstructionList(tangent);
tangentLine = tangent.getOutput(0);
tangentLine.setSelectionAllowed(false);
tangentLine.setObjColor(listener.getColor(Colors.GEO));
tangentLine.setEuclidianVisible(false);
pointTabGeoList.add(tangentLine);
// osculating circle
AlgoOsculatingCircle oc = new AlgoOsculatingCircle(cons, testPoint, f);
cons.removeFromConstructionList(oc);
oscCircle = oc.getOutput(0);
oscCircle.setSelectionAllowed(false);
oscCircle.setObjColor(listener.getColor(Colors.GEO));
oscCircle.setEuclidianVisible(false);
pointTabGeoList.add(oscCircle);
// derivative
AlgoDerivative deriv = new AlgoDerivative(cons, f, true,
new EvalInfo(false));
cons.removeFromConstructionList(deriv);
derivative = (GeoFunction) deriv.getOutput(0);
derivative.setEuclidianVisible(false);
hiddenGeoList.add(derivative);
// 2nd derivative
AlgoDerivative deriv2 = new AlgoDerivative(cons, derivative, true,
new EvalInfo(false));
cons.removeFromConstructionList(deriv2);
derivative2 = (GeoFunction) deriv2.getOutput(0);
derivative2.setEuclidianVisible(false);
hiddenGeoList.add(derivative2);
// point list
pts = new GeoList(cons);
pts.setEuclidianVisible(true);
pts.setObjColor(GeoGebraColorConstants.DARK_GRAY);
pts.setPointSize(3);
pts.setLayer(f.getLayer() + 1);
pts.setSelectionAllowed(false);
for (int i = 0; i < pointCount; i++) {
pts.add(new GeoPoint(cons));
}
pointTabGeoList.add(pts);
// create interval table geos
// ================================================
// interval points
AlgoPointOnPath pxAlgo = new AlgoPointOnPath(cons, f,
(2 * activeEV.getXmin() + activeEV.getXmax()) / 3, 0);
cons.removeFromConstructionList(pxAlgo);
lowPoint = (GeoPoint) pxAlgo.getOutput(0);
lowPoint.setEuclidianVisible(false);
lowPoint.setPointSize(4);
lowPoint.setObjColor(listener.getColor(Colors.GEO));
lowPoint.setLayer(f.getLayer() + 1);
intervalTabGeoList.add(lowPoint);
AlgoPointOnPath pyAlgo = new AlgoPointOnPath(cons, f,
(activeEV.getXmin() + 2 * activeEV.getXmax()) / 3, 0);
cons.removeFromConstructionList(pyAlgo);
highPoint = (GeoPoint) pyAlgo.getOutput(0);
highPoint.setEuclidianVisible(false);
highPoint.setPointSize(4);
highPoint.setObjColor(listener.getColor(Colors.GEO));
highPoint.setLayer(f.getLayer() + 1);
intervalTabGeoList.add(highPoint);
ExpressionNode low = new ExpressionNode(kernel, lowPoint,
Operation.XCOORD, null);
ExpressionNode high = new ExpressionNode(kernel, highPoint,
Operation.XCOORD, null);
FunctionVariable x = new FunctionVariable(kernel);
ExpressionNode fx = x.wrap();
ExpressionNode expr = fx.apply(Operation.LESS_EQUAL, high)
.apply(Operation.AND, fx.apply(Operation.GREATER_EQUAL, low))
.apply(Operation.IF, f.wrap().apply(Operation.FUNCTION, x));
AlgoDependentNumber xLow = new AlgoDependentNumber(cons, low, false);
cons.removeFromConstructionList(xLow);
AlgoDependentNumber xHigh = new AlgoDependentNumber(cons, high, false);
cons.removeFromConstructionList(xHigh);
AlgoDependentFunction interval = new AlgoDependentFunction(cons,
new Function(expr, x), false);
functionInterval = interval.getOutput(0);
functionInterval.setSelectionAllowed(false);
functionInterval.setEuclidianVisible(false);
functionInterval.setLineThickness(selectedGeo.getLineThickness() + 5);
functionInterval.setObjColor(listener.getColor(Colors.GEO));
functionInterval.setLayer(f.getLayer() + 1);
intervalTabGeoList.add(functionInterval);
AlgoIntegralDefinite inte = new AlgoIntegralDefinite(cons, selectedGeo,
(GeoNumberValue) xLow.getOutput(0),
(GeoNumberValue) xHigh.getOutput(0), null, false);
cons.removeFromConstructionList(inte);
integralGeo = inte.getOutput(0);
integralGeo.setSelectionAllowed(false);
integralGeo.setEuclidianVisible(false);
integralGeo.setObjColor(listener.getColor(Colors.GEO));
intervalTabGeoList.add(integralGeo);
ExpressionNode en = new ExpressionNode(kernel, selectedGeo,
Operation.ABS, null);
AlgoDependentFunction funAlgo = new AlgoDependentFunction(cons,
(Function) en.evaluate(StringTemplate.defaultTemplate), false);
// the antiderivative of a function containing the absolute function
// might be difficult to find if it exists at all. Therefore the
// definite integral is calculated numerically.
AlgoIntegralDefinite area = new AlgoIntegralDefinite(cons,
(GeoFunction) funAlgo.getOutput(0),
(GeoNumberValue) xLow.getOutput(0),
(GeoNumberValue) xHigh.getOutput(0), null, true);
cons.removeFromConstructionList(area);
areaGeo = area.getOutput(0);
areaGeo.setSelectionAllowed(false);
areaGeo.setEuclidianVisible(false);
intervalTabGeoList.add(areaGeo);
AlgoLengthFunction len = new AlgoLengthFunction(cons, selectedGeo,
(GeoNumeric) xLow.getOutput(0),
(GeoNumeric) xHigh.getOutput(0));
cons.removeFromConstructionList(len);
lengthGeo = len.getOutput(0);
hiddenGeoList.add(lengthGeo);
minPoint = new GeoPoint(cons);
minPoint.setEuclidianVisible(false);
minPoint.setPointSize(4);
minPoint.setPointStyle(
EuclidianStyleConstants.POINT_STYLE_FILLED_DIAMOND);
minPoint.setObjColor(listener.getColor(Colors.GEO).darker());
minPoint.setLayer(f.getLayer() + 1);
minPoint.setFixed(true);
intervalTabGeoList.add(minPoint);
maxPoint = new GeoPoint(cons);
maxPoint.setEuclidianVisible(false);
maxPoint.setPointSize(4);
maxPoint.setPointStyle(
EuclidianStyleConstants.POINT_STYLE_FILLED_DIAMOND);
maxPoint.setObjColor(listener.getColor(Colors.GEO).darker());
maxPoint.setLayer(f.getLayer() + 1);
maxPoint.setFixed(true);
intervalTabGeoList.add(maxPoint);
// process the geos
// ==================================================
// add the display geos to the active EV and hide the tooltips
for (GeoElement geo : intervalTabGeoList) {
activeEV.add(geo);
geo.addView(App.VIEW_FUNCTION_INSPECTOR);
geo.setTooltipMode(GeoElement.TOOLTIP_OFF);
geo.update();
}
for (GeoElement geo : pointTabGeoList) {
activeEV.add(geo);
geo.addView(App.VIEW_FUNCTION_INSPECTOR);
geo.setTooltipMode(GeoElement.TOOLTIP_OFF);
geo.update();
}
updateTestPoint();
activeEV.repaint();
}
public void updateTestPoint() {
if (testPoint == null) {
return;
}
int row = listener.getSelectedXYRow();
if (row >= 0) {
String str = (String) listener.getXYValueAt(row, 0);
if (!"".equals(str)) {
double x = Double.parseDouble(str);
double y = selectedGeo.value(x);
testPoint.setCoords(x, y, 1);
testPoint.updateRepaint();
}
}
}
public void clearGeoList() {
for (GeoElement geo : intervalTabGeoList) {
if (geo != null) {
geo.remove();
}
}
intervalTabGeoList.clear();
for (GeoElement geo : pointTabGeoList) {
if (geo != null) {
geo.remove();
}
}
pointTabGeoList.clear();
for (GeoElement geo : hiddenGeoList) {
if (geo != null) {
geo.remove();
}
}
hiddenGeoList.clear();
rootGeos = null;
}
public void updateIntervalGeoVisiblity() {
// minPoint.setEuclidianVisible(tableInterval.isRowSelected(0));
minPoint.setEuclidianVisible(false);
minPoint.update();
// maxPoint.setEuclidianVisible(tableInterval.isRowSelected(1));
maxPoint.setEuclidianVisible(false);
maxPoint.update();
// integralGeo.setEuclidianVisible(tableInterval.isRowSelected(5));
areaGeo.setEuclidianVisible(false);
areaGeo.update();
integralGeo.setEuclidianVisible(true);
integralGeo.update();
activeEV.repaint();
}
public void copyPointsToSpreadsheet(int colCount, int rowCount) {
GeoElement geo = null;
int targetColumn = app.getSpreadsheetTableModel()
.getHighestUsedColumn();
for (int c = 0; c < colCount; c++) {
targetColumn++;
for (int row = 0; row < rowCount + 1; row++) {
// copy table header
if (row == 0) {
geo = new GeoText(cons, getColumnNameForCopy(c));
processCellGeo(geo, targetColumn, row);
}
// copy column data value
else if (xyTableCopyList.get(c)[row - 1] != null) {
geo = new GeoNumeric(cons, xyTableCopyList.get(c)[row - 1]);
processCellGeo(geo, targetColumn, row);
}
}
}
}
public void copyIntervalsToSpreadsheet(int colCount, int rowCount) {
GeoElement geo = null;
int targetColumn = app.getSpreadsheetTableModel()
.getHighestUsedColumn();
for (int c = 0; c < colCount; c++) {
targetColumn++;
for (int row = 0; row < rowCount; row++) {
// first column has property names
if (c == 0 && property.get(row) != null) {
geo = new GeoText(cons, property.get(row));
processCellGeo(geo, targetColumn, row);
}
// remaining columns have data
else if (value2.get(row) != null) {
for (int k = 0; k < value2.get(row).length; k++) {
if (value2.get(row)[k] != null) {
geo = new GeoNumeric(cons, value2.get(row)[k]);
processCellGeo(geo, targetColumn + k, row);
}
}
}
}
}
}
private static void processCellGeo(GeoElement geo, int column, int row) {
geo.setLabel(GeoElementSpreadsheet.getSpreadsheetCellName(column, row));
geo.setEuclidianVisible(false);
geo.setAuxiliaryObject(true);
geo.update();
}
public void setStart(double value) {
start = value;
}
public String[] getColumnNames() {
setColumnNames();
return columnNames;
}
public String[] getIntervalColumnNames() {
String[] names = { loc.getPlain("fncInspector.Property"),
loc.getPlain("fncInspector.Value") };
return names;
}
public int getPrintFigures() {
return printFigures;
}
public void setPrintFigures(int printFigures) {
this.printFigures = printFigures;
}
public int getPrintDecimals() {
return printDecimals;
}
public void setPrintDecimals(int printDecimals) {
this.printDecimals = printDecimals;
}
public void applyDecimalPlaces(int index) {
if (index < 8) // decimal places
{
printDecimals = OptionsMenu.roundingMenuLookup(index);
printFigures = -1;
} else // significant figures
{
printDecimals = -1;
printFigures = OptionsMenu.roundingMenuLookup(index);
}
listener.changedNumberFormat();
}
public double getInitialX() {
return getStartX();
}
// public void setInitialX(double initialX) {
// this.initialX = initialX;
// }
}