plotting
This commit is contained in:
parent
7484d8b1b6
commit
db9daaa8ac
@ -32,7 +32,8 @@
|
|||||||
на итальянский - Gabriele Ravanetti\n\n
|
на итальянский - Gabriele Ravanetti\n\n
|
||||||
Это приложение использует следующие открытые библиотеки:\n
|
Это приложение использует следующие открытые библиотеки:\n
|
||||||
<a href="http://simple.sourceforge.net">Simple (XML serialization)</a>\n
|
<a href="http://simple.sourceforge.net">Simple (XML serialization)</a>\n
|
||||||
<a href="http://meditorworld.appspot.com/meditor.txt">JSCL</a>
|
<a href="http://meditorworld.appspot.com/meditor.txt">JSCL</a>\n
|
||||||
|
<a href="http://www.achartengine.org/">AChartEngine</a>
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="c_undo">назад</string>
|
<string name="c_undo">назад</string>
|
||||||
@ -40,6 +41,10 @@
|
|||||||
<string name="c_paste">вставить</string>
|
<string name="c_paste">вставить</string>
|
||||||
<string name="c_vars">переменные</string>
|
<string name="c_vars">переменные</string>
|
||||||
|
|
||||||
|
<string name="c_copy">Копировать</string>
|
||||||
|
<string name="c_plot">Построить график</string>
|
||||||
|
<string name="c_graph">График</string>
|
||||||
|
|
||||||
<string name="c_calc_color_display_title">Подсветка выражений</string>
|
<string name="c_calc_color_display_title">Подсветка выражений</string>
|
||||||
<string name="c_calc_round_result_title">Округление результата</string>
|
<string name="c_calc_round_result_title">Округление результата</string>
|
||||||
<string name="c_calc_round_result_summary">Включает/выключает округление результата</string>
|
<string name="c_calc_round_result_summary">Включает/выключает округление результата</string>
|
||||||
@ -235,10 +240,9 @@ deg(4.67748) = 268\n
|
|||||||
(2i + 1) ^ = -3 + 4i\n
|
(2i + 1) ^ = -3 + 4i\n
|
||||||
e ^ i = 0.5403 + 0.84147i\n
|
e ^ i = 0.5403 + 0.84147i\n
|
||||||
\n
|
\n
|
||||||
<b>Умеет ли К++ рисовать графики функций?</b>\n
|
<b>Умеет ли К++ строить графики функций?</b>\n
|
||||||
\n
|
|
||||||
Нет.\n
|
|
||||||
\n
|
\n
|
||||||
|
Да, введите выражение с 1 неизвестной переменной (например, cos(t)) и нажмите на результат. В контекстном меню выберите \'Построить график\'\n
|
||||||
<b>Поддерживает ли К++ матричные вычисления?</b>\n
|
<b>Поддерживает ли К++ матричные вычисления?</b>\n
|
||||||
\n
|
\n
|
||||||
Нет.\n
|
Нет.\n
|
||||||
|
@ -33,7 +33,8 @@
|
|||||||
Italian - Gabriele Ravanetti\n\n
|
Italian - Gabriele Ravanetti\n\n
|
||||||
This application uses next open source libraries:\n
|
This application uses next open source libraries:\n
|
||||||
<a href="http://simple.sourceforge.net">Simple (XML serialization)</a>\n
|
<a href="http://simple.sourceforge.net">Simple (XML serialization)</a>\n
|
||||||
<a href="http://meditorworld.appspot.com/meditor.txt">JSCL</a>
|
<a href="http://meditorworld.appspot.com/meditor.txt">JSCL</a>\n
|
||||||
|
<a href="http://www.achartengine.org/">AChartEngine</a>
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="c_undo">undo</string>
|
<string name="c_undo">undo</string>
|
||||||
@ -43,6 +44,10 @@
|
|||||||
<string name="c_paste">paste</string>
|
<string name="c_paste">paste</string>
|
||||||
<string name="c_vars">vars</string>
|
<string name="c_vars">vars</string>
|
||||||
|
|
||||||
|
<string name="c_copy">Copy</string>
|
||||||
|
<string name="c_plot">Plot graph</string>
|
||||||
|
<string name="c_graph">Graph</string>
|
||||||
|
|
||||||
<string name="c_calc_color_display_title">Highlight expressions</string>
|
<string name="c_calc_color_display_title">Highlight expressions</string>
|
||||||
<string name="c_calc_round_result_title">Round result</string>
|
<string name="c_calc_round_result_title">Round result</string>
|
||||||
<string name="c_calc_round_result_summary">Toggles rounding of the result</string>
|
<string name="c_calc_round_result_summary">Toggles rounding of the result</string>
|
||||||
@ -240,7 +245,7 @@ e ^ i = 0.5403 + 0.84147i\n
|
|||||||
\n
|
\n
|
||||||
<b>Can C++ plot graph of the function?</b>\n
|
<b>Can C++ plot graph of the function?</b>\n
|
||||||
\n
|
\n
|
||||||
No, currently C++ cannot plot functions\' graphs.\n
|
Yes, type expression which contains 1 undefined variable (e.g. cos(t) and t has no value) and click on the result. In the context menu choose \'Plot graph\'.\n
|
||||||
\n
|
\n
|
||||||
<b>Does C++ support matrix calculations?</b>\n
|
<b>Does C++ support matrix calculations?</b>\n
|
||||||
\n
|
\n
|
||||||
|
@ -3,22 +3,10 @@ package org.solovyev.android.calculator;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import jscl.math.Expression;
|
|
||||||
import jscl.math.Generic;
|
import jscl.math.Generic;
|
||||||
import jscl.math.JsclInteger;
|
|
||||||
import jscl.math.NumericWrapper;
|
|
||||||
import jscl.math.function.Constant;
|
import jscl.math.function.Constant;
|
||||||
import jscl.math.numeric.Complex;
|
|
||||||
import jscl.math.numeric.Numeric;
|
|
||||||
import jscl.math.numeric.Real;
|
|
||||||
import org.achartengine.ChartFactory;
|
import org.achartengine.ChartFactory;
|
||||||
import org.achartengine.model.XYMultipleSeriesDataset;
|
|
||||||
import org.achartengine.model.XYSeries;
|
|
||||||
import org.achartengine.renderer.XYMultipleSeriesRenderer;
|
|
||||||
import org.achartengine.renderer.XYSeriesRenderer;
|
|
||||||
import org.achartengine.util.MathHelper;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.android.calculator.help.HelpActivity;
|
import org.solovyev.android.calculator.help.HelpActivity;
|
||||||
import org.solovyev.common.utils.StringUtils;
|
import org.solovyev.common.utils.StringUtils;
|
||||||
|
|
||||||
@ -57,8 +45,9 @@ public class CalculatorActivityLauncher {
|
|||||||
context.startActivity(new Intent(context, CalculatorVarsActivity.class));
|
context.startActivity(new Intent(context, CalculatorVarsActivity.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void plotGraph(@NotNull final Context context, @NotNull Generic generic, @NotNull Constant constant) throws ArithmeticException {
|
public static void plotGraph(@NotNull final Context context, @NotNull Generic generic, @NotNull Constant constant){
|
||||||
final Intent intent = new Intent();
|
final Intent intent = new Intent();
|
||||||
|
intent.putExtra(ChartFactory.TITLE, context.getString(R.string.c_graph));
|
||||||
intent.putExtra(CalculatorPlotActivity.INPUT, new CalculatorPlotActivity.Input(generic.toString(), constant.getName()));
|
intent.putExtra(CalculatorPlotActivity.INPUT, new CalculatorPlotActivity.Input(generic.toString(), constant.getName()));
|
||||||
intent.setClass(context, CalculatorPlotActivity.class);
|
intent.setClass(context, CalculatorPlotActivity.class);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
|
@ -8,6 +8,7 @@ package org.solovyev.android.calculator;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.text.ClipboardManager;
|
import android.text.ClipboardManager;
|
||||||
@ -69,58 +70,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
|
|||||||
this.editor.setHighlightText(preferences.getBoolean(activity.getString(R.string.p_calc_color_display_key), colorExpressionsInBracketsDefault));
|
this.editor.setHighlightText(preferences.getBoolean(activity.getString(R.string.p_calc_color_display_key), colorExpressionsInBracketsDefault));
|
||||||
|
|
||||||
this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay);
|
this.display = (CalculatorDisplay) activity.findViewById(R.id.calculatorDisplay);
|
||||||
this.display.setOnClickListener(new View.OnClickListener() {
|
this.display.setOnClickListener(new CalculatorDisplayOnClickListener(activity));
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (v instanceof CalculatorDisplay) {
|
|
||||||
final CalculatorDisplay cd = (CalculatorDisplay)v;
|
|
||||||
if (cd.isValid()) {
|
|
||||||
switch (cd.getJsclOperation()) {
|
|
||||||
case simplify:
|
|
||||||
Generic genericResult = cd.getGenericResult();
|
|
||||||
if ( genericResult != null ) {
|
|
||||||
final Set<Constant> notSystemConstants = new HashSet<Constant>();
|
|
||||||
for (Constant constant : genericResult.getConstants()) {
|
|
||||||
Var var = CalculatorEngine.instance.getVarsRegister().get(constant.getName());
|
|
||||||
if (var != null && !var.isSystem()) {
|
|
||||||
notSystemConstants.add(constant);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( notSystemConstants.size() > 0 ) {
|
|
||||||
if (notSystemConstants.size() > 1) {
|
|
||||||
copyResult(activity, cd);
|
|
||||||
} else {
|
|
||||||
final Constant constant = CollectionsUtils.getFirstCollectionElement(notSystemConstants);
|
|
||||||
assert constant != null;
|
|
||||||
try {
|
|
||||||
CalculatorActivityLauncher.plotGraph(activity, genericResult, constant);
|
|
||||||
} catch (ArithmeticException e) {
|
|
||||||
copyResult(activity, cd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
copyResult(activity, cd);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
copyResult(activity, cd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case elementary:
|
|
||||||
case numeric:
|
|
||||||
copyResult(activity, cd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final String errorMessage = cd.getErrorMessage();
|
|
||||||
if ( errorMessage != null ) {
|
|
||||||
showEvaluationError(activity, errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
final CalculatorHistoryState lastState = CalculatorHistory.instance.getLastHistoryState();
|
final CalculatorHistoryState lastState = CalculatorHistory.instance.getLastHistoryState();
|
||||||
if (lastState == null) {
|
if (lastState == null) {
|
||||||
@ -439,4 +389,73 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
|
|||||||
public CalculatorDisplay getDisplay() {
|
public CalculatorDisplay getDisplay() {
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class CalculatorDisplayOnClickListener implements View.OnClickListener {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final Activity activity;
|
||||||
|
|
||||||
|
public CalculatorDisplayOnClickListener(@NotNull Activity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (v instanceof CalculatorDisplay) {
|
||||||
|
final CalculatorDisplay cd = (CalculatorDisplay)v;
|
||||||
|
if (cd.isValid()) {
|
||||||
|
switch (cd.getJsclOperation()) {
|
||||||
|
case simplify:
|
||||||
|
final Generic genericResult = cd.getGenericResult();
|
||||||
|
if ( genericResult != null ) {
|
||||||
|
final Set<Constant> notSystemConstants = new HashSet<Constant>();
|
||||||
|
for (Constant constant : genericResult.getConstants()) {
|
||||||
|
Var var = CalculatorEngine.instance.getVarsRegister().get(constant.getName());
|
||||||
|
if (var != null && !var.isSystem()) {
|
||||||
|
notSystemConstants.add(constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( notSystemConstants.size() > 0 ) {
|
||||||
|
if (notSystemConstants.size() > 1) {
|
||||||
|
copyResult(activity, cd);
|
||||||
|
} else {
|
||||||
|
final CharSequence[] items = {activity.getText(R.string.c_plot), activity.getText(R.string.c_copy)};
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
|
builder.setItems(items, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int item) {
|
||||||
|
if (item == 0) {
|
||||||
|
final Constant constant = CollectionsUtils.getFirstCollectionElement(notSystemConstants);
|
||||||
|
assert constant != null;
|
||||||
|
CalculatorActivityLauncher.plotGraph(activity, genericResult, constant);
|
||||||
|
} else if ( item == 1 ) {
|
||||||
|
copyResult(activity, cd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.create().show();
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyResult(activity, cd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyResult(activity, cd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case elementary:
|
||||||
|
case numeric:
|
||||||
|
copyResult(activity, cd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final String errorMessage = cd.getErrorMessage();
|
||||||
|
if ( errorMessage != null ) {
|
||||||
|
showEvaluationError(activity, errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,7 @@ import android.view.Window;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import jscl.math.Expression;
|
import jscl.math.Expression;
|
||||||
import jscl.math.Generic;
|
import jscl.math.Generic;
|
||||||
import jscl.math.JsclInteger;
|
|
||||||
import jscl.math.NumericWrapper;
|
|
||||||
import jscl.math.function.Constant;
|
import jscl.math.function.Constant;
|
||||||
import jscl.math.numeric.Complex;
|
|
||||||
import jscl.math.numeric.Numeric;
|
|
||||||
import jscl.math.numeric.Real;
|
|
||||||
import jscl.text.MutableInt;
|
|
||||||
import jscl.text.ParseException;
|
import jscl.text.ParseException;
|
||||||
import org.achartengine.ChartFactory;
|
import org.achartengine.ChartFactory;
|
||||||
import org.achartengine.GraphicalView;
|
import org.achartengine.GraphicalView;
|
||||||
@ -37,16 +31,13 @@ import org.achartengine.renderer.XYSeriesRenderer;
|
|||||||
import org.achartengine.tools.PanListener;
|
import org.achartengine.tools.PanListener;
|
||||||
import org.achartengine.tools.ZoomEvent;
|
import org.achartengine.tools.ZoomEvent;
|
||||||
import org.achartengine.tools.ZoomListener;
|
import org.achartengine.tools.ZoomListener;
|
||||||
import org.achartengine.util.MathHelper;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.solovyev.android.calculator.plot.MyXYSeries;
|
||||||
|
import org.solovyev.android.calculator.plot.PlotUtils;
|
||||||
import org.solovyev.common.utils.MutableObject;
|
import org.solovyev.common.utils.MutableObject;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: serso
|
* User: serso
|
||||||
@ -63,7 +54,7 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
|
|
||||||
public static final String INPUT = "org.solovyev.android.calculator.CalculatorPlotActivity_input";
|
public static final String INPUT = "org.solovyev.android.calculator.CalculatorPlotActivity_input";
|
||||||
|
|
||||||
public static final long EVAL_DELAY_MILLIS = 400;
|
public static final long EVAL_DELAY_MILLIS = 200;
|
||||||
|
|
||||||
private XYChart chart;
|
private XYChart chart;
|
||||||
|
|
||||||
@ -78,8 +69,6 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private Constant variable;
|
private Constant variable;
|
||||||
|
|
||||||
private static final double MAX_Y_DIFF = Math.pow(10, 6);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -132,22 +121,11 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
double maxY = Double.MIN_VALUE;
|
double maxY = Double.MIN_VALUE;
|
||||||
|
|
||||||
for (XYSeries series : chart.getDataset().getSeries()) {
|
for (XYSeries series : chart.getDataset().getSeries()) {
|
||||||
if (checkMinMaxValue(series.getMinX())) {
|
|
||||||
minX = Math.min(minX, series.getMinX());
|
minX = Math.min(minX, series.getMinX());
|
||||||
}
|
|
||||||
|
|
||||||
if (checkMinMaxValue(series.getMinY())) {
|
|
||||||
minY = Math.min(minY, series.getMinY());
|
minY = Math.min(minY, series.getMinY());
|
||||||
}
|
|
||||||
|
|
||||||
if (checkMinMaxValue(series.getMaxX())) {
|
|
||||||
maxX = Math.max(maxX, series.getMaxX());
|
maxX = Math.max(maxX, series.getMaxX());
|
||||||
}
|
|
||||||
|
|
||||||
if (checkMinMaxValue(series.getMaxY())) {
|
|
||||||
maxY = Math.max(maxY, series.getMaxY());
|
maxY = Math.max(maxY, series.getMaxY());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(CalculatorPlotActivity.class.getName(), "min x: " + minX + ", min y: " + minY + ", max x: " + maxX + ", max y: " + maxY);
|
Log.d(CalculatorPlotActivity.class.getName(), "min x: " + minX + ", min y: " + minY + ", max x: " + maxX + ", max y: " + maxY);
|
||||||
Log.d(CalculatorPlotActivity.class.getName(), "Plot boundaries are " + plotBoundaries);
|
Log.d(CalculatorPlotActivity.class.getName(), "Plot boundaries are " + plotBoundaries);
|
||||||
@ -187,14 +165,15 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
});
|
});
|
||||||
graphContainer.addView(graphicalView);
|
graphContainer.addView(graphicalView);
|
||||||
|
|
||||||
updateDataSets(chart);
|
updateDataSets(chart, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkMinMaxValue(@NotNull Double value) {
|
|
||||||
return !value.equals(MathHelper.NULL_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateDataSets(@NotNull final XYChart chart) {
|
private void updateDataSets(@NotNull final XYChart chart) {
|
||||||
|
updateDataSets(chart, EVAL_DELAY_MILLIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDataSets(@NotNull final XYChart chart, long millisToWait) {
|
||||||
pendingOperation.setObject(new Runnable() {
|
pendingOperation.setObject(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -206,17 +185,17 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
|
|
||||||
//Log.d(CalculatorPlotActivity.class.getName(), "x = [" + dr.getXAxisMin() + ", " + dr.getXAxisMax() + "], y = [" + dr.getYAxisMin() + ", " + dr.getYAxisMax() + "]");
|
//Log.d(CalculatorPlotActivity.class.getName(), "x = [" + dr.getXAxisMin() + ", " + dr.getXAxisMax() + "], y = [" + dr.getYAxisMin() + ", " + dr.getYAxisMax() + "]");
|
||||||
|
|
||||||
final XYSeries realSeries = chart.getDataset().getSeriesAt(0);
|
final MyXYSeries realSeries = (MyXYSeries)chart.getDataset().getSeriesAt(0);
|
||||||
|
|
||||||
final XYSeries imagSeries;
|
final MyXYSeries imagSeries;
|
||||||
if (chart.getDataset().getSeriesCount() > 1) {
|
if (chart.getDataset().getSeriesCount() > 1) {
|
||||||
imagSeries = chart.getDataset().getSeriesAt(1);
|
imagSeries = (MyXYSeries)chart.getDataset().getSeriesAt(1);
|
||||||
} else {
|
} else {
|
||||||
imagSeries = new XYSeries(getImagFunctionName(CalculatorPlotActivity.this.variable));
|
imagSeries = new MyXYSeries(getImagFunctionName(CalculatorPlotActivity.this.variable), DEFAULT_NUMBER_OF_STEPS * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (addXY(dr.getXAxisMin(), dr.getXAxisMax(), expression, variable, realSeries, imagSeries, true)) {
|
if (PlotUtils.addXY(dr.getXAxisMin(), dr.getXAxisMax(), expression, variable, realSeries, imagSeries, true, DEFAULT_NUMBER_OF_STEPS)) {
|
||||||
if (chart.getDataset().getSeriesCount() <= 1) {
|
if (chart.getDataset().getSeriesCount() <= 1) {
|
||||||
chart.getDataset().addSeries(imagSeries);
|
chart.getDataset().addSeries(imagSeries);
|
||||||
chart.getRenderer().addSeriesRenderer(createImagRenderer());
|
chart.getRenderer().addSeriesRenderer(createImagRenderer());
|
||||||
@ -237,7 +216,7 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
new Handler().postDelayed(pendingOperation.getObject(), EVAL_DELAY_MILLIS);
|
new Handler().postDelayed(pendingOperation.getObject(), millisToWait);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -254,10 +233,10 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
|
private final static MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
|
||||||
|
|
||||||
private static XYChart prepareChart(final double minValue, final double maxValue, @NotNull final Generic expression, @NotNull final Constant variable) {
|
private static XYChart prepareChart(final double minValue, final double maxValue, @NotNull final Generic expression, @NotNull final Constant variable) {
|
||||||
final XYSeries realSeries = new XYSeries(getRealFunctionName(expression, variable));
|
final MyXYSeries realSeries = new MyXYSeries(getRealFunctionName(expression, variable), DEFAULT_NUMBER_OF_STEPS * 2);
|
||||||
final XYSeries imagSeries = new XYSeries(getImagFunctionName(variable));
|
final MyXYSeries imagSeries = new MyXYSeries(getImagFunctionName(variable), DEFAULT_NUMBER_OF_STEPS * 2);
|
||||||
|
|
||||||
boolean imagExists = addXY(minValue, maxValue, expression, variable, realSeries, imagSeries, false);
|
boolean imagExists = PlotUtils.addXY(minValue, maxValue, expression, variable, realSeries, imagSeries, false, DEFAULT_NUMBER_OF_STEPS);
|
||||||
|
|
||||||
final XYMultipleSeriesDataset data = new XYMultipleSeriesDataset();
|
final XYMultipleSeriesDataset data = new XYMultipleSeriesDataset();
|
||||||
data.addSeries(realSeries);
|
data.addSeries(realSeries);
|
||||||
@ -286,111 +265,6 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
return imagRenderer;
|
return imagRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean addXY(double minValue, double maxValue, Generic expression, Constant variable, @NotNull XYSeries realSeries, @NotNull XYSeries imagSeries, boolean addExtra) {
|
|
||||||
boolean imagExists = false;
|
|
||||||
|
|
||||||
double min = Math.min(minValue, maxValue);
|
|
||||||
double max = Math.max(minValue, maxValue);
|
|
||||||
double dist = max - min;
|
|
||||||
if (addExtra) {
|
|
||||||
min = min - dist;
|
|
||||||
max = max + dist;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int numberOfSteps = DEFAULT_NUMBER_OF_STEPS;
|
|
||||||
final double step = Math.max( dist / numberOfSteps, 0.000000001);
|
|
||||||
|
|
||||||
Double prevRealY = null;
|
|
||||||
Double prevX = null;
|
|
||||||
Double prevImagY = null;
|
|
||||||
|
|
||||||
final MutableInt realSeriesI = new MutableInt(0);
|
|
||||||
final MutableInt imagSeriesI = new MutableInt(0);
|
|
||||||
|
|
||||||
double x = min;
|
|
||||||
while (x <= max) {
|
|
||||||
|
|
||||||
boolean needToCalculateRealY = needToCalculate(realSeries, step, x, realSeriesI);
|
|
||||||
|
|
||||||
if (needToCalculateRealY) {
|
|
||||||
final Complex c = calculatorExpression(expression, variable, x);
|
|
||||||
Double y = prepareY(c.realPart());
|
|
||||||
if (y != null) {
|
|
||||||
addSingularityPoint(realSeries, prevX, x, prevRealY, y);
|
|
||||||
realSeries.add(x, y);
|
|
||||||
prevRealY = y;
|
|
||||||
prevX = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean needToCalculateImagY = needToCalculate(imagSeries, step, x, imagSeriesI);
|
|
||||||
if (needToCalculateImagY) {
|
|
||||||
y = prepareY(c.imaginaryPart());
|
|
||||||
if (y != null) {
|
|
||||||
addSingularityPoint(imagSeries, prevX, x, prevImagY, y);
|
|
||||||
imagSeries.add(x, y);
|
|
||||||
prevImagY = y;
|
|
||||||
prevX = x;
|
|
||||||
}
|
|
||||||
if (c.imaginaryPart() != 0d) {
|
|
||||||
imagExists = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
boolean needToCalculateImagY = needToCalculate(imagSeries, step, x, imagSeriesI);
|
|
||||||
if (needToCalculateImagY) {
|
|
||||||
final Complex c = calculatorExpression(expression, variable, x);
|
|
||||||
Double y = prepareY(c.imaginaryPart());
|
|
||||||
if (y != null) {
|
|
||||||
addSingularityPoint(imagSeries, prevX, x, prevImagY, y);
|
|
||||||
imagSeries.add(x, y);
|
|
||||||
prevImagY = y;
|
|
||||||
prevX = x;
|
|
||||||
}
|
|
||||||
if (c.imaginaryPart() != 0d) {
|
|
||||||
imagExists = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
x += step;
|
|
||||||
}
|
|
||||||
|
|
||||||
sortSeries(realSeries);
|
|
||||||
if (imagExists) {
|
|
||||||
sortSeries(imagSeries);
|
|
||||||
}
|
|
||||||
|
|
||||||
return imagExists;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant variable, double x) {
|
|
||||||
return unwrap(expression.substitute(variable, Expression.valueOf(x)).numeric());
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo serso: UNABLE TO PLOT i/ln(t)!!!
|
|
||||||
|
|
||||||
private static void addSingularityPoint(@NotNull XYSeries series, @Nullable Double prevX, @NotNull Double x, @Nullable Double prevY, @NotNull Double y) {
|
|
||||||
if (prevX != null && prevY != null) {
|
|
||||||
if ( (Math.abs(y) > 0d && Math.abs(prevY / y) > MAX_Y_DIFF) || (Math.abs(prevY) > 0d && Math.abs(y / prevY) > MAX_Y_DIFF)) {
|
|
||||||
//Log.d(CalculatorPlotActivity.class.getName(), "Singularity! Prev point: (" + prevX + ", " + prevY + "), current point: (" +x+ ", " + y +")" );
|
|
||||||
//Log.d(CalculatorPlotActivity.class.getName(), String.valueOf(prevX + Math.abs(x - prevX) / 2) + ", null");
|
|
||||||
series.add( prevX + Math.abs(x - prevX) / 2, MathHelper.NULL_VALUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean needToCalculate(@NotNull XYSeries series, double step, double x, @NotNull MutableInt i) {
|
|
||||||
boolean needToCalculateY = true;
|
|
||||||
for ( ; i.intValue() < series.getItemCount(); i.increment() ){
|
|
||||||
if ( Math.abs(x - series.getX(i.intValue())) < step ) {
|
|
||||||
needToCalculateY = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return needToCalculateY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object onRetainNonConfigurationInstance() {
|
public Object onRetainNonConfigurationInstance() {
|
||||||
return new PlotBoundaries(chart.getRenderer());
|
return new PlotBoundaries(chart.getRenderer());
|
||||||
@ -464,27 +338,6 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sortSeries(@NotNull XYSeries series) {
|
|
||||||
final List<Point> values = new ArrayList<Point>(series.getItemCount());
|
|
||||||
|
|
||||||
for (int i = 0; i < series.getItemCount(); i++) {
|
|
||||||
values.add(new Point(series.getX(i), series.getY(i)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(values, new Comparator<Point>() {
|
|
||||||
@Override
|
|
||||||
public int compare(Point point, Point point1) {
|
|
||||||
return Double.compare(point.getX(), point1.getX());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Log.d(CalculatorPlotActivity.class.getName(), "Points for " + series.getTitle());
|
|
||||||
series.clear();
|
|
||||||
for (Point value : values) {
|
|
||||||
series.add(value.getX(), value.getY());
|
|
||||||
Log.d(CalculatorPlotActivity.class.getName(), "x = " + value.getX() + ", y = " + value.getY());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static XYSeriesRenderer createCommonRenderer() {
|
private static XYSeriesRenderer createCommonRenderer() {
|
||||||
@ -497,37 +350,6 @@ public class CalculatorPlotActivity extends Activity {
|
|||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static Double prepareY(double y) {
|
|
||||||
if (Double.isNaN(y)) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static Complex unwrap(@Nullable Generic numeric) {
|
|
||||||
if (numeric instanceof JsclInteger) {
|
|
||||||
return Complex.valueOf(((JsclInteger) numeric).intValue(), 0d);
|
|
||||||
} else if (numeric instanceof NumericWrapper) {
|
|
||||||
return unwrap(((NumericWrapper) numeric).content());
|
|
||||||
} else {
|
|
||||||
throw new ArithmeticException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static Complex unwrap(@Nullable Numeric content) {
|
|
||||||
if (content instanceof Real) {
|
|
||||||
return Complex.valueOf(((Real) content).doubleValue(), 0d);
|
|
||||||
} else if (content instanceof Complex) {
|
|
||||||
return ((Complex) content);
|
|
||||||
} else {
|
|
||||||
throw new ArithmeticException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static class Input implements Serializable {
|
public static class Input implements Serializable {
|
||||||
|
|
||||||
|
@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||||
|
* For more information, please, contact se.solovyev@gmail.com
|
||||||
|
* or visit http://se.solovyev.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import org.achartengine.model.XYSeries;
|
||||||
|
import org.achartengine.util.MathHelper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 12/5/11
|
||||||
|
* Time: 8:43 PM
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BEST SOLUTION IS TO MODIFY LIBRARY CLASS
|
||||||
|
* NOTE: this class is a copy of XYSeries with some modifications:
|
||||||
|
* 1. Possibility to insert point in th emiddle og the range
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class MyXYSeries extends XYSeries {
|
||||||
|
/**
|
||||||
|
* The series title.
|
||||||
|
*/
|
||||||
|
private String mTitle;
|
||||||
|
/**
|
||||||
|
* A list to contain the values for the X axis.
|
||||||
|
*/
|
||||||
|
private List<Double> mX = new ArrayList<Double>();
|
||||||
|
/**
|
||||||
|
* A list to contain the values for the Y axis.
|
||||||
|
*/
|
||||||
|
private List<Double> mY = new ArrayList<Double>();
|
||||||
|
/**
|
||||||
|
* The minimum value for the X axis.
|
||||||
|
*/
|
||||||
|
private double mMinX = MathHelper.NULL_VALUE;
|
||||||
|
/**
|
||||||
|
* The maximum value for the X axis.
|
||||||
|
*/
|
||||||
|
private double mMaxX = -MathHelper.NULL_VALUE;
|
||||||
|
/**
|
||||||
|
* The minimum value for the Y axis.
|
||||||
|
*/
|
||||||
|
private double mMinY = MathHelper.NULL_VALUE;
|
||||||
|
/**
|
||||||
|
* The maximum value for the Y axis.
|
||||||
|
*/
|
||||||
|
private double mMaxY = -MathHelper.NULL_VALUE;
|
||||||
|
/**
|
||||||
|
* The scale number for this series.
|
||||||
|
*/
|
||||||
|
private int mScaleNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a new XY series.
|
||||||
|
*
|
||||||
|
* @param title the series title.
|
||||||
|
*/
|
||||||
|
public MyXYSeries(String title) {
|
||||||
|
this(title, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyXYSeries(String title, int initialCapacity) {
|
||||||
|
super(title, 0);
|
||||||
|
|
||||||
|
this.mX = new ArrayList<Double>(initialCapacity);
|
||||||
|
this.mY = new ArrayList<Double>(initialCapacity);
|
||||||
|
|
||||||
|
mTitle = title;
|
||||||
|
mScaleNumber = 0;
|
||||||
|
|
||||||
|
initRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getScaleNumber() {
|
||||||
|
return mScaleNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the range for both axes.
|
||||||
|
*/
|
||||||
|
private void initRange() {
|
||||||
|
mMinX = MathHelper.NULL_VALUE;
|
||||||
|
mMaxX = -MathHelper.NULL_VALUE;
|
||||||
|
mMinY = MathHelper.NULL_VALUE;
|
||||||
|
mMaxY = -MathHelper.NULL_VALUE;
|
||||||
|
int length = getItemCount();
|
||||||
|
for (int k = 0; k < length; k++) {
|
||||||
|
double x = getX(k);
|
||||||
|
double y = getY(k);
|
||||||
|
updateRange(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the range on both axes.
|
||||||
|
*
|
||||||
|
* @param x the new x value
|
||||||
|
* @param y the new y value
|
||||||
|
*/
|
||||||
|
private void updateRange(double x, double y) {
|
||||||
|
mMinX = Math.min(mMinX, x);
|
||||||
|
mMaxX = Math.max(mMaxX, x);
|
||||||
|
mMinY = Math.min(mMinY, y);
|
||||||
|
mMaxY = Math.max(mMaxY, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the series title.
|
||||||
|
*
|
||||||
|
* @return the series title
|
||||||
|
*/
|
||||||
|
public String getTitle() {
|
||||||
|
return mTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the series title.
|
||||||
|
*
|
||||||
|
* @param title the series title
|
||||||
|
*/
|
||||||
|
public void setTitle(String title) {
|
||||||
|
mTitle = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new value to the series.
|
||||||
|
*
|
||||||
|
* @param x the value for the X axis
|
||||||
|
* @param y the value for the Y axis
|
||||||
|
*/
|
||||||
|
public synchronized void add(double x, double y) {
|
||||||
|
boolean added = false;
|
||||||
|
for (int i = 0; i < mX.size(); i++ ) {
|
||||||
|
if ( mX.get(i) > x ) {
|
||||||
|
mX.add(i, x);
|
||||||
|
mY.add(i, y);
|
||||||
|
added = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !added ) {
|
||||||
|
mX.add(x);
|
||||||
|
mY.add(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRange(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean needToAdd(double density, double x) {
|
||||||
|
boolean result = true;
|
||||||
|
|
||||||
|
for (Double x1 : mX) {
|
||||||
|
if (Math.abs(x - x1) < density) {
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an existing value from the series.
|
||||||
|
*
|
||||||
|
* @param index the index in the series of the value to remove
|
||||||
|
*/
|
||||||
|
public synchronized void remove(int index) {
|
||||||
|
double removedX = mX.remove(index);
|
||||||
|
double removedY = mY.remove(index);
|
||||||
|
if (removedX == mMinX || removedX == mMaxX || removedY == mMinY || removedY == mMaxY) {
|
||||||
|
initRange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all the existing values from the series.
|
||||||
|
*/
|
||||||
|
public synchronized void clear() {
|
||||||
|
mX.clear();
|
||||||
|
mY.clear();
|
||||||
|
initRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the X axis value at the specified index.
|
||||||
|
*
|
||||||
|
* @param index the index
|
||||||
|
* @return the X value
|
||||||
|
*/
|
||||||
|
public synchronized double getX(int index) {
|
||||||
|
return mX.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Y axis value at the specified index.
|
||||||
|
*
|
||||||
|
* @param index the index
|
||||||
|
* @return the Y value
|
||||||
|
*/
|
||||||
|
public synchronized double getY(int index) {
|
||||||
|
return mY.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the series item count.
|
||||||
|
*
|
||||||
|
* @return the series item count
|
||||||
|
*/
|
||||||
|
public synchronized int getItemCount() {
|
||||||
|
return mX == null ? 0 : mX.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum value on the X axis.
|
||||||
|
*
|
||||||
|
* @return the X axis minimum value
|
||||||
|
*/
|
||||||
|
public double getMinX() {
|
||||||
|
return mMinX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum value on the Y axis.
|
||||||
|
*
|
||||||
|
* @return the Y axis minimum value
|
||||||
|
*/
|
||||||
|
public double getMinY() {
|
||||||
|
return mMinY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum value on the X axis.
|
||||||
|
*
|
||||||
|
* @return the X axis maximum value
|
||||||
|
*/
|
||||||
|
public double getMaxX() {
|
||||||
|
return mMaxX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum value on the Y axis.
|
||||||
|
*
|
||||||
|
* @return the Y axis maximum value
|
||||||
|
*/
|
||||||
|
public double getMaxY() {
|
||||||
|
return mMaxY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||||
|
* For more information, please, contact se.solovyev@gmail.com
|
||||||
|
* or visit http://se.solovyev.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import jscl.math.Expression;
|
||||||
|
import jscl.math.Generic;
|
||||||
|
import jscl.math.JsclInteger;
|
||||||
|
import jscl.math.NumericWrapper;
|
||||||
|
import jscl.math.function.Constant;
|
||||||
|
import jscl.math.numeric.Complex;
|
||||||
|
import jscl.math.numeric.Numeric;
|
||||||
|
import jscl.math.numeric.Real;
|
||||||
|
import org.achartengine.util.MathHelper;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 12/5/11
|
||||||
|
* Time: 8:58 PM
|
||||||
|
*/
|
||||||
|
public final class PlotUtils {
|
||||||
|
|
||||||
|
private static final double MAX_Y_DIFF = Math.pow(10, 6);
|
||||||
|
|
||||||
|
// not intended for instantiation
|
||||||
|
private PlotUtils() {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean addXY(double minValue,
|
||||||
|
double maxValue,
|
||||||
|
@NotNull Generic expression,
|
||||||
|
@NotNull Constant variable,
|
||||||
|
@NotNull MyXYSeries realSeries,
|
||||||
|
@NotNull MyXYSeries imagSeries,
|
||||||
|
boolean addExtra,
|
||||||
|
int numberOfSteps) {
|
||||||
|
|
||||||
|
boolean imagExists = false;
|
||||||
|
|
||||||
|
double min = Math.min(minValue, maxValue);
|
||||||
|
double max = Math.max(minValue, maxValue);
|
||||||
|
double dist = max - min;
|
||||||
|
if (addExtra) {
|
||||||
|
min = min - dist;
|
||||||
|
max = max + dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
final double step = Math.max( dist / numberOfSteps, 0.000000001);
|
||||||
|
|
||||||
|
Double prevRealY = null;
|
||||||
|
Double prevX = null;
|
||||||
|
Double prevImagY = null;
|
||||||
|
|
||||||
|
double x = min;
|
||||||
|
while (x <= max) {
|
||||||
|
|
||||||
|
boolean needToCalculateRealY = realSeries.needToAdd(step, x);
|
||||||
|
|
||||||
|
if (needToCalculateRealY) {
|
||||||
|
final Complex c = calculatorExpression(expression, variable, x);
|
||||||
|
Double y = prepareY(c.realPart());
|
||||||
|
if (y != null) {
|
||||||
|
addSingularityPoint(realSeries, prevX, x, prevRealY, y);
|
||||||
|
realSeries.add(x, y);
|
||||||
|
prevRealY = y;
|
||||||
|
prevX = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean needToCalculateImagY = imagSeries.needToAdd(step, x);
|
||||||
|
if (needToCalculateImagY) {
|
||||||
|
y = prepareY(c.imaginaryPart());
|
||||||
|
if (y != null) {
|
||||||
|
addSingularityPoint(imagSeries, prevX, x, prevImagY, y);
|
||||||
|
imagSeries.add(x, y);
|
||||||
|
prevImagY = y;
|
||||||
|
prevX = x;
|
||||||
|
}
|
||||||
|
if (c.imaginaryPart() != 0d) {
|
||||||
|
imagExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boolean needToCalculateImagY = imagSeries.needToAdd(step, x);
|
||||||
|
if (needToCalculateImagY) {
|
||||||
|
final Complex c = calculatorExpression(expression, variable, x);
|
||||||
|
Double y = prepareY(c.imaginaryPart());
|
||||||
|
if (y != null) {
|
||||||
|
addSingularityPoint(imagSeries, prevX, x, prevImagY, y);
|
||||||
|
imagSeries.add(x, y);
|
||||||
|
prevImagY = y;
|
||||||
|
prevX = x;
|
||||||
|
}
|
||||||
|
if (c.imaginaryPart() != 0d) {
|
||||||
|
imagExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x += step;
|
||||||
|
}
|
||||||
|
|
||||||
|
return imagExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant variable, double x) {
|
||||||
|
return unwrap(expression.substitute(variable, Expression.valueOf(x)).numeric());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addSingularityPoint(@NotNull MyXYSeries series, @Nullable Double prevX, @NotNull Double x, @Nullable Double prevY, @NotNull Double y) {
|
||||||
|
if (prevX != null && prevY != null) {
|
||||||
|
// y or prevY should be more than 1d because if they are too small false singularity may occur (e.g., 1/0.000000000000000001)
|
||||||
|
if ( (Math.abs(y) >= 1d && Math.abs(prevY / y) > MAX_Y_DIFF) || (Math.abs(prevY) >= 1d && Math.abs(y / prevY) > MAX_Y_DIFF)) {
|
||||||
|
//Log.d(CalculatorPlotActivity.class.getName(), "Singularity! Prev point: (" + prevX + ", " + prevY + "), current point: (" +x+ ", " + y +")" );
|
||||||
|
//Log.d(CalculatorPlotActivity.class.getName(), String.valueOf(prevX + Math.abs(x - prevX) / 2) + ", null");
|
||||||
|
series.add(prevX + Math.abs(x - prevX) / 2, MathHelper.NULL_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Double prepareY(double y) {
|
||||||
|
if (Double.isNaN(y)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static Complex unwrap(@Nullable Generic numeric) {
|
||||||
|
if (numeric instanceof JsclInteger) {
|
||||||
|
return Complex.valueOf(((JsclInteger) numeric).intValue(), 0d);
|
||||||
|
} else if (numeric instanceof NumericWrapper) {
|
||||||
|
return unwrap(((NumericWrapper) numeric).content());
|
||||||
|
} else {
|
||||||
|
throw new ArithmeticException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static Complex unwrap(@Nullable Numeric content) {
|
||||||
|
if (content instanceof Real) {
|
||||||
|
return Complex.valueOf(((Real) content).doubleValue(), 0d);
|
||||||
|
} else if (content instanceof Complex) {
|
||||||
|
return ((Complex) content);
|
||||||
|
} else {
|
||||||
|
throw new ArithmeticException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
||||||
|
* For more information, please, contact se.solovyev@gmail.com
|
||||||
|
* or visit http://se.solovyev.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import jscl.math.Expression;
|
||||||
|
import jscl.math.function.Constant;
|
||||||
|
import junit.framework.Assert;
|
||||||
|
import org.achartengine.model.XYSeries;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 12/5/11
|
||||||
|
* Time: 9:07 PM
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PlotUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddXY() throws Exception {
|
||||||
|
MyXYSeries series = new MyXYSeries("test_01", 100);
|
||||||
|
PlotUtils.addXY(-10, 10, Expression.valueOf("asin(t)"), new Constant("t"), series, new MyXYSeries("test_01_imag"), false, 200);
|
||||||
|
testAscSeries(series);
|
||||||
|
PlotUtils.addXY(-1, 1, Expression.valueOf("asin(t)"), new Constant("t"), series, new MyXYSeries("test_01_imag"), true, 200);
|
||||||
|
testAscSeries(series);
|
||||||
|
|
||||||
|
series = new MyXYSeries("test_02", 1000);
|
||||||
|
PlotUtils.addXY(-10, 10, Expression.valueOf("1/t"), new Constant("t"), series, new MyXYSeries("test_01_imag"), false, 1000);
|
||||||
|
testAscSeries(series);
|
||||||
|
PlotUtils.addXY(-1, 1, Expression.valueOf("1/t"), new Constant("t"), series, new MyXYSeries("test_01_imag"), true, 1000);
|
||||||
|
testAscSeries(series);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAscSeries(@NotNull XYSeries series) {
|
||||||
|
for ( int i = 0; i < series.getItemCount(); i++ ) {
|
||||||
|
if (i > 1) {
|
||||||
|
Assert.assertTrue(series.getX(i - 1) + " > " +series.getX(i) + " at " + i + " of " + series.getItemCount(), series.getX(i - 1) <= series.getX(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user