new plotter
This commit is contained in:
parent
3dab118a1b
commit
5f7ee1e64e
@ -9,7 +9,9 @@ import org.solovyev.android.calculator.jscl.JsclOperation;
|
|||||||
import org.solovyev.android.calculator.plot.PlotInput;
|
import org.solovyev.android.calculator.plot.PlotInput;
|
||||||
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
|
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
|
||||||
import org.solovyev.android.menu.LabeledMenuItem;
|
import org.solovyev.android.menu.LabeledMenuItem;
|
||||||
import org.solovyev.common.collections.CollectionsUtils;
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: Solovyev_S
|
* User: Solovyev_S
|
||||||
@ -82,10 +84,17 @@ public enum CalculatorDisplayMenuItem implements LabeledMenuItem<CalculatorDispl
|
|||||||
final Generic generic = data.getResult();
|
final Generic generic = data.getResult();
|
||||||
assert generic != null;
|
assert generic != null;
|
||||||
|
|
||||||
final Constant constant = CollectionsUtils.getFirstCollectionElement(CalculatorUtils.getNotSystemConstants(generic));
|
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(generic));
|
||||||
assert constant != null;
|
final Constant xVariable = variables.get(0);
|
||||||
|
|
||||||
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.plot_graph, PlotInput.newInstance(generic, constant), context);
|
final Constant yVariable;
|
||||||
|
if ( variables.size() > 1 ) {
|
||||||
|
yVariable = variables.get(1);
|
||||||
|
} else {
|
||||||
|
yVariable = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.plot_graph, PlotInput.newInstance(generic, xVariable, yVariable), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3,6 +3,7 @@ package org.solovyev.android.calculator.plot;
|
|||||||
import jscl.math.Generic;
|
import jscl.math.Generic;
|
||||||
import jscl.math.function.Constant;
|
import jscl.math.function.Constant;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: serso
|
* User: serso
|
||||||
@ -15,19 +16,25 @@ public class PlotInput {
|
|||||||
private Generic function;
|
private Generic function;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private Constant constant;
|
private Constant xVariable;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Constant yVariable;
|
||||||
|
|
||||||
public PlotInput() {
|
public PlotInput() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlotInput(@NotNull Generic function, @NotNull Constant constant) {
|
|
||||||
this.function = function;
|
|
||||||
this.constant = constant;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static PlotInput newInstance(@NotNull Generic function, @NotNull Constant constant) {
|
public static PlotInput newInstance(@NotNull Generic function,
|
||||||
return new PlotInput(function, constant);
|
@NotNull Constant xVariable,
|
||||||
|
@Nullable Constant yVariable) {
|
||||||
|
PlotInput result = new PlotInput();
|
||||||
|
|
||||||
|
result.function = function;
|
||||||
|
result.xVariable = xVariable;
|
||||||
|
result.yVariable = yVariable;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -36,7 +43,12 @@ public class PlotInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Constant getConstant() {
|
public Constant getXVariable() {
|
||||||
return constant;
|
return xVariable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Constant getYVariable() {
|
||||||
|
return yVariable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
android-app/misc/lib/arity-2.1.6.jar
Executable file
BIN
android-app/misc/lib/arity-2.1.6.jar
Executable file
Binary file not shown.
@ -111,6 +111,14 @@
|
|||||||
<version>0.7.1</version>
|
<version>0.7.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>arity</groupId>
|
||||||
|
<artifactId>arity</artifactId>
|
||||||
|
<version>2.1.6</version>
|
||||||
|
<scope>system</scope>
|
||||||
|
<systemPath>${project.basedir}/misc/lib/arity-2.1.6.jar</systemPath>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>admob</groupId>
|
<groupId>admob</groupId>
|
||||||
<artifactId>admob</artifactId>
|
<artifactId>admob</artifactId>
|
||||||
|
496
android-app/src/main/java/arity/calculator/Calculator.java
Executable file
496
android-app/src/main/java/arity/calculator/Calculator.java
Executable file
@ -0,0 +1,496 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import org.javia.arity.*;
|
||||||
|
import org.javia.arity.Util;
|
||||||
|
import org.solovyev.android.calculator.Locator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class Calculator extends Activity implements TextWatcher,
|
||||||
|
View.OnKeyListener,
|
||||||
|
View.OnClickListener,
|
||||||
|
AdapterView.OnItemClickListener,
|
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
|
{
|
||||||
|
static final char MINUS = '\u2212', TIMES = '\u00d7', DIV = '\u00f7', SQRT = '\u221a', PI = '\u03c0',
|
||||||
|
UP_ARROW = '\u21e7', DN_ARROW = '\u21e9', ARROW = '\u21f3';
|
||||||
|
|
||||||
|
private static final int MSG_INPUT_CHANGED = 1;
|
||||||
|
private static final String INFINITY = "Infinity";
|
||||||
|
private static final String INFINITY_UNICODE = "\u221e";
|
||||||
|
|
||||||
|
static Symbols symbols = new Symbols();
|
||||||
|
static Function function;
|
||||||
|
|
||||||
|
private TextView result;
|
||||||
|
private EditText input;
|
||||||
|
private ListView historyView;
|
||||||
|
private Graph2dView graphView;
|
||||||
|
private Graph3dView graph3dView;
|
||||||
|
private History history;
|
||||||
|
private int nDigits = 0;
|
||||||
|
private boolean pendingClearResult;
|
||||||
|
private boolean isAlphaVisible;
|
||||||
|
static ArrayList<Function> graphedFunction;
|
||||||
|
static Defs defs;
|
||||||
|
private ArrayList<Function> auxFuncs = new ArrayList<Function>();
|
||||||
|
static boolean useHighQuality3d = true;
|
||||||
|
|
||||||
|
private static final char[][] ALPHA = {
|
||||||
|
{'q', 'w', '=', ',', ';', SQRT, '!', '\''},
|
||||||
|
{'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'},
|
||||||
|
{'a', 's', 'd', 'f', 'g', 'h', 'j', 'k'},
|
||||||
|
{'z', 'x', 'c', 'v', 'b', 'n', 'm', 'l'},
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final char[][] DIGITS = {
|
||||||
|
{'7', '8', '9', '%', '^', ARROW},
|
||||||
|
{'4', '5', '6','(', ')', 'C'},
|
||||||
|
{'1', '2', '3', TIMES, DIV, 'E'},
|
||||||
|
{'0', '0', '.', '+', MINUS, 'E'},
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final char[][] DIGITS2 = {
|
||||||
|
{'0', '.', '+', MINUS, TIMES, DIV, '^', '(', ')', 'C'},
|
||||||
|
{'1', '2', '3', '4', '5', '6', '7', '8', '9', 'E'},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
private static final char[][] DIGITS3 = {
|
||||||
|
{'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'},
|
||||||
|
{'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', PI},
|
||||||
|
{'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '=', '%'},
|
||||||
|
{'0', '.', '+', MINUS, TIMES, DIV, '^', '(', ')', 'C'},
|
||||||
|
{'1', '2', '3', '4', '5', '6', '7', '8', '9', 'E'},
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void onConfigurationChanged(Configuration config) {
|
||||||
|
super.onConfigurationChanged(config);
|
||||||
|
internalConfigChange(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void internalConfigChange(Configuration config) {
|
||||||
|
/*setContentView(R.layout.main);
|
||||||
|
graphView = (GraphView) findViewById(R.id.graph);
|
||||||
|
graph3dView = (Graph3dView) findViewById(R.id.graph3d);
|
||||||
|
historyView = (ListView) findViewById(R.id.history);
|
||||||
|
|
||||||
|
final boolean isLandscape = config.orientation == Configuration.ORIENTATION_LANDSCAPE;
|
||||||
|
// final boolean hasKeyboard = config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
|
||||||
|
|
||||||
|
alpha = (KeyboardView) findViewById(R.id.alpha);
|
||||||
|
digits = (KeyboardView) findViewById(R.id.digits);
|
||||||
|
if (isLandscape) {
|
||||||
|
digits.init(DIGITS2, false, true);
|
||||||
|
isAlphaVisible = false;
|
||||||
|
} else {
|
||||||
|
alpha.init(ALPHA, false, false);
|
||||||
|
digits.init(DIGITS, true, true);
|
||||||
|
updateAlpha();
|
||||||
|
}
|
||||||
|
|
||||||
|
result = (TextView) findViewById(R.id.result);
|
||||||
|
|
||||||
|
Editable oldText = input != null ? input.getText() : null;
|
||||||
|
input = (EditText) findViewById(R.id.input);
|
||||||
|
input.setOnKeyListener(this);
|
||||||
|
input.addTextChangedListener(this);
|
||||||
|
input.setEditableFactory(new CalculatorEditable.Factory());
|
||||||
|
input.setInputType(0);
|
||||||
|
changeInput(history.getText());
|
||||||
|
if (oldText != null) {
|
||||||
|
input.setText(oldText);
|
||||||
|
}
|
||||||
|
input.requestFocus();
|
||||||
|
graphView.setOnClickListener(this);
|
||||||
|
graph3dView.setOnClickListener(this);
|
||||||
|
if (historyView != null) {
|
||||||
|
historyView.setAdapter(adapter);
|
||||||
|
historyView.setOnItemClickListener(this);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
history = new History(this);
|
||||||
|
internalConfigChange(getResources().getConfiguration());
|
||||||
|
|
||||||
|
defs = new Defs(this, symbols);
|
||||||
|
if (history.fileNotFound) {
|
||||||
|
String[] init = {
|
||||||
|
"sqrt(pi)\u00f70.5!",
|
||||||
|
"e^(i\u00d7pi)",
|
||||||
|
"ln(e^100)",
|
||||||
|
"sin(x)",
|
||||||
|
"x^2"
|
||||||
|
};
|
||||||
|
nDigits = 10;
|
||||||
|
for (String s : init) {
|
||||||
|
onEnter(s);
|
||||||
|
}
|
||||||
|
nDigits = 0;
|
||||||
|
}
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
String value = prefs.getString("quality", null);
|
||||||
|
if (value == null) {
|
||||||
|
useHighQuality3d = Build.VERSION.SDK_INT >= 5;
|
||||||
|
prefs.edit().putString("quality", useHighQuality3d ? "high" : "low").commit();
|
||||||
|
} else {
|
||||||
|
useHighQuality3d = value.equals("high");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
graph3dView.onPause();
|
||||||
|
history.updateEdited(input.getText().toString());
|
||||||
|
history.save();
|
||||||
|
defs.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
graph3dView.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
//OnSharedPreferenceChangeListener
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
||||||
|
if (key.equals("quality")) {
|
||||||
|
useHighQuality3d = prefs.getString(key, "high").equals("high");
|
||||||
|
// Calculator.log("useHigh quality changed to " + useHighQuality3d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//OnClickListener
|
||||||
|
public void onClick(View target) {
|
||||||
|
if (target == graphView || target == graph3dView) {
|
||||||
|
startActivity(new Intent(this, ShowGraph.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnItemClickListener
|
||||||
|
public void onItemClick(AdapterView parent, View view, int pos, long id) {
|
||||||
|
history.moveToPos(pos, input.getText().toString());
|
||||||
|
changeInput(history.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextWatcher
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
// handler.removeMessages(MSG_INPUT_CHANGED);
|
||||||
|
// handler.sendEmptyMessageDelayed(MSG_INPUT_CHANGED, 250);
|
||||||
|
evaluate();
|
||||||
|
/*
|
||||||
|
if (pendingClearResult && s.length() != 0) {
|
||||||
|
if (!(s.length() == 4 && s.toString().startsWith("ans"))) {
|
||||||
|
result.setText(null);
|
||||||
|
}
|
||||||
|
showGraph(null);
|
||||||
|
pendingClearResult = false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// OnKeyListener
|
||||||
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||||
|
int action = event.getAction();
|
||||||
|
if (action == KeyEvent.ACTION_DOWN) {
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_ENTER:
|
||||||
|
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||||
|
doEnter();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KeyEvent.KEYCODE_DPAD_UP:
|
||||||
|
onUp();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||||
|
onDown();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_ENTER:
|
||||||
|
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||||
|
case KeyEvent.KEYCODE_DPAD_UP:
|
||||||
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private Handler handler = new Handler() {
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case MSG_INPUT_CHANGED:
|
||||||
|
// String text = input.getText().toString();
|
||||||
|
evaluate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void log(String mes) {
|
||||||
|
if (mes != null) {
|
||||||
|
Locator.getInstance().getLogger().debug(null, mes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void evaluate() {
|
||||||
|
evaluate(input.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatEval(Complex value) {
|
||||||
|
if (nDigits == 0) {
|
||||||
|
nDigits = getResultSpace();
|
||||||
|
}
|
||||||
|
String res = Util.complexToString(value, nDigits, 2);
|
||||||
|
return res.replace(INFINITY, INFINITY_UNICODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void evaluate(String text) {
|
||||||
|
// log("evaluate " + text);
|
||||||
|
if (text.length() == 0) {
|
||||||
|
result.setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auxFuncs.clear();
|
||||||
|
int end = -1;
|
||||||
|
do {
|
||||||
|
text = text.substring(end+1);
|
||||||
|
end = text.indexOf(';');
|
||||||
|
String slice = end == -1 ? text : text.substring(0, end);
|
||||||
|
try {
|
||||||
|
Function f = symbols.compile(slice);
|
||||||
|
auxFuncs.add(f);
|
||||||
|
} catch (SyntaxException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} while (end != -1);
|
||||||
|
|
||||||
|
graphedFunction = auxFuncs;
|
||||||
|
int size = auxFuncs.size();
|
||||||
|
if (size == 0) {
|
||||||
|
result.setEnabled(false);
|
||||||
|
return;
|
||||||
|
} else if (size == 1) {
|
||||||
|
Function f = auxFuncs.get(0);
|
||||||
|
int arity = f.arity();
|
||||||
|
// Calculator.log("res " + f);
|
||||||
|
if (arity == 1 || arity == 2) {
|
||||||
|
result.setText(null);
|
||||||
|
showGraph(f);
|
||||||
|
} else if (arity == 0) {
|
||||||
|
result.setText(formatEval(f.evalComplex()));
|
||||||
|
result.setEnabled(true);
|
||||||
|
showGraph(null);
|
||||||
|
} else {
|
||||||
|
result.setText("function");
|
||||||
|
result.setEnabled(true);
|
||||||
|
showGraph(null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
graphView.setFunctions(auxFuncs);
|
||||||
|
if (graphView.getVisibility() != View.VISIBLE) {
|
||||||
|
if (isAlphaVisible) {
|
||||||
|
isAlphaVisible = false;
|
||||||
|
updateAlpha();
|
||||||
|
}
|
||||||
|
result.setVisibility(View.GONE);
|
||||||
|
historyView.setVisibility(View.GONE);
|
||||||
|
graph3dView.setVisibility(View.GONE);
|
||||||
|
graph3dView.onPause();
|
||||||
|
graphView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getResultSpace() {
|
||||||
|
int width = result.getWidth() - result.getTotalPaddingLeft() - result.getTotalPaddingRight();
|
||||||
|
float oneDigitWidth = result.getPaint().measureText("5555555555") / 10f;
|
||||||
|
return (int) (width / oneDigitWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAlpha() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringBuilder oneChar = new StringBuilder(" ");
|
||||||
|
void onKey(char key) {
|
||||||
|
if (key == 'E') {
|
||||||
|
doEnter();
|
||||||
|
} else if (key == 'C') {
|
||||||
|
doBackspace();
|
||||||
|
} else if (key == ARROW) {
|
||||||
|
isAlphaVisible = !isAlphaVisible;
|
||||||
|
updateAlpha();
|
||||||
|
} else {
|
||||||
|
int cursor = input.getSelectionStart();
|
||||||
|
oneChar.setCharAt(0, key);
|
||||||
|
input.getText().insert(cursor, oneChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showGraph(Function f) {
|
||||||
|
if (f == null) {
|
||||||
|
if (historyView.getVisibility() != View.VISIBLE) {
|
||||||
|
graphView.setVisibility(View.GONE);
|
||||||
|
graph3dView.setVisibility(View.GONE);
|
||||||
|
graph3dView.onPause();
|
||||||
|
historyView.setVisibility(View.VISIBLE);
|
||||||
|
result.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// graphedFunction = f;
|
||||||
|
if (f.arity() == 1) {
|
||||||
|
graphView.setFunction(f);
|
||||||
|
if (graphView.getVisibility() != View.VISIBLE) {
|
||||||
|
if (isAlphaVisible) {
|
||||||
|
isAlphaVisible = false;
|
||||||
|
updateAlpha();
|
||||||
|
}
|
||||||
|
result.setVisibility(View.GONE);
|
||||||
|
historyView.setVisibility(View.GONE);
|
||||||
|
graph3dView.setVisibility(View.GONE);
|
||||||
|
graph3dView.onPause();
|
||||||
|
graphView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
graph3dView.setFunction(f);
|
||||||
|
if (graph3dView.getVisibility() != View.VISIBLE) {
|
||||||
|
if (isAlphaVisible) {
|
||||||
|
isAlphaVisible = false;
|
||||||
|
updateAlpha();
|
||||||
|
}
|
||||||
|
result.setVisibility(View.GONE);
|
||||||
|
historyView.setVisibility(View.GONE);
|
||||||
|
graphView.setVisibility(View.GONE);
|
||||||
|
graph3dView.setVisibility(View.VISIBLE);
|
||||||
|
graph3dView.onResume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onEnter() {
|
||||||
|
onEnter(input.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void onEnter(String text) {
|
||||||
|
boolean historyChanged = false;
|
||||||
|
try {
|
||||||
|
FunctionAndName fan = symbols.compileWithName(text);
|
||||||
|
if (fan.name != null) {
|
||||||
|
symbols.define(fan);
|
||||||
|
defs.add(text);
|
||||||
|
}
|
||||||
|
Function f = fan.function;
|
||||||
|
int arity = f.arity();
|
||||||
|
Complex value = null;
|
||||||
|
if (arity == 0) {
|
||||||
|
value = f.evalComplex();
|
||||||
|
symbols.define("ans", value);
|
||||||
|
}
|
||||||
|
historyChanged = arity == 0 ?
|
||||||
|
history.onEnter(text, formatEval(value)) :
|
||||||
|
history.onEnter(text, null);
|
||||||
|
} catch (SyntaxException e) {
|
||||||
|
historyChanged = history.onEnter(text, null);
|
||||||
|
}
|
||||||
|
showGraph(null);
|
||||||
|
|
||||||
|
if (text.length() == 0) {
|
||||||
|
result.setText(null);
|
||||||
|
}
|
||||||
|
changeInput(history.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeInput(String newInput) {
|
||||||
|
input.setText(newInput);
|
||||||
|
input.setSelection(newInput.length());
|
||||||
|
/*
|
||||||
|
if (newInput.length() > 0) {
|
||||||
|
result.setText(null);
|
||||||
|
} else {
|
||||||
|
pendingClearResult = true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
if (result.getText().equals("function")) {
|
||||||
|
result.setText(null);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private void updateChecked() {
|
||||||
|
int pos = history.getListPos();
|
||||||
|
if (pos >= 0) {
|
||||||
|
log("check " + pos);
|
||||||
|
historyView.setItemChecked(pos, true);
|
||||||
|
adapter.notifyDataSetInvalidated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
void onUp() {
|
||||||
|
if (history.moveUp(input.getText().toString())) {
|
||||||
|
changeInput(history.getText());
|
||||||
|
// updateChecked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDown() {
|
||||||
|
if (history.moveDown(input.getText().toString())) {
|
||||||
|
changeInput(history.getText());
|
||||||
|
// updateChecked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final KeyEvent
|
||||||
|
KEY_DEL = new KeyEvent(0, KeyEvent.KEYCODE_DEL),
|
||||||
|
KEY_ENTER = new KeyEvent(0, KeyEvent.KEYCODE_ENTER);
|
||||||
|
|
||||||
|
void doEnter() {
|
||||||
|
onEnter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void doBackspace() {
|
||||||
|
input.dispatchKeyEvent(KEY_DEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
85
android-app/src/main/java/arity/calculator/CalculatorEditable.java
Executable file
85
android-app/src/main/java/arity/calculator/CalculatorEditable.java
Executable file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
|
||||||
|
class CalculatorEditable extends SpannableStringBuilder {
|
||||||
|
static class Factory extends Editable.Factory {
|
||||||
|
public Editable newEditable(CharSequence source) {
|
||||||
|
return new CalculatorEditable(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final char MINUS = '\u2212', TIMES = '\u00d7', DIV = '\u00f7';
|
||||||
|
private boolean isRec;
|
||||||
|
|
||||||
|
public CalculatorEditable(CharSequence source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpannableStringBuilder replace(int start, int end, CharSequence buf, int bufStart, int bufEnd) {
|
||||||
|
if (isRec || bufEnd - bufStart != 1) {
|
||||||
|
return super.replace(start, end, buf, bufStart, bufEnd);
|
||||||
|
} else {
|
||||||
|
isRec = true;
|
||||||
|
try {
|
||||||
|
char c = buf.charAt(bufStart);
|
||||||
|
return internalReplace(start, end, c);
|
||||||
|
} finally {
|
||||||
|
isRec = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOperator(char c) {
|
||||||
|
return "\u2212\u00d7\u00f7+-/*=^,".indexOf(c) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SpannableStringBuilder internalReplace(int start, int end, char c) {
|
||||||
|
switch (c) {
|
||||||
|
case '-': c = MINUS; break;
|
||||||
|
case '*': c = TIMES; break;
|
||||||
|
case '/': c = DIV; break;
|
||||||
|
}
|
||||||
|
if (c == '.') {
|
||||||
|
int p = start - 1;
|
||||||
|
while (p >= 0 && Character.isDigit(charAt(p))) {
|
||||||
|
--p;
|
||||||
|
}
|
||||||
|
if (p >= 0 && charAt(p) == '.') {
|
||||||
|
return super.replace(start, end, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char prevChar = start > 0 ? charAt(start-1) : '\0';
|
||||||
|
|
||||||
|
if (c == MINUS && prevChar == MINUS) {
|
||||||
|
return super.replace(start, end, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOperator(c)) {
|
||||||
|
while (isOperator(prevChar) &&
|
||||||
|
(c != MINUS || prevChar == '+')) {
|
||||||
|
--start;
|
||||||
|
prevChar = start > 0 ? charAt(start-1) : '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//don't allow leading operator + * /
|
||||||
|
if (start == 0 && isOperator(c)) { // && c != MINUS
|
||||||
|
return super.replace(start, end, "ans" + c);
|
||||||
|
}
|
||||||
|
|
||||||
|
//allow at most one '='
|
||||||
|
if (c == '=') {
|
||||||
|
for (int pos = 0; pos < start; ++pos) {
|
||||||
|
if (charAt(pos) == '=') {
|
||||||
|
return super.replace(start, end, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.replace(start, end, "" + c);
|
||||||
|
}
|
||||||
|
}
|
145
android-app/src/main/java/arity/calculator/Data.java
Executable file
145
android-app/src/main/java/arity/calculator/Data.java
Executable file
@ -0,0 +1,145 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
class Data {
|
||||||
|
float[] xs = new float[4];
|
||||||
|
float[] ys = new float[4];
|
||||||
|
int size = 0;
|
||||||
|
int allocSize = 4;
|
||||||
|
|
||||||
|
void swap(Data o) {
|
||||||
|
float savex[] = o.xs;
|
||||||
|
float savey[] = o.ys;
|
||||||
|
int ssize = o.size;
|
||||||
|
int salloc = o.allocSize;
|
||||||
|
|
||||||
|
o.xs = xs;
|
||||||
|
o.ys = ys;
|
||||||
|
o.size = size;
|
||||||
|
o.allocSize = allocSize;
|
||||||
|
|
||||||
|
xs = savex;
|
||||||
|
ys = savey;
|
||||||
|
size = ssize;
|
||||||
|
allocSize = salloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(float x, float y) {
|
||||||
|
if (size >= allocSize) {
|
||||||
|
makeSpace(size+1);
|
||||||
|
}
|
||||||
|
// Calculator.log("push " + size + ' ' + x + ' ' + y);
|
||||||
|
xs[size] = x;
|
||||||
|
ys[size] = y;
|
||||||
|
++size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeSpace(int sizeNeeded) {
|
||||||
|
int oldAllocSize = allocSize;
|
||||||
|
while (sizeNeeded > allocSize) {
|
||||||
|
allocSize += allocSize;
|
||||||
|
}
|
||||||
|
if (oldAllocSize != allocSize) {
|
||||||
|
float[] a = new float[allocSize];
|
||||||
|
System.arraycopy(xs, 0, a, 0, size);
|
||||||
|
xs = a;
|
||||||
|
a = new float[allocSize];
|
||||||
|
System.arraycopy(ys, 0, a, 0, size);
|
||||||
|
ys = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float topX() {
|
||||||
|
return xs[size-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
float topY() {
|
||||||
|
return ys[size-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
float firstX() {
|
||||||
|
return xs[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
float firstY() {
|
||||||
|
return ys[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop() {
|
||||||
|
--size;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean empty() {
|
||||||
|
return size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void eraseBefore(float x) {
|
||||||
|
int pos = 0;
|
||||||
|
while (pos < size && xs[pos] < x) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
--pos;
|
||||||
|
if (pos > 0) {
|
||||||
|
size -= pos;
|
||||||
|
System.arraycopy(xs, pos, xs, 0, size);
|
||||||
|
System.arraycopy(ys, pos, ys, 0, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void eraseAfter(float x) {
|
||||||
|
int pos = size-1;
|
||||||
|
while (pos >= 0 && x < xs[pos]) {
|
||||||
|
--pos;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
if (pos < size-1) {
|
||||||
|
size = pos+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int findPosAfter(float x, float y) {
|
||||||
|
int pos = 0;
|
||||||
|
while (pos < size && xs[pos] <= x) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (Float.isNaN(y)) {
|
||||||
|
while (pos < size && ys[pos] != ys[pos]) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Calculator.log("pos " + pos);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(Data d) {
|
||||||
|
makeSpace(size + d.size);
|
||||||
|
int pos = d.findPosAfter(xs[size-1], ys[size-1]);
|
||||||
|
/*
|
||||||
|
while (pos < d.size && d.xs[pos] <= last) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
if (last != last) {
|
||||||
|
while (pos < d.size && d.ys[pos] != d.ys[pos]) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
System.arraycopy(d.xs, pos, xs, size, d.size-pos);
|
||||||
|
System.arraycopy(d.ys, pos, ys, size, d.size-pos);
|
||||||
|
size += d.size-pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append(size).append(": ");
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
b.append(xs[i]).append(", ");
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
}
|
62
android-app/src/main/java/arity/calculator/Defs.java
Executable file
62
android-app/src/main/java/arity/calculator/Defs.java
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import org.javia.arity.Symbols;
|
||||||
|
import org.javia.arity.SyntaxException;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
class Defs extends FileHandler {
|
||||||
|
private static final int SIZE_LIMIT = 50;
|
||||||
|
ArrayList<String> lines = new ArrayList<String>();
|
||||||
|
private Symbols symbols;
|
||||||
|
|
||||||
|
Defs(Context context, Symbols symbols) {
|
||||||
|
super(context, "defs", 1);
|
||||||
|
this.symbols = symbols;
|
||||||
|
symbols.pushFrame();
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
lines.clear();
|
||||||
|
symbols.popFrame();
|
||||||
|
symbols.pushFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() {
|
||||||
|
return lines.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void doRead(DataInputStream is) throws IOException {
|
||||||
|
int size = is.readInt();
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
String line = is.readUTF();
|
||||||
|
lines.add(line);
|
||||||
|
try {
|
||||||
|
symbols.define(symbols.compileWithName(line));
|
||||||
|
} catch (SyntaxException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void doWrite(DataOutputStream os) throws IOException {
|
||||||
|
os.writeInt(lines.size());
|
||||||
|
for (String s : lines) {
|
||||||
|
os.writeUTF(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(String text) {
|
||||||
|
if (lines.size() >= SIZE_LIMIT) {
|
||||||
|
lines.remove(0);
|
||||||
|
}
|
||||||
|
lines.add(text);
|
||||||
|
}
|
||||||
|
}
|
22
android-app/src/main/java/arity/calculator/FPS.java
Executable file
22
android-app/src/main/java/arity/calculator/FPS.java
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
class FPS {
|
||||||
|
private int drawCnt;
|
||||||
|
private long lastTime;
|
||||||
|
private int fps;
|
||||||
|
|
||||||
|
boolean incFrame() {
|
||||||
|
if (--drawCnt > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
drawCnt = 100;
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
fps = Math.round(100000f / (now - lastTime));
|
||||||
|
lastTime = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getValue() {
|
||||||
|
return fps;
|
||||||
|
}
|
||||||
|
}
|
62
android-app/src/main/java/arity/calculator/FileHandler.java
Executable file
62
android-app/src/main/java/arity/calculator/FileHandler.java
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
abstract class FileHandler {
|
||||||
|
private String fileName;
|
||||||
|
private Context context;
|
||||||
|
private int version;
|
||||||
|
boolean fileNotFound;
|
||||||
|
|
||||||
|
private DataInputStream openInput() throws IOException {
|
||||||
|
try {
|
||||||
|
return new DataInputStream(new BufferedInputStream(context.openFileInput(fileName), 256));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
fileNotFound = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataOutputStream openOutput() throws IOException {
|
||||||
|
return new DataOutputStream(new BufferedOutputStream(context.openFileOutput(fileName, 0), 256));
|
||||||
|
}
|
||||||
|
|
||||||
|
FileHandler(Context context, String fileName, int version) {
|
||||||
|
this.context = context;
|
||||||
|
this.fileName = fileName;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load() {
|
||||||
|
try {
|
||||||
|
DataInputStream is = openInput();
|
||||||
|
if (is != null) {
|
||||||
|
int readVersion = is.readInt();
|
||||||
|
if (readVersion != version) {
|
||||||
|
throw new IllegalStateException("invalid version " + readVersion);
|
||||||
|
}
|
||||||
|
doRead(is);
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("" + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void save() {
|
||||||
|
try {
|
||||||
|
DataOutputStream os = openOutput();
|
||||||
|
os.writeInt(version);
|
||||||
|
doWrite(os);
|
||||||
|
os.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("" + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void doRead(DataInputStream is) throws IOException;
|
||||||
|
abstract void doWrite(DataOutputStream os) throws IOException;
|
||||||
|
}
|
183
android-app/src/main/java/arity/calculator/GLView.java
Executable file
183
android-app/src/main/java/arity/calculator/GLView.java
Executable file
@ -0,0 +1,183 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
|
||||||
|
import javax.microedition.khronos.egl.*;
|
||||||
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
import javax.microedition.khronos.opengles.GL11;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
|
||||||
|
private boolean hasSurface;
|
||||||
|
private boolean paused;
|
||||||
|
private EGL10 egl;
|
||||||
|
private EGLDisplay display;
|
||||||
|
private EGLConfig config;
|
||||||
|
private EGLSurface surface;
|
||||||
|
private EGLContext eglContext;
|
||||||
|
private GL11 gl;
|
||||||
|
protected int width, height;
|
||||||
|
private boolean mIsLooping;
|
||||||
|
|
||||||
|
abstract void onDrawFrame(GL10 gl);
|
||||||
|
abstract void onSurfaceCreated(GL10 gl, int width, int height);
|
||||||
|
|
||||||
|
public String captureScreenshot() {
|
||||||
|
Bitmap bitmap = getRawPixels(gl, width, height);
|
||||||
|
Util.bitmapBGRtoRGB(bitmap, width, height);
|
||||||
|
return Util.saveBitmap(bitmap, GraphView.SCREENSHOT_DIR, "calculator");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap getRawPixels(GL10 gl, int width, int height) {
|
||||||
|
int size = width * height;
|
||||||
|
ByteBuffer buf = ByteBuffer.allocateDirect(size * 4);
|
||||||
|
buf.order(ByteOrder.nativeOrder());
|
||||||
|
gl.glReadPixels(0, 0, width, height, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, buf);
|
||||||
|
int data[] = new int[size];
|
||||||
|
buf.asIntBuffer().get(data);
|
||||||
|
buf = null;
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
|
||||||
|
bitmap.setPixels(data, size-width, -width, 0, 0, width, height);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Handler handler = new Handler() {
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
glDraw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public GLView(Context context) {
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GLView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
SurfaceHolder holder = getHolder();
|
||||||
|
holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
|
||||||
|
holder.addCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onResume() {
|
||||||
|
Calculator.log("onResume " + this);
|
||||||
|
paused = false;
|
||||||
|
if (hasSurface) {
|
||||||
|
initGL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPause() {
|
||||||
|
Calculator.log("onPause " + this);
|
||||||
|
deinitGL();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initGL() {
|
||||||
|
egl = (EGL10) EGLContext.getEGL();
|
||||||
|
display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
|
||||||
|
int[] ver = new int[2];
|
||||||
|
egl.eglInitialize(display, ver);
|
||||||
|
|
||||||
|
int[] configSpec = {EGL10.EGL_NONE};
|
||||||
|
EGLConfig[] configOut = new EGLConfig[1];
|
||||||
|
int[] nConfig = new int[1];
|
||||||
|
egl.eglChooseConfig(display, configSpec, configOut, 1, nConfig);
|
||||||
|
config = configOut[0];
|
||||||
|
eglContext = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, null);
|
||||||
|
surface = egl.eglCreateWindowSurface(display, config, getHolder(), null);
|
||||||
|
egl.eglMakeCurrent(display, surface, surface, eglContext);
|
||||||
|
gl = (GL11) eglContext.getGL();
|
||||||
|
onSurfaceCreated(gl, width, height);
|
||||||
|
requestDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deinitGL() {
|
||||||
|
paused = true;
|
||||||
|
if (display != null) {
|
||||||
|
egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
|
||||||
|
egl.eglDestroySurface(display, surface);
|
||||||
|
egl.eglDestroyContext(display, eglContext);
|
||||||
|
egl.eglTerminate(display);
|
||||||
|
|
||||||
|
egl = null;
|
||||||
|
config = null;
|
||||||
|
eglContext = null;
|
||||||
|
surface = null;
|
||||||
|
display = null;
|
||||||
|
gl = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void glDraw() {
|
||||||
|
if (hasSurface && !paused) {
|
||||||
|
onDrawFrame(gl);
|
||||||
|
if (!egl.eglSwapBuffers(display, surface)) {
|
||||||
|
Calculator.log("swapBuffers error " + egl.eglGetError());
|
||||||
|
}
|
||||||
|
if (egl.eglGetError() == EGL11.EGL_CONTEXT_LOST) {
|
||||||
|
Calculator.log("egl context lost " + this);
|
||||||
|
paused = true;
|
||||||
|
}
|
||||||
|
if (mIsLooping) {
|
||||||
|
requestDraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
|
Calculator.log("surfaceCreated " + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||||
|
Calculator.log("surfaceChanged " + format + ' ' + this);
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
boolean doInit = !hasSurface && !paused;
|
||||||
|
hasSurface = true;
|
||||||
|
if (doInit) {
|
||||||
|
initGL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
|
Calculator.log("surfaceDestroyed " + this);
|
||||||
|
hasSurface = false;
|
||||||
|
deinitGL();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startLooping() {
|
||||||
|
if (!mIsLooping) {
|
||||||
|
Calculator.log("start looping");
|
||||||
|
mIsLooping = true;
|
||||||
|
glDraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopLooping() {
|
||||||
|
if (mIsLooping) {
|
||||||
|
Calculator.log("stop looping");
|
||||||
|
mIsLooping = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLooping() {
|
||||||
|
return mIsLooping;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestDraw() {
|
||||||
|
handler.sendEmptyMessage(1);
|
||||||
|
}
|
||||||
|
}
|
566
android-app/src/main/java/arity/calculator/Graph2dView.java
Executable file
566
android-app/src/main/java/arity/calculator/Graph2dView.java
Executable file
@ -0,0 +1,566 @@
|
|||||||
|
// Copyright (C) 2009-2010 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.*;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Scroller;
|
||||||
|
import android.widget.ZoomButtonsController;
|
||||||
|
import org.javia.arity.Function;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Graph2dView extends View implements
|
||||||
|
GraphView,
|
||||||
|
ZoomButtonsController.OnZoomListener,
|
||||||
|
TouchHandler.TouchHandlerInterface {
|
||||||
|
|
||||||
|
private int width, height;
|
||||||
|
private Matrix matrix = new Matrix();
|
||||||
|
private Paint paint = new Paint(), textPaint = new Paint(), fillPaint = new Paint();
|
||||||
|
private ArrayList<Function> funcs = new ArrayList<Function>();
|
||||||
|
private Data next = new Data(), endGraph = new Data();
|
||||||
|
private Data graphs[] = {new Data(), new Data(), new Data(), new Data(), new Data()};
|
||||||
|
private static final int GRAPHS_SIZE = 5;
|
||||||
|
private float gwidth = 8;
|
||||||
|
private float currentX, currentY;
|
||||||
|
private float lastMinX;
|
||||||
|
private Scroller scroller;
|
||||||
|
private float boundMinY, boundMaxY;
|
||||||
|
protected ZoomButtonsController zoomController = new ZoomButtonsController(this);
|
||||||
|
private ZoomTracker zoomTracker = new ZoomTracker();
|
||||||
|
private TouchHandler touchHandler;
|
||||||
|
private float lastTouchX, lastTouchY;
|
||||||
|
|
||||||
|
private static final int
|
||||||
|
COL_AXIS = 0xff00a000,
|
||||||
|
COL_GRID = 0xff004000,
|
||||||
|
COL_TEXT = 0xff00ff00;
|
||||||
|
|
||||||
|
private static final int COL_GRAPH[] = {0xffffffff, 0xff00ffff, 0xffffff00, 0xffff00ff, 0xff80ff80};
|
||||||
|
|
||||||
|
private static final int
|
||||||
|
COL_ZOOM = 0x40ffffff,
|
||||||
|
COL_ZOOM_TEXT1 = 0xd0ffffff,
|
||||||
|
COL_ZOOM_TEXT2 = 0x30ffffff;
|
||||||
|
|
||||||
|
public Graph2dView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Graph2dView(Context context) {
|
||||||
|
super(context);
|
||||||
|
touchHandler = new TouchHandler(this);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Context context) {
|
||||||
|
zoomController.setOnZoomListener(this);
|
||||||
|
scroller = new Scroller(context);
|
||||||
|
paint.setAntiAlias(false);
|
||||||
|
textPaint.setAntiAlias(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String captureScreenshot() {
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
onDraw(canvas);
|
||||||
|
return Util.saveBitmap(bitmap, GraphView.SCREENSHOT_DIR, "calculator");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearAllGraph() {
|
||||||
|
for (int i = 0; i < GRAPHS_SIZE; ++i) {
|
||||||
|
graphs[i].clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFunctions(List<Function> fs) {
|
||||||
|
funcs.clear();
|
||||||
|
for (Function f : fs) {
|
||||||
|
int arity = f.arity();
|
||||||
|
if (arity == 0 || arity == 1) {
|
||||||
|
funcs.add(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearAllGraph();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFunction(Function f) {
|
||||||
|
funcs.clear();
|
||||||
|
if (f != null) {
|
||||||
|
funcs.add(f);
|
||||||
|
}
|
||||||
|
clearAllGraph();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onVisibilityChanged(boolean visible) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onZoom(boolean zoomIn) {
|
||||||
|
if (zoomIn) {
|
||||||
|
if (canZoomIn()) {
|
||||||
|
gwidth /= 2;
|
||||||
|
invalidateGraphs();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (canZoomOut()) {
|
||||||
|
gwidth *= 2;
|
||||||
|
invalidateGraphs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zoomController.setZoomInEnabled(canZoomIn());
|
||||||
|
zoomController.setZoomOutEnabled(canZoomOut());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onResume() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPause() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDetachedFromWindow() {
|
||||||
|
zoomController.setVisible(false);
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onSizeChanged(int w, int h, int ow, int oh) {
|
||||||
|
width = w;
|
||||||
|
height = h;
|
||||||
|
clearAllGraph();
|
||||||
|
// points = new float[w+w];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
if (funcs.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (scroller.computeScrollOffset()) {
|
||||||
|
final float scale = gwidth / width;
|
||||||
|
currentX = scroller.getCurrX() * scale;
|
||||||
|
currentY = scroller.getCurrY() * scale;
|
||||||
|
if (!scroller.isFinished()) {
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawGraph(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float eval(Function f, float x) {
|
||||||
|
float v = (float) f.eval(x);
|
||||||
|
// Calculator.log("eval " + x + "; " + v);
|
||||||
|
if (v < -10000f) {
|
||||||
|
return -10000f;
|
||||||
|
}
|
||||||
|
if (v > 10000f) {
|
||||||
|
return 10000f;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// distance from (x,y) to the line (x1,y1) to (x2,y2), squared, multiplied by 4
|
||||||
|
/*
|
||||||
|
private float distance(float x1, float y1, float x2, float y2, float x, float y) {
|
||||||
|
float dx = x2 - x1;
|
||||||
|
float dy = y2 - y1;
|
||||||
|
float mx = x - x1;
|
||||||
|
float my = y - y1;
|
||||||
|
float up = dx*my - dy*mx;
|
||||||
|
return up*up*4/(dx*dx + dy*dy);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// distance as above when x==(x1+x2)/2.
|
||||||
|
private float distance2(float x1, float y1, float x2, float y2, float y) {
|
||||||
|
final float dx = x2 - x1;
|
||||||
|
final float dy = y2 - y1;
|
||||||
|
final float up = dx * (y1 + y2 - y - y);
|
||||||
|
return up * up / (dx * dx + dy * dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void computeGraph(Function f, float minX, float maxX, float minY, float maxY, Data graph) {
|
||||||
|
if (f.arity() == 0) {
|
||||||
|
float v = (float) f.eval();
|
||||||
|
if (v < -10000f) {
|
||||||
|
v = -10000f;
|
||||||
|
}
|
||||||
|
if (v > 10000f) {
|
||||||
|
v = 10000f;
|
||||||
|
}
|
||||||
|
graph.clear();
|
||||||
|
graph.push(minX, v);
|
||||||
|
graph.push(maxX, v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final float scale = width / gwidth;
|
||||||
|
final float maxStep = 15.8976f / scale;
|
||||||
|
final float minStep = .05f / scale;
|
||||||
|
float ythresh = 1 / scale;
|
||||||
|
ythresh = ythresh * ythresh;
|
||||||
|
// next.clear();
|
||||||
|
// endGraph.clear();
|
||||||
|
if (!graph.empty()) {
|
||||||
|
// Calculator.log("last " + lastMinX + " min " + minX);
|
||||||
|
if (minX >= lastMinX) {
|
||||||
|
graph.eraseBefore(minX);
|
||||||
|
} else {
|
||||||
|
graph.eraseAfter(maxX);
|
||||||
|
maxX = Math.min(maxX, graph.firstX());
|
||||||
|
graph.swap(endGraph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (graph.empty()) {
|
||||||
|
graph.push(minX, eval(f, minX));
|
||||||
|
}
|
||||||
|
float leftX, leftY;
|
||||||
|
float rightX = graph.topX(), rightY = graph.topY();
|
||||||
|
int nEval = 1;
|
||||||
|
while (true) {
|
||||||
|
leftX = rightX;
|
||||||
|
leftY = rightY;
|
||||||
|
if (leftX > maxX) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next.empty()) {
|
||||||
|
float x = leftX + maxStep;
|
||||||
|
next.push(x, eval(f, x));
|
||||||
|
++nEval;
|
||||||
|
}
|
||||||
|
rightX = next.topX();
|
||||||
|
rightY = next.topY();
|
||||||
|
next.pop();
|
||||||
|
|
||||||
|
if (leftY != leftY && rightY != rightY) { // NaN
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dx = rightX - leftX;
|
||||||
|
float middleX = (leftX + rightX) / 2;
|
||||||
|
float middleY = eval(f, middleX);
|
||||||
|
++nEval;
|
||||||
|
boolean middleIsOutside = (middleY < leftY && middleY < rightY) || (leftY < middleY && rightY < middleY);
|
||||||
|
if (dx < minStep) {
|
||||||
|
// Calculator.log("minStep");
|
||||||
|
if (middleIsOutside) {
|
||||||
|
graph.push(rightX, Float.NaN);
|
||||||
|
}
|
||||||
|
graph.push(rightX, rightY);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (middleIsOutside && ((leftY < minY && rightY > maxY) || (leftY > maxY && rightY < minY))) {
|
||||||
|
graph.push(rightX, Float.NaN);
|
||||||
|
graph.push(rightX, rightY);
|
||||||
|
// Calculator.log("+-inf");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!middleIsOutside) {
|
||||||
|
/*
|
||||||
|
float diff = leftY + rightY - middleY - middleY;
|
||||||
|
float dy = rightY - leftY;
|
||||||
|
float dx2 = dx*dx;
|
||||||
|
float distance = dx2*diff*diff/(dx2+dy*dy);
|
||||||
|
*/
|
||||||
|
// Calculator.log("" + dx + ' ' + leftY + ' ' + middleY + ' ' + rightY + ' ' + distance + ' ' + ythresh);
|
||||||
|
if (distance2(leftX, leftY, rightX, rightY, middleY) < ythresh) {
|
||||||
|
graph.push(rightX, rightY);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next.push(rightX, rightY);
|
||||||
|
next.push(middleX, middleY);
|
||||||
|
rightX = leftX;
|
||||||
|
rightY = leftY;
|
||||||
|
}
|
||||||
|
if (!endGraph.empty()) {
|
||||||
|
graph.append(endGraph);
|
||||||
|
}
|
||||||
|
long t2 = System.currentTimeMillis();
|
||||||
|
// Calculator.log("graph points " + graph.size + " evals " + nEval + " time " + (t2-t1));
|
||||||
|
|
||||||
|
next.clear();
|
||||||
|
endGraph.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path path = new Path();
|
||||||
|
|
||||||
|
private Path graphToPath(Data graph) {
|
||||||
|
boolean first = true;
|
||||||
|
int size = graph.size;
|
||||||
|
float[] xs = graph.xs;
|
||||||
|
float[] ys = graph.ys;
|
||||||
|
path.rewind();
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
float y = ys[i];
|
||||||
|
float x = xs[i];
|
||||||
|
// Calculator.log("path " + x + ' ' + y);
|
||||||
|
if (y == y) { // !NaN
|
||||||
|
if (first) {
|
||||||
|
path.moveTo(x, y);
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
path.lineTo(x, y);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
first = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final float NTICKS = 15;
|
||||||
|
|
||||||
|
private static float stepFactor(float w) {
|
||||||
|
float f = 1;
|
||||||
|
while (w / f > NTICKS) {
|
||||||
|
f *= 10;
|
||||||
|
}
|
||||||
|
while (w / f < NTICKS / 10) {
|
||||||
|
f /= 10;
|
||||||
|
}
|
||||||
|
float r = w / f;
|
||||||
|
if (r < NTICKS / 5) {
|
||||||
|
return f / 5;
|
||||||
|
} else if (r < NTICKS / 2) {
|
||||||
|
return f / 2;
|
||||||
|
} else {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StringBuilder b = new StringBuilder();
|
||||||
|
private static char[] buf = new char[20];
|
||||||
|
|
||||||
|
private static StringBuilder format(float fv) {
|
||||||
|
int pos = 0;
|
||||||
|
boolean addDot = false;
|
||||||
|
int v = Math.round(fv * 100);
|
||||||
|
boolean isNeg = v < 0;
|
||||||
|
v = isNeg ? -v : v;
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
int digit = v % 10;
|
||||||
|
v /= 10;
|
||||||
|
if (digit != 0 || addDot) {
|
||||||
|
buf[pos++] = (char) ('0' + digit);
|
||||||
|
addDot = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (addDot) {
|
||||||
|
buf[pos++] = '.';
|
||||||
|
}
|
||||||
|
if (v == 0) {
|
||||||
|
buf[pos++] = '0';
|
||||||
|
}
|
||||||
|
while (v != 0) {
|
||||||
|
buf[pos++] = (char) ('0' + (v % 10));
|
||||||
|
v /= 10;
|
||||||
|
}
|
||||||
|
if (isNeg) {
|
||||||
|
buf[pos++] = '-';
|
||||||
|
}
|
||||||
|
b.setLength(0);
|
||||||
|
b.append(buf, 0, pos);
|
||||||
|
b.reverse();
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawGraph(Canvas canvas) {
|
||||||
|
long t1 = System.currentTimeMillis();
|
||||||
|
float minX = getXMin();
|
||||||
|
float maxX = getXMax(minX);
|
||||||
|
float ywidth = gwidth * height / width;
|
||||||
|
float minY = currentY - ywidth / 2;
|
||||||
|
float maxY = minY + ywidth;
|
||||||
|
if (minY < boundMinY || maxY > boundMaxY) {
|
||||||
|
float halfw = ywidth / 2;
|
||||||
|
boundMinY = minY - halfw;
|
||||||
|
boundMaxY = maxY + halfw;
|
||||||
|
clearAllGraph();
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawColor(0xff000000);
|
||||||
|
|
||||||
|
paint.setStrokeWidth(0);
|
||||||
|
paint.setAntiAlias(false);
|
||||||
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
|
||||||
|
final float h2 = height / 2f;
|
||||||
|
final float scale = width / gwidth;
|
||||||
|
|
||||||
|
float x0 = -minX * scale;
|
||||||
|
boolean drawYAxis = true;
|
||||||
|
if (x0 < 25) {
|
||||||
|
x0 = 25;
|
||||||
|
// drawYAxis = false;
|
||||||
|
} else if (x0 > width - 3) {
|
||||||
|
x0 = width - 3;
|
||||||
|
// drawYAxis = false;
|
||||||
|
}
|
||||||
|
float y0 = maxY * scale;
|
||||||
|
if (y0 < 3) {
|
||||||
|
y0 = 3;
|
||||||
|
} else if (y0 > height - 15) {
|
||||||
|
y0 = height - 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
final float tickSize = 3;
|
||||||
|
final float y2 = y0 + tickSize;
|
||||||
|
paint.setColor(COL_GRID);
|
||||||
|
float step = stepFactor(gwidth);
|
||||||
|
// Calculator.log("width " + gwidth + " step " + step);
|
||||||
|
float v = ((int) (minX / step)) * step;
|
||||||
|
textPaint.setColor(COL_TEXT);
|
||||||
|
textPaint.setTextSize(12);
|
||||||
|
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
float stepScale = step * scale;
|
||||||
|
for (float x = (v - minX) * scale; x <= width; x += stepScale, v += step) {
|
||||||
|
canvas.drawLine(x, 0, x, height, paint);
|
||||||
|
if (!(-.001f < v && v < .001f)) {
|
||||||
|
StringBuilder b = format(v);
|
||||||
|
canvas.drawText(b, 0, b.length(), x, y2 + 10, textPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final float x1 = x0 - tickSize;
|
||||||
|
v = ((int) (minY / step)) * step;
|
||||||
|
textPaint.setTextAlign(Paint.Align.RIGHT);
|
||||||
|
for (float y = height - (v - minY) * scale; y >= 0; y -= stepScale, v += step) {
|
||||||
|
canvas.drawLine(0, y, width, y, paint);
|
||||||
|
if (!(-.001f < v && v < .001f)) {
|
||||||
|
StringBuilder b = format(v);
|
||||||
|
canvas.drawText(b, 0, b.length(), x1, y + 4, textPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paint.setColor(COL_AXIS);
|
||||||
|
if (drawYAxis) {
|
||||||
|
canvas.drawLine(x0, 0, x0, height, paint);
|
||||||
|
}
|
||||||
|
canvas.drawLine(0, y0, width, y0, paint);
|
||||||
|
|
||||||
|
matrix.reset();
|
||||||
|
matrix.preTranslate(-currentX, -currentY);
|
||||||
|
matrix.postScale(scale, -scale);
|
||||||
|
matrix.postTranslate(width / 2, height / 2);
|
||||||
|
|
||||||
|
paint.setStrokeWidth(0);
|
||||||
|
paint.setAntiAlias(false);
|
||||||
|
|
||||||
|
int n = Math.min(funcs.size(), GRAPHS_SIZE);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
computeGraph(funcs.get(i), minX, maxX, boundMinY, boundMaxY, graphs[i]);
|
||||||
|
Path path = graphToPath(graphs[i]);
|
||||||
|
path.transform(matrix);
|
||||||
|
paint.setColor(COL_GRAPH[i]);
|
||||||
|
canvas.drawPath(path, paint);
|
||||||
|
}
|
||||||
|
lastMinX = minX;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getXMax(float minX) {
|
||||||
|
return minX + gwidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getXMax() {
|
||||||
|
return getXMax(getXMin());
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getXMin() {
|
||||||
|
return currentX - gwidth / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canZoomIn() {
|
||||||
|
return gwidth > 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canZoomOut() {
|
||||||
|
return gwidth < 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateGraphs() {
|
||||||
|
clearAllGraph();
|
||||||
|
boundMinY = boundMaxY = 0;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
return touchHandler != null ? touchHandler.onTouchEvent(event) : super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchDown(float x, float y) {
|
||||||
|
zoomController.setVisible(true);
|
||||||
|
if (!scroller.isFinished()) {
|
||||||
|
scroller.abortAnimation();
|
||||||
|
}
|
||||||
|
lastTouchX = x;
|
||||||
|
lastTouchY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchMove(float x, float y) {
|
||||||
|
float deltaX = x - lastTouchX;
|
||||||
|
float deltaY = y - lastTouchY;
|
||||||
|
if (deltaX < -1 || deltaX > 1 || deltaY < -1 || deltaY > 1) {
|
||||||
|
scroll(-deltaX, deltaY);
|
||||||
|
lastTouchX = x;
|
||||||
|
lastTouchY = y;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchUp(float x, float y) {
|
||||||
|
final float scale = width / gwidth;
|
||||||
|
float sx = -touchHandler.velocityTracker.getXVelocity();
|
||||||
|
float sy = touchHandler.velocityTracker.getYVelocity();
|
||||||
|
final float asx = Math.abs(sx);
|
||||||
|
final float asy = Math.abs(sy);
|
||||||
|
if (asx < asy / 3) {
|
||||||
|
sx = 0;
|
||||||
|
} else if (asy < asx / 3) {
|
||||||
|
sy = 0;
|
||||||
|
}
|
||||||
|
scroller.fling(Math.round(currentX * scale),
|
||||||
|
Math.round(currentY * scale),
|
||||||
|
Math.round(sx), Math.round(sy), -10000, 10000, -10000, 10000);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchZoomDown(float x1, float y1, float x2, float y2) {
|
||||||
|
zoomTracker.start(gwidth, x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchZoomMove(float x1, float y1, float x2, float y2) {
|
||||||
|
if (!zoomTracker.update(x1, y1, x2, y2)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float targetGwidth = zoomTracker.value;
|
||||||
|
if (targetGwidth > .25f && targetGwidth < 200) {
|
||||||
|
gwidth = targetGwidth;
|
||||||
|
}
|
||||||
|
// scroll(-zoomTracker.moveX, zoomTracker.moveY);
|
||||||
|
invalidateGraphs();
|
||||||
|
// Calculator.log("zoom redraw");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scroll(float deltaX, float deltaY) {
|
||||||
|
final float scale = gwidth / width;
|
||||||
|
float dx = deltaX * scale;
|
||||||
|
float dy = deltaY * scale;
|
||||||
|
final float adx = Math.abs(dx);
|
||||||
|
final float ady = Math.abs(dy);
|
||||||
|
if (adx < ady / 3) {
|
||||||
|
dx = 0;
|
||||||
|
} else if (ady < adx / 3) {
|
||||||
|
dy = 0;
|
||||||
|
}
|
||||||
|
currentX += dx;
|
||||||
|
currentY += dy;
|
||||||
|
}
|
||||||
|
}
|
264
android-app/src/main/java/arity/calculator/Graph3d.java
Executable file
264
android-app/src/main/java/arity/calculator/Graph3d.java
Executable file
@ -0,0 +1,264 @@
|
|||||||
|
// Copyright (C) 2009-2010 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import org.javia.arity.Function;
|
||||||
|
|
||||||
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
import javax.microedition.khronos.opengles.GL11;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.nio.ShortBuffer;
|
||||||
|
|
||||||
|
class Graph3d {
|
||||||
|
private final int N = Calculator.useHighQuality3d ? 36 : 24;
|
||||||
|
private ShortBuffer verticeIdx;
|
||||||
|
private FloatBuffer vertexBuf;
|
||||||
|
private ByteBuffer colorBuf;
|
||||||
|
private int vertexVbo, colorVbo, vertexElementVbo;
|
||||||
|
private boolean useVBO;
|
||||||
|
private int nVertex;
|
||||||
|
|
||||||
|
Graph3d(GL11 gl) {
|
||||||
|
short[] b = new short[N*N];
|
||||||
|
int p = 0;
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
short v = 0;
|
||||||
|
for (int j = 0; j < N; v += N+N, j+=2) {
|
||||||
|
b[p++] = (short)(v+i);
|
||||||
|
b[p++] = (short)(v+N+N-1-i);
|
||||||
|
}
|
||||||
|
v = (short) (N*(N-2));
|
||||||
|
i++;
|
||||||
|
for (int j = N-1; j >= 0; v -= N+N, j-=2) {
|
||||||
|
b[p++] = (short)(v+N+N-1-i);
|
||||||
|
b[p++] = (short)(v+i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verticeIdx = buildBuffer(b);
|
||||||
|
|
||||||
|
String extensions = gl.glGetString(GL10.GL_EXTENSIONS);
|
||||||
|
useVBO = extensions.indexOf("vertex_buffer_object") != -1;
|
||||||
|
Calculator.log("VBOs support: " + useVBO + " version " + gl.glGetString(GL10.GL_VERSION));
|
||||||
|
|
||||||
|
if (useVBO) {
|
||||||
|
int[] out = new int[3];
|
||||||
|
gl.glGenBuffers(3, out, 0);
|
||||||
|
vertexVbo = out[0];
|
||||||
|
colorVbo = out[1];
|
||||||
|
vertexElementVbo = out[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FloatBuffer buildBuffer(float[] b) {
|
||||||
|
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 2);
|
||||||
|
bb.order(ByteOrder.nativeOrder());
|
||||||
|
FloatBuffer sb = bb.asFloatBuffer();
|
||||||
|
sb.put(b);
|
||||||
|
sb.position(0);
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ShortBuffer buildBuffer(short[] b) {
|
||||||
|
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 1);
|
||||||
|
bb.order(ByteOrder.nativeOrder());
|
||||||
|
ShortBuffer sb = bb.asShortBuffer();
|
||||||
|
sb.put(b);
|
||||||
|
sb.position(0);
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteBuffer buildBuffer(byte[] b) {
|
||||||
|
ByteBuffer bb = ByteBuffer.allocateDirect(b.length << 1);
|
||||||
|
bb.order(ByteOrder.nativeOrder());
|
||||||
|
bb.put(b);
|
||||||
|
bb.position(0);
|
||||||
|
return bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(GL11 gl, Function f, float zoom) {
|
||||||
|
final int NTICK = Calculator.useHighQuality3d ? 5 : 0;
|
||||||
|
final float size = 4*zoom;
|
||||||
|
final float minX = -size, maxX = size, minY = -size, maxY = size;
|
||||||
|
|
||||||
|
Calculator.log("update VBOs " + vertexVbo + ' ' + colorVbo + ' ' + vertexElementVbo);
|
||||||
|
nVertex = N*N+6+8 + NTICK*6;
|
||||||
|
int nFloats = nVertex * 3;
|
||||||
|
float vertices[] = new float[nFloats];
|
||||||
|
byte colors[] = new byte[nVertex << 2];
|
||||||
|
if (f != null) {
|
||||||
|
Calculator.log("Graph3d update");
|
||||||
|
float sizeX = maxX - minX;
|
||||||
|
float sizeY = maxY - minY;
|
||||||
|
float stepX = sizeX / (N-1);
|
||||||
|
float stepY = sizeY / (N-1);
|
||||||
|
int pos = 0;
|
||||||
|
double sum = 0;
|
||||||
|
float y = minY;
|
||||||
|
float x = minX - stepX;
|
||||||
|
int nRealPoints = 0;
|
||||||
|
for (int i = 0; i < N; i++, y+=stepY) {
|
||||||
|
float xinc = (i & 1) == 0 ? stepX : -stepX;
|
||||||
|
x += xinc;
|
||||||
|
for (int j = 0; j < N; ++j, x+=xinc, pos+=3) {
|
||||||
|
float z = (float) f.eval(x, y);
|
||||||
|
vertices[pos] = x;
|
||||||
|
vertices[pos+1] = y;
|
||||||
|
vertices[pos+2] = z;
|
||||||
|
if (z == z) { // not NAN
|
||||||
|
sum += z * z;
|
||||||
|
++nRealPoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float maxAbs = (float) Math.sqrt(sum / nRealPoints);
|
||||||
|
maxAbs *= .9f;
|
||||||
|
maxAbs = Math.min(maxAbs, 15);
|
||||||
|
maxAbs = Math.max(maxAbs, .001f);
|
||||||
|
|
||||||
|
final int limitColor = N*N*4;
|
||||||
|
for (int i = 0, j = 2; i < limitColor; i+=4, j+=3) {
|
||||||
|
float z = vertices[j];
|
||||||
|
if (z == z) {
|
||||||
|
final float a = z / maxAbs;
|
||||||
|
final float abs = a < 0 ? -a : a;
|
||||||
|
colors[i] = floatToByte(a);
|
||||||
|
colors[i+1] = floatToByte(1-abs*.3f);
|
||||||
|
colors[i+2] = floatToByte(-a);
|
||||||
|
colors[i+3] = (byte) 255;
|
||||||
|
} else {
|
||||||
|
vertices[j] = 0;
|
||||||
|
z = 0;
|
||||||
|
colors[i] = 0;
|
||||||
|
colors[i+1] = 0;
|
||||||
|
colors[i+2] = 0;
|
||||||
|
colors[i+3] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int base = N*N*3;
|
||||||
|
int colorBase = N*N*4;
|
||||||
|
int p = base;
|
||||||
|
final int baseSize = 2;
|
||||||
|
for (int i = -baseSize; i <= baseSize; i+=2*baseSize) {
|
||||||
|
vertices[p] = i; vertices[p+1] = -baseSize; vertices[p+2] = 0;
|
||||||
|
p += 3;
|
||||||
|
vertices[p] = i; vertices[p+1] = baseSize; vertices[p+2] = 0;
|
||||||
|
p += 3;
|
||||||
|
vertices[p] = -baseSize; vertices[p+1] = i; vertices[p+2] = 0;
|
||||||
|
p += 3;
|
||||||
|
vertices[p] = baseSize; vertices[p+1] = i; vertices[p+2] = 0;
|
||||||
|
p += 3;
|
||||||
|
}
|
||||||
|
for (int i = colorBase; i < colorBase+8*4; i += 4) {
|
||||||
|
colors[i] = 0;
|
||||||
|
colors[i+1] = 0;
|
||||||
|
colors[i+2] = (byte) 255;
|
||||||
|
colors[i+3] = (byte) 255;
|
||||||
|
}
|
||||||
|
base += 8*3;
|
||||||
|
colorBase += 8*4;
|
||||||
|
|
||||||
|
final float unit = 2;
|
||||||
|
final float axis[] = {
|
||||||
|
0, 0, 0,
|
||||||
|
unit, 0, 0,
|
||||||
|
0, 0, 0,
|
||||||
|
0, unit, 0,
|
||||||
|
0, 0, 0,
|
||||||
|
0, 0, unit,
|
||||||
|
};
|
||||||
|
System.arraycopy(axis, 0, vertices, base, 6*3);
|
||||||
|
for (int i = colorBase; i < colorBase+6*4; i+=4) {
|
||||||
|
colors[i] = (byte) 255;
|
||||||
|
colors[i+1] = (byte) 255;
|
||||||
|
colors[i+2] = (byte) 255;
|
||||||
|
colors[i+3] = (byte) 255;
|
||||||
|
}
|
||||||
|
base += 6*3;
|
||||||
|
colorBase += 6*4;
|
||||||
|
|
||||||
|
p = base;
|
||||||
|
final float tick = .03f;
|
||||||
|
final float offset = .01f;
|
||||||
|
for (int i = 1; i <= NTICK; ++i) {
|
||||||
|
vertices[p] = i-tick;
|
||||||
|
vertices[p+1] = -offset;
|
||||||
|
vertices[p+2] = -offset;
|
||||||
|
|
||||||
|
vertices[p+3] = i+tick;
|
||||||
|
vertices[p+4] = offset;
|
||||||
|
vertices[p+5] = offset;
|
||||||
|
p += 6;
|
||||||
|
|
||||||
|
vertices[p] = -offset;
|
||||||
|
vertices[p+1] = i-tick;
|
||||||
|
vertices[p+2] = -offset;
|
||||||
|
|
||||||
|
vertices[p+3] = offset;
|
||||||
|
vertices[p+4] = i+tick;
|
||||||
|
vertices[p+5] = offset;
|
||||||
|
p += 6;
|
||||||
|
|
||||||
|
vertices[p] = -offset;
|
||||||
|
vertices[p+1] = -offset;
|
||||||
|
vertices[p+2] = i-tick;
|
||||||
|
|
||||||
|
vertices[p+3] = offset;
|
||||||
|
vertices[p+4] = offset;
|
||||||
|
vertices[p+5] = i+tick;
|
||||||
|
p += 6;
|
||||||
|
|
||||||
|
}
|
||||||
|
for (int i = colorBase+NTICK*6*4-1; i >= colorBase; --i) {
|
||||||
|
colors[i] = (byte) 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertexBuf = buildBuffer(vertices);
|
||||||
|
colorBuf = buildBuffer(colors);
|
||||||
|
|
||||||
|
if (useVBO) {
|
||||||
|
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexVbo);
|
||||||
|
gl.glBufferData(GL11.GL_ARRAY_BUFFER, vertexBuf.capacity()*4, vertexBuf, GL11.GL_STATIC_DRAW);
|
||||||
|
vertexBuf = null;
|
||||||
|
|
||||||
|
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, colorVbo);
|
||||||
|
gl.glBufferData(GL11.GL_ARRAY_BUFFER, colorBuf.capacity(), colorBuf, GL11.GL_STATIC_DRAW);
|
||||||
|
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
|
||||||
|
colorBuf = null;
|
||||||
|
|
||||||
|
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, vertexElementVbo);
|
||||||
|
gl.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, verticeIdx.capacity()*2, verticeIdx, GL11.GL_STATIC_DRAW);
|
||||||
|
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte floatToByte(float v) {
|
||||||
|
return (byte) (v <= 0 ? 0 : v >= 1 ? 255 : (int)(v*255));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(GL11 gl) {
|
||||||
|
if (useVBO) {
|
||||||
|
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexVbo);
|
||||||
|
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, 0);
|
||||||
|
|
||||||
|
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, colorVbo);
|
||||||
|
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, 0);
|
||||||
|
|
||||||
|
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
|
||||||
|
// gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, N*N);
|
||||||
|
|
||||||
|
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, vertexElementVbo);
|
||||||
|
gl.glDrawElements(GL10.GL_LINE_STRIP, N*N, GL10.GL_UNSIGNED_SHORT, 0);
|
||||||
|
gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
} else {
|
||||||
|
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuf);
|
||||||
|
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colorBuf);
|
||||||
|
gl.glDrawElements(GL10.GL_LINE_STRIP, N*N, GL10.GL_UNSIGNED_SHORT, verticeIdx);
|
||||||
|
}
|
||||||
|
final int N2 = N*N;
|
||||||
|
gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, N2);
|
||||||
|
gl.glDrawArrays(GL10.GL_LINES, N2, nVertex - N2);
|
||||||
|
}
|
||||||
|
}
|
249
android-app/src/main/java/arity/calculator/Graph3dView.java
Executable file
249
android-app/src/main/java/arity/calculator/Graph3dView.java
Executable file
@ -0,0 +1,249 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.opengl.Matrix;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.widget.ZoomButtonsController;
|
||||||
|
import org.javia.arity.Function;
|
||||||
|
|
||||||
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
import javax.microedition.khronos.opengles.GL11;
|
||||||
|
|
||||||
|
public class Graph3dView extends GLView implements
|
||||||
|
GraphView,
|
||||||
|
ZoomButtonsController.OnZoomListener,
|
||||||
|
TouchHandler.TouchHandlerInterface {
|
||||||
|
|
||||||
|
private float lastTouchX, lastTouchY;
|
||||||
|
private TouchHandler touchHandler;
|
||||||
|
private ZoomButtonsController zoomController = new ZoomButtonsController(this);
|
||||||
|
private float zoomLevel = 1, targetZoom, zoomStep = 0, currentZoom;
|
||||||
|
private FPS fps = new FPS();
|
||||||
|
private Graph3d graph;
|
||||||
|
|
||||||
|
public Graph3dView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Graph3dView(Context context) {
|
||||||
|
super(context);
|
||||||
|
touchHandler = new TouchHandler(this);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
startLooping();
|
||||||
|
zoomController.setOnZoomListener(this);
|
||||||
|
|
||||||
|
Matrix.setIdentityM(matrix1, 0);
|
||||||
|
Matrix.rotateM(matrix1, 0, -75, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onVisibilityChanged(boolean visible) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onZoom(boolean zoomIn) {
|
||||||
|
boolean changed = false;
|
||||||
|
if (zoomIn) {
|
||||||
|
if (canZoomIn(zoomLevel)) {
|
||||||
|
targetZoom = zoomLevel * .625f;
|
||||||
|
zoomStep = -zoomLevel / 40;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (canZoomOut(zoomLevel)) {
|
||||||
|
targetZoom = zoomLevel * 1.6f;
|
||||||
|
zoomStep = zoomLevel / 20;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
zoomController.setZoomInEnabled(canZoomIn(targetZoom));
|
||||||
|
zoomController.setZoomOutEnabled(canZoomOut(targetZoom));
|
||||||
|
if (!shouldRotate()) {
|
||||||
|
setRotation(0, 0);
|
||||||
|
}
|
||||||
|
startLooping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void glDraw() {
|
||||||
|
if ((zoomStep < 0 && zoomLevel > targetZoom) ||
|
||||||
|
(zoomStep > 0 && zoomLevel < targetZoom)) {
|
||||||
|
zoomLevel += zoomStep;
|
||||||
|
} else if (zoomStep != 0) {
|
||||||
|
zoomStep = 0;
|
||||||
|
zoomLevel = targetZoom;
|
||||||
|
isDirty = true;
|
||||||
|
if (!shouldRotate()) {
|
||||||
|
stopLooping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.glDraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canZoomIn(float zoom) {
|
||||||
|
return zoom > .2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canZoomOut(float zoom) {
|
||||||
|
return zoom < 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromWindow() {
|
||||||
|
zoomController.setVisible(false);
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchDown(float x, float y) {
|
||||||
|
zoomController.setVisible(true);
|
||||||
|
stopLooping();
|
||||||
|
lastTouchX = x;
|
||||||
|
lastTouchY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchMove(float x, float y) {
|
||||||
|
float deltaX = x - lastTouchX;
|
||||||
|
float deltaY = y - lastTouchY;
|
||||||
|
if (deltaX > 1 || deltaX < -1 || deltaY > 1 || deltaY < -1) {
|
||||||
|
setRotation(deltaX, deltaY);
|
||||||
|
glDraw();
|
||||||
|
lastTouchX = x;
|
||||||
|
lastTouchY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchUp(float x, float y) {
|
||||||
|
float vx = touchHandler.velocityTracker.getXVelocity();
|
||||||
|
float vy = touchHandler.velocityTracker.getYVelocity();
|
||||||
|
// Calculator.log("velocity " + vx + ' ' + vy);
|
||||||
|
setRotation(vx / 100, vy / 100);
|
||||||
|
if (shouldRotate()) {
|
||||||
|
startLooping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchZoomDown(float x1, float y1, float x2, float y2) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTouchZoomMove(float x1, float y1, float x2, float y2) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
return touchHandler != null ? touchHandler.onTouchEvent(event) : super.onTouchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
|
private float[] matrix1 = new float[16], matrix2 = new float[16], matrix3 = new float[16];
|
||||||
|
private float angleX, angleY;
|
||||||
|
private boolean isDirty;
|
||||||
|
private Function function;
|
||||||
|
private static final float DISTANCE = 15f;
|
||||||
|
|
||||||
|
void setRotation(float x, float y) {
|
||||||
|
angleX = x;
|
||||||
|
angleY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean shouldRotate() {
|
||||||
|
final float limit = .5f;
|
||||||
|
return angleX < -limit || angleX > limit || angleY < -limit || angleY > limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFunction(Function f) {
|
||||||
|
function = f;
|
||||||
|
zoomLevel = 1;
|
||||||
|
isDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceCreated(GL10 gl, int width, int height) {
|
||||||
|
gl.glDisable(GL10.GL_DITHER);
|
||||||
|
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
|
||||||
|
gl.glClearColor(0, 0, 0, 1);
|
||||||
|
gl.glShadeModel(Calculator.useHighQuality3d ? GL10.GL_SMOOTH : GL10.GL_FLAT);
|
||||||
|
gl.glDisable(GL10.GL_LIGHTING);
|
||||||
|
graph = new Graph3d((GL11) gl);
|
||||||
|
isDirty = true;
|
||||||
|
angleX = .5f;
|
||||||
|
angleY = 0;
|
||||||
|
|
||||||
|
gl.glViewport(0, 0, width, height);
|
||||||
|
initFrustum(gl, DISTANCE * zoomLevel);
|
||||||
|
currentZoom = zoomLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDrawFrame(GL10 gl10) {
|
||||||
|
GL11 gl = (GL11) gl10;
|
||||||
|
if (currentZoom != zoomLevel) {
|
||||||
|
initFrustum(gl, DISTANCE * zoomLevel);
|
||||||
|
currentZoom = zoomLevel;
|
||||||
|
}
|
||||||
|
if (isDirty) {
|
||||||
|
graph.update(gl, function, zoomLevel);
|
||||||
|
isDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fps.incFrame()) {
|
||||||
|
Calculator.log("f/s " + fps.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
|
||||||
|
gl.glMatrixMode(GL10.GL_MODELVIEW);
|
||||||
|
gl.glLoadIdentity();
|
||||||
|
gl.glTranslatef(0, 0, -DISTANCE * zoomLevel);
|
||||||
|
|
||||||
|
Matrix.setIdentityM(matrix2, 0);
|
||||||
|
float ax = Math.abs(angleX);
|
||||||
|
float ay = Math.abs(angleY);
|
||||||
|
if (ay * 3 < ax) {
|
||||||
|
Matrix.rotateM(matrix2, 0, angleX, 0, 1, 0);
|
||||||
|
} else if (ax * 3 < ay) {
|
||||||
|
Matrix.rotateM(matrix2, 0, angleY, 1, 0, 0);
|
||||||
|
} else {
|
||||||
|
if (ax > ay) {
|
||||||
|
Matrix.rotateM(matrix2, 0, angleX, 0, 1, 0);
|
||||||
|
Matrix.rotateM(matrix2, 0, angleY, 1, 0, 0);
|
||||||
|
} else {
|
||||||
|
Matrix.rotateM(matrix2, 0, angleY, 1, 0, 0);
|
||||||
|
Matrix.rotateM(matrix2, 0, angleX, 0, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Matrix.multiplyMM(matrix3, 0, matrix2, 0, matrix1, 0);
|
||||||
|
gl.glMultMatrixf(matrix3, 0);
|
||||||
|
System.arraycopy(matrix3, 0, matrix1, 0, 16);
|
||||||
|
graph.draw(gl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initFrustum(GL10 gl, float distance) {
|
||||||
|
gl.glMatrixMode(GL10.GL_PROJECTION);
|
||||||
|
gl.glLoadIdentity();
|
||||||
|
float near = distance * (1 / 3f);
|
||||||
|
float far = distance * 3f;
|
||||||
|
float dimen = near / 5f;
|
||||||
|
float h = dimen * height / width;
|
||||||
|
gl.glFrustumf(-dimen, dimen, -h, h, near, far);
|
||||||
|
gl.glMatrixMode(GL10.GL_MODELVIEW);
|
||||||
|
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
|
||||||
|
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printMatrix(float[] m, String name) {
|
||||||
|
StringBuffer b = new StringBuffer();
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
b.append(m[i]).append(' ');
|
||||||
|
}
|
||||||
|
Calculator.log(name + ' ' + b.toString());
|
||||||
|
}
|
||||||
|
}
|
30
android-app/src/main/java/arity/calculator/GraphView.java
Executable file
30
android-app/src/main/java/arity/calculator/GraphView.java
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (C) 2009-2010 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import org.javia.arity.Function;
|
||||||
|
|
||||||
|
public interface GraphView {
|
||||||
|
|
||||||
|
static final String SCREENSHOT_DIR = "/screenshots";
|
||||||
|
|
||||||
|
public void setFunction(Function f);
|
||||||
|
|
||||||
|
public void onPause();
|
||||||
|
public void onResume();
|
||||||
|
|
||||||
|
public String captureScreenshot();
|
||||||
|
|
||||||
|
void setId(int id);
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************************************
|
||||||
|
*
|
||||||
|
* CUSTOMIZATION
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* void setBgColor(int color);
|
||||||
|
void setAxisColor(int color);*/
|
||||||
|
}
|
16
android-app/src/main/java/arity/calculator/Help.java
Executable file
16
android-app/src/main/java/arity/calculator/Help.java
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
public class Help extends Activity {
|
||||||
|
public void onCreate(Bundle icicle) {
|
||||||
|
super.onCreate(icicle);
|
||||||
|
WebView view = new WebView(this);
|
||||||
|
setContentView(view);
|
||||||
|
view.loadUrl("file:///android_asset/help.html");
|
||||||
|
}
|
||||||
|
}
|
112
android-app/src/main/java/arity/calculator/History.java
Executable file
112
android-app/src/main/java/arity/calculator/History.java
Executable file
@ -0,0 +1,112 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
class History extends FileHandler {
|
||||||
|
private static final int SIZE_LIMIT = 30;
|
||||||
|
ArrayList<HistoryEntry> entries = new ArrayList<HistoryEntry>();
|
||||||
|
int pos;
|
||||||
|
HistoryEntry aboveTop = new HistoryEntry("", "");
|
||||||
|
|
||||||
|
|
||||||
|
History(Context context) {
|
||||||
|
super(context, "history", 1);
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
entries.clear();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() {
|
||||||
|
return entries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void doRead(DataInputStream is) throws IOException {
|
||||||
|
aboveTop = new HistoryEntry(is);
|
||||||
|
int loadSize = is.readInt();
|
||||||
|
for (int i = 0; i < loadSize; ++i) {
|
||||||
|
entries.add(new HistoryEntry(is));
|
||||||
|
}
|
||||||
|
pos = entries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void doWrite(DataOutputStream os) throws IOException {
|
||||||
|
aboveTop.save(os);
|
||||||
|
os.writeInt(entries.size());
|
||||||
|
for (HistoryEntry entry : entries) {
|
||||||
|
entry.save(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HistoryEntry currentEntry() {
|
||||||
|
if (pos < entries.size()) {
|
||||||
|
return entries.get(pos);
|
||||||
|
} else {
|
||||||
|
return aboveTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getListPos() {
|
||||||
|
return entries.size() - 1 - pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean onEnter(String text, String result) {
|
||||||
|
if (result == null) {
|
||||||
|
result = "";
|
||||||
|
}
|
||||||
|
currentEntry().onEnter();
|
||||||
|
pos = entries.size();
|
||||||
|
if (text.length() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (entries.size() > 0) {
|
||||||
|
HistoryEntry top = entries.get(entries.size()-1);
|
||||||
|
if (text.equals(top.line) && result.equals(top.result)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (entries.size() > SIZE_LIMIT) {
|
||||||
|
entries.remove(0);
|
||||||
|
}
|
||||||
|
entries.add(new HistoryEntry(text, result));
|
||||||
|
pos = entries.size();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveToPos(int listPos, String text) {
|
||||||
|
currentEntry().editLine = text;
|
||||||
|
pos = entries.size() - listPos - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateEdited(String text) {
|
||||||
|
currentEntry().editLine = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean moveUp(String text) {
|
||||||
|
updateEdited(text);
|
||||||
|
if (pos >= entries.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean moveDown(String text) {
|
||||||
|
updateEdited(text);
|
||||||
|
if (pos <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
--pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getText() {
|
||||||
|
return currentEntry().editLine;
|
||||||
|
}
|
||||||
|
}
|
34
android-app/src/main/java/arity/calculator/HistoryEntry.java
Executable file
34
android-app/src/main/java/arity/calculator/HistoryEntry.java
Executable file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
class HistoryEntry {
|
||||||
|
String line, editLine, result;
|
||||||
|
|
||||||
|
HistoryEntry(DataInputStream is) throws IOException {
|
||||||
|
line = is.readUTF();
|
||||||
|
editLine = is.readUTF();
|
||||||
|
if (editLine.length() == 0) {
|
||||||
|
editLine = line;
|
||||||
|
}
|
||||||
|
result = is.readUTF();
|
||||||
|
}
|
||||||
|
|
||||||
|
HistoryEntry(String text, String result) {
|
||||||
|
line = text;
|
||||||
|
editLine = text;
|
||||||
|
this.result = result == null ? "" : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void save(DataOutputStream os) throws IOException {
|
||||||
|
os.writeUTF(line);
|
||||||
|
os.writeUTF(editLine.equals(line) ? "" : editLine);
|
||||||
|
os.writeUTF(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onEnter() {
|
||||||
|
editLine = line;
|
||||||
|
}
|
||||||
|
}
|
21
android-app/src/main/java/arity/calculator/MotionEventWrap.java
Executable file
21
android-app/src/main/java/arity/calculator/MotionEventWrap.java
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (C) 2010 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
class MotionEventWrap {
|
||||||
|
private static final boolean IS_API_5 = Util.SDK_VERSION >= 5;
|
||||||
|
|
||||||
|
static int getPointerCount(MotionEvent event) {
|
||||||
|
return IS_API_5 ? MotionEventWrapNew.getPointerCount(event) : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float getX(MotionEvent event, int idx) {
|
||||||
|
return IS_API_5 ? MotionEventWrapNew.getX(event, idx) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float getY(MotionEvent event, int idx) {
|
||||||
|
return IS_API_5 ? MotionEventWrapNew.getX(event, idx) : 0;
|
||||||
|
}
|
||||||
|
}
|
19
android-app/src/main/java/arity/calculator/MotionEventWrapNew.java
Executable file
19
android-app/src/main/java/arity/calculator/MotionEventWrapNew.java
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (C) 2010 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
class MotionEventWrapNew {
|
||||||
|
static int getPointerCount(MotionEvent event) {
|
||||||
|
return event.getPointerCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
static float getX(MotionEvent event, int idx) {
|
||||||
|
return event.getX(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float getY(MotionEvent event, int idx) {
|
||||||
|
return event.getY(idx);
|
||||||
|
}
|
||||||
|
}
|
69
android-app/src/main/java/arity/calculator/ShowGraph.java
Executable file
69
android-app/src/main/java/arity/calculator/ShowGraph.java
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (C) 2009 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import org.javia.arity.Function;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class ShowGraph extends Activity {
|
||||||
|
|
||||||
|
private GraphView view;
|
||||||
|
|
||||||
|
public void onCreate(Bundle icicle) {
|
||||||
|
super.onCreate(icicle);
|
||||||
|
ArrayList<Function> funcs = Calculator.graphedFunction;
|
||||||
|
if (funcs == null) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int size = funcs.size();
|
||||||
|
if (size == 1) {
|
||||||
|
Function f = funcs.get(0);
|
||||||
|
view = f.arity() == 1 ? new Graph2dView(this) : new Graph3dView(this);
|
||||||
|
view.setFunction(f);
|
||||||
|
} else {
|
||||||
|
view = new Graph2dView(this);
|
||||||
|
((Graph2dView) view).setFunctions(funcs);
|
||||||
|
}
|
||||||
|
setContentView((View) view);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
view.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
view.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
super.onCreateOptionsMenu(menu);
|
||||||
|
(new MenuInflater(this)).inflate(R.menu.graph, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
super.onOptionsItemSelected(item);
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.capture_screenshot:
|
||||||
|
String fileName = view.captureScreenshot();
|
||||||
|
if (fileName != null) {
|
||||||
|
Toast.makeText(this, "screenshot saved as \n" + fileName, Toast.LENGTH_LONG).show();
|
||||||
|
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||||
|
i.setDataAndType(Uri.fromFile(new File(fileName)), "image/png");
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}*/
|
||||||
|
}
|
79
android-app/src/main/java/arity/calculator/TouchHandler.java
Executable file
79
android-app/src/main/java/arity/calculator/TouchHandler.java
Executable file
@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (C) 2009-2010 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.VelocityTracker;
|
||||||
|
|
||||||
|
class TouchHandler {
|
||||||
|
static interface TouchHandlerInterface {
|
||||||
|
void onTouchDown(float x, float y);
|
||||||
|
void onTouchMove(float x, float y);
|
||||||
|
void onTouchUp(float x, float y);
|
||||||
|
void onTouchZoomDown(float x1, float y1, float x2, float y2);
|
||||||
|
void onTouchZoomMove(float x1, float y1, float x2, float y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
VelocityTracker velocityTracker = VelocityTracker.obtain();
|
||||||
|
|
||||||
|
private boolean isAfterZoom;
|
||||||
|
private TouchHandlerInterface listener;
|
||||||
|
|
||||||
|
TouchHandler(TouchHandlerInterface listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
// Calculator.log("touch " + event + ' ' + event.getPointerCount() + event.getPointerId(0));
|
||||||
|
|
||||||
|
int fullAction = event.getAction();
|
||||||
|
int action = fullAction & MotionEvent.ACTION_MASK;
|
||||||
|
int pointer = (fullAction & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
|
||||||
|
float x = event.getX();
|
||||||
|
float y = event.getY();
|
||||||
|
int nPoints = MotionEventWrap.getPointerCount(event);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
isAfterZoom = false;
|
||||||
|
velocityTracker.clear();
|
||||||
|
velocityTracker.addMovement(event);
|
||||||
|
listener.onTouchDown(x, y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
if (nPoints == 1) {
|
||||||
|
if (isAfterZoom) {
|
||||||
|
velocityTracker.clear();
|
||||||
|
listener.onTouchDown(x, y);
|
||||||
|
isAfterZoom = false;
|
||||||
|
}
|
||||||
|
velocityTracker.addMovement(event);
|
||||||
|
listener.onTouchMove(x, y);
|
||||||
|
} else if (nPoints == 2) {
|
||||||
|
listener.onTouchZoomMove(x, y, MotionEventWrap.getX(event, 1), MotionEventWrap.getY(event, 1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
velocityTracker.addMovement(event);
|
||||||
|
velocityTracker.computeCurrentVelocity(1000);
|
||||||
|
listener.onTouchUp(x, y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_POINTER_DOWN:
|
||||||
|
if (nPoints == 2) {
|
||||||
|
listener.onTouchZoomDown(x, y, MotionEventWrap.getX(event, 1), MotionEventWrap.getY(event, 1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_POINTER_UP:
|
||||||
|
if (nPoints == 2) {
|
||||||
|
isAfterZoom = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
70
android-app/src/main/java/arity/calculator/Util.java
Executable file
70
android-app/src/main/java/arity/calculator/Util.java
Executable file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright (C) 2009-2010 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import java.nio.ShortBuffer;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
class Util {
|
||||||
|
public static final int SDK_VERSION = getSdkVersion();
|
||||||
|
|
||||||
|
private static int getSdkVersion() {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(Build.VERSION.SDK);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Calculator.log("invalid SDK " + Build.VERSION.SDK);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String saveBitmap(Bitmap bitmap, String dir, String baseName) {
|
||||||
|
try {
|
||||||
|
File sdcard = Environment.getExternalStorageDirectory();
|
||||||
|
File pictureDir = new File(sdcard, dir);
|
||||||
|
pictureDir.mkdirs();
|
||||||
|
File f = null;
|
||||||
|
for (int i = 1; i < 200; ++i) {
|
||||||
|
String name = baseName + i + ".png";
|
||||||
|
f = new File(pictureDir, name);
|
||||||
|
if (!f.exists()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!f.exists()) {
|
||||||
|
String name = f.getAbsolutePath();
|
||||||
|
FileOutputStream fos = new FileOutputStream(name);
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||||
|
fos.flush();
|
||||||
|
fos.close();
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Calculator.log("exception saving screenshot: " + e);
|
||||||
|
} finally {
|
||||||
|
/*
|
||||||
|
if (fos != null) {
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bitmapBGRtoRGB(Bitmap bitmap, int width, int height) {
|
||||||
|
int size = width * height;
|
||||||
|
short data[] = new short[size];
|
||||||
|
ShortBuffer buf = ShortBuffer.wrap(data);
|
||||||
|
bitmap.copyPixelsToBuffer(buf);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
//BGR-565 to RGB-565
|
||||||
|
short v = data[i];
|
||||||
|
data[i] = (short) (((v&0x1f) << 11) | (v&0x7e0) | ((v&0xf800) >> 11));
|
||||||
|
}
|
||||||
|
buf.rewind();
|
||||||
|
bitmap.copyPixelsFromBuffer(buf);
|
||||||
|
}
|
||||||
|
}
|
53
android-app/src/main/java/arity/calculator/ZoomTracker.java
Executable file
53
android-app/src/main/java/arity/calculator/ZoomTracker.java
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (C) 2010 Mihai Preda
|
||||||
|
|
||||||
|
package arity.calculator;
|
||||||
|
|
||||||
|
class ZoomTracker {
|
||||||
|
private float sx1, sy1, sx2, sy2;
|
||||||
|
private float initialDist;
|
||||||
|
private float initialValue;
|
||||||
|
|
||||||
|
float value;
|
||||||
|
float moveX, moveY;
|
||||||
|
|
||||||
|
void start(float value, float x1, float y1, float x2, float y2) {
|
||||||
|
sx1 = x1;
|
||||||
|
sy1 = y1;
|
||||||
|
sx2 = x2;
|
||||||
|
sy2 = y2;
|
||||||
|
initialDist = distance(x1, y1, x2, y2);
|
||||||
|
initialValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean update(float x1, float y1, float x2, float y2) {
|
||||||
|
final float LIMIT = 1.5f;
|
||||||
|
if (Math.abs(x1 - sx1) < LIMIT && Math.abs(y1 - sy1) < LIMIT &&
|
||||||
|
Math.abs(x2 - sx2) < LIMIT && Math.abs(y2 - sy2) < LIMIT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
moveX = common(x1, sx1, x2, sx2);
|
||||||
|
moveY = common(y1, sy1, y2, sy2);
|
||||||
|
float dist = distance(x1, y1, x2, y2);
|
||||||
|
value = initialDist / dist * initialValue;
|
||||||
|
sx1 = x1;
|
||||||
|
sx2 = x2;
|
||||||
|
sy1 = y1;
|
||||||
|
sy2 = y2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float distance(float x1, float y1, float x2, float y2) {
|
||||||
|
final float dx = x1-x2;
|
||||||
|
final float dy = y1-y2;
|
||||||
|
// return (float) Math.sqrt(dx*dx+dy*dy);
|
||||||
|
return Math.max(dx*dx, dy*dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float common(float x1, float sx1, float x2, float sx2) {
|
||||||
|
float dx1 = x1 - sx1;
|
||||||
|
float dx2 = x2 - sx2;
|
||||||
|
return (dx1 < 0 && dx2 < 0) ? Math.max(dx1, dx2) :
|
||||||
|
(dx1 > 0 && dx2 > 0) ? Math.min(dx1, dx2):
|
||||||
|
0;
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
public class AndroidCalculatorLogger implements CalculatorLogger {
|
public class AndroidCalculatorLogger implements CalculatorLogger {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static final String TAG = AndroidCalculatorLogger.class.getSimpleName();
|
private static final String TAG = "Calculatorpp";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void debug(@Nullable String tag, @NotNull String message) {
|
public void debug(@Nullable String tag, @NotNull String message) {
|
||||||
@ -21,7 +21,7 @@ public class AndroidCalculatorLogger implements CalculatorLogger {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String getTag(@Nullable String tag) {
|
private String getTag(@Nullable String tag) {
|
||||||
return tag != null ? tag : TAG;
|
return tag != null ? TAG + "/" + tag : TAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.solovyev.android.AndroidUtils;
|
import org.solovyev.android.AndroidUtils;
|
||||||
import org.solovyev.android.calculator.about.CalculatorFragmentType;
|
import org.solovyev.android.calculator.about.CalculatorFragmentType;
|
||||||
import org.solovyev.android.calculator.about.CalculatorReleaseNotesFragment;
|
import org.solovyev.android.calculator.about.CalculatorReleaseNotesFragment;
|
||||||
|
import org.solovyev.android.calculator.plot.CalculatorPlotActivity;
|
||||||
import org.solovyev.android.fragments.FragmentUtils;
|
import org.solovyev.android.fragments.FragmentUtils;
|
||||||
import org.solovyev.android.prefs.Preference;
|
import org.solovyev.android.prefs.Preference;
|
||||||
import org.solovyev.common.equals.EqualsTool;
|
import org.solovyev.common.equals.EqualsTool;
|
||||||
@ -63,7 +64,7 @@ public class CalculatorActivity extends SherlockFragmentActivity implements Shar
|
|||||||
activityHelper.addTab(this, CalculatorFragmentType.variables, null, R.id.main_second_pane);
|
activityHelper.addTab(this, CalculatorFragmentType.variables, null, R.id.main_second_pane);
|
||||||
activityHelper.addTab(this, CalculatorFragmentType.functions, null, R.id.main_second_pane);
|
activityHelper.addTab(this, CalculatorFragmentType.functions, null, R.id.main_second_pane);
|
||||||
activityHelper.addTab(this, CalculatorFragmentType.operators, null, R.id.main_second_pane);
|
activityHelper.addTab(this, CalculatorFragmentType.operators, null, R.id.main_second_pane);
|
||||||
activityHelper.addTab(this, CalculatorFragmentType.plotter, null, R.id.main_second_pane);
|
activityHelper.addTab(this, CalculatorPlotActivity.getPlotterFragmentType(), null, R.id.main_second_pane);
|
||||||
activityHelper.addTab(this, CalculatorFragmentType.faq, null, R.id.main_second_pane);
|
activityHelper.addTab(this, CalculatorFragmentType.faq, null, R.id.main_second_pane);
|
||||||
} else {
|
} else {
|
||||||
getSupportActionBar().hide();
|
getSupportActionBar().hide();
|
||||||
|
@ -23,6 +23,7 @@ import org.solovyev.android.calculator.help.CalculatorHelpActivity;
|
|||||||
import org.solovyev.android.calculator.history.CalculatorHistoryActivity;
|
import org.solovyev.android.calculator.history.CalculatorHistoryActivity;
|
||||||
import org.solovyev.android.calculator.math.edit.*;
|
import org.solovyev.android.calculator.math.edit.*;
|
||||||
import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity;
|
import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity;
|
||||||
|
import org.solovyev.android.calculator.plot.AbstractCalculatorPlotFragment;
|
||||||
import org.solovyev.android.calculator.plot.CalculatorPlotActivity;
|
import org.solovyev.android.calculator.plot.CalculatorPlotActivity;
|
||||||
import org.solovyev.android.calculator.plot.CalculatorPlotFragment;
|
import org.solovyev.android.calculator.plot.CalculatorPlotFragment;
|
||||||
import org.solovyev.android.calculator.plot.PlotInput;
|
import org.solovyev.android.calculator.plot.PlotInput;
|
||||||
@ -99,10 +100,14 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
|
|||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void plotGraph(@NotNull final Context context, @NotNull Generic generic, @NotNull Constant constant){
|
public static void plotGraph(@NotNull final Context context,
|
||||||
|
@NotNull Generic generic,
|
||||||
|
@NotNull Constant xVariable,
|
||||||
|
@Nullable Constant yVariable){
|
||||||
final Intent intent = new Intent();
|
final Intent intent = new Intent();
|
||||||
intent.putExtra(ChartFactory.TITLE, context.getString(R.string.c_graph));
|
intent.putExtra(ChartFactory.TITLE, context.getString(R.string.c_graph));
|
||||||
intent.putExtra(CalculatorPlotFragment.INPUT, new CalculatorPlotFragment.Input(generic.toString(), constant.getName()));
|
final AbstractCalculatorPlotFragment.Input input = new CalculatorPlotFragment.Input(generic.toString(), xVariable.getName(), yVariable == null ? null : yVariable.getName());
|
||||||
|
intent.putExtra(CalculatorPlotFragment.INPUT, input);
|
||||||
intent.setClass(context, CalculatorPlotActivity.class);
|
intent.setClass(context, CalculatorPlotActivity.class);
|
||||||
AndroidUtils2.addFlags(intent, false, context);
|
AndroidUtils2.addFlags(intent, false, context);
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
@ -214,7 +219,7 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
|
|||||||
App.getInstance().getUiThreadExecutor().execute(new Runnable() {
|
App.getInstance().getUiThreadExecutor().execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
plotGraph(context, plotInput.getFunction(), plotInput.getConstant());
|
plotGraph(context, plotInput.getFunction(), plotInput.getXVariable(), plotInput.getYVariable());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -3,6 +3,7 @@ package org.solovyev.android.calculator;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Handler;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import net.robotmedia.billing.BillingController;
|
import net.robotmedia.billing.BillingController;
|
||||||
@ -65,8 +66,12 @@ public class CalculatorApplication extends android.app.Application implements Sh
|
|||||||
**********************************************************************
|
**********************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@NotNull
|
||||||
private final List<CalculatorEventListener> listeners = new ArrayList<CalculatorEventListener>();
|
private final List<CalculatorEventListener> listeners = new ArrayList<CalculatorEventListener>();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected final Handler uiHandler = new Handler();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
*
|
*
|
||||||
@ -186,6 +191,11 @@ public class CalculatorApplication extends android.app.Application implements Sh
|
|||||||
return new CalculatorFragmentHelperImpl(layoutId, titleResId, listenersOnCreate);
|
return new CalculatorFragmentHelperImpl(layoutId, titleResId, listenersOnCreate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Handler getUiHandler() {
|
||||||
|
return uiHandler;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
*
|
*
|
||||||
|
@ -13,6 +13,7 @@ import org.solovyev.android.calculator.math.edit.CalculatorFunctionsFragment;
|
|||||||
import org.solovyev.android.calculator.math.edit.CalculatorOperatorsFragment;
|
import org.solovyev.android.calculator.math.edit.CalculatorOperatorsFragment;
|
||||||
import org.solovyev.android.calculator.math.edit.CalculatorVarsFragment;
|
import org.solovyev.android.calculator.math.edit.CalculatorVarsFragment;
|
||||||
import org.solovyev.android.calculator.matrix.CalculatorMatrixEditFragment;
|
import org.solovyev.android.calculator.matrix.CalculatorMatrixEditFragment;
|
||||||
|
import org.solovyev.android.calculator.plot.CalculatorArityPlotFragment;
|
||||||
import org.solovyev.android.calculator.plot.CalculatorPlotFragment;
|
import org.solovyev.android.calculator.plot.CalculatorPlotFragment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,6 +32,7 @@ public enum CalculatorFragmentType {
|
|||||||
functions(CalculatorFunctionsFragment.class, R.layout.math_entities_fragment, R.string.c_functions),
|
functions(CalculatorFunctionsFragment.class, R.layout.math_entities_fragment, R.string.c_functions),
|
||||||
operators(CalculatorOperatorsFragment.class, R.layout.math_entities_fragment, R.string.c_operators),
|
operators(CalculatorOperatorsFragment.class, R.layout.math_entities_fragment, R.string.c_operators),
|
||||||
plotter(CalculatorPlotFragment.class, R.layout.plot_fragment, R.string.c_graph),
|
plotter(CalculatorPlotFragment.class, R.layout.plot_fragment, R.string.c_graph),
|
||||||
|
plotter_2(CalculatorArityPlotFragment.class, R.layout.plot_fragment, R.string.c_graph),
|
||||||
about(CalculatorAboutFragment.class, R.layout.about_fragment, R.string.c_about),
|
about(CalculatorAboutFragment.class, R.layout.about_fragment, R.string.c_about),
|
||||||
faq(CalculatorHelpFaqFragment.class, R.layout.help_faq_fragment, R.string.c_faq),
|
faq(CalculatorHelpFaqFragment.class, R.layout.help_faq_fragment, R.string.c_faq),
|
||||||
hints(CalculatorHelpHintsFragment.class, R.layout.help_hints_fragment, R.string.c_hints),
|
hints(CalculatorHelpHintsFragment.class, R.layout.help_hints_fragment, R.string.c_hints),
|
||||||
|
@ -0,0 +1,525 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import com.actionbarsherlock.view.Menu;
|
||||||
|
import com.actionbarsherlock.view.MenuInflater;
|
||||||
|
import com.actionbarsherlock.view.MenuItem;
|
||||||
|
import jscl.math.Expression;
|
||||||
|
import jscl.math.Generic;
|
||||||
|
import jscl.math.function.Constant;
|
||||||
|
import jscl.text.ParseException;
|
||||||
|
import org.achartengine.renderer.XYMultipleSeriesRenderer;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.solovyev.android.calculator.*;
|
||||||
|
import org.solovyev.android.menu.ActivityMenu;
|
||||||
|
import org.solovyev.android.menu.IdentifiableMenuItem;
|
||||||
|
import org.solovyev.android.menu.ListActivityMenu;
|
||||||
|
import org.solovyev.android.sherlock.menu.SherlockMenuHelper;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 12/30/12
|
||||||
|
* Time: 3:09 PM
|
||||||
|
*/
|
||||||
|
public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment implements CalculatorEventListener {
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************************************
|
||||||
|
*
|
||||||
|
* CONSTANTS
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected static final String TAG = "CalculatorPlotFragment";
|
||||||
|
|
||||||
|
public static final String INPUT = "plotter_input";
|
||||||
|
|
||||||
|
protected static final String PLOT_BOUNDARIES = "plot_boundaries";
|
||||||
|
|
||||||
|
private static final int DEFAULT_MIN_NUMBER = -10;
|
||||||
|
|
||||||
|
private static final int DEFAULT_MAX_NUMBER = 10;
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************************************
|
||||||
|
*
|
||||||
|
* FIELDS
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Input input;
|
||||||
|
|
||||||
|
private int bgColor;
|
||||||
|
|
||||||
|
// thread for applying UI changes
|
||||||
|
@NotNull
|
||||||
|
private final Handler uiHandler = new Handler();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private PreparedInput preparedInput;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private ActivityMenu<Menu, MenuItem> fragmentMenu = ListActivityMenu.fromResource(R.menu.plot_menu, PlotMenu.class, SherlockMenuHelper.getInstance());
|
||||||
|
|
||||||
|
// thread which calculated data for graph view
|
||||||
|
@NotNull
|
||||||
|
private final Executor plotExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final CalculatorEventHolder lastEventHolder = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId());
|
||||||
|
|
||||||
|
|
||||||
|
public AbstractCalculatorPlotFragment() {
|
||||||
|
super(CalculatorApplication.getInstance().createFragmentHelper(R.layout.plot_fragment, R.string.c_graph, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
final Bundle arguments = getArguments();
|
||||||
|
|
||||||
|
if (arguments != null) {
|
||||||
|
input = (CalculatorPlotFragment.Input) arguments.getSerializable(INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input == null) {
|
||||||
|
this.bgColor = getResources().getColor(R.color.cpp_pane_background);
|
||||||
|
} else {
|
||||||
|
this.bgColor = getResources().getColor(android.R.color.transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
if (input == null) {
|
||||||
|
this.preparedInput = prepareInputFromDisplay(Locator.getInstance().getDisplay().getViewState(), savedInstanceState);
|
||||||
|
} else {
|
||||||
|
this.preparedInput = prepareInput(input, true, savedInstanceState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle out) {
|
||||||
|
super.onSaveInstanceState(out);
|
||||||
|
|
||||||
|
final PlotBoundaries plotBoundaries = getPlotBoundaries();
|
||||||
|
if (plotBoundaries != null) {
|
||||||
|
out.putSerializable(PLOT_BOUNDARIES, plotBoundaries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected abstract PlotBoundaries getPlotBoundaries();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
createChart(preparedInput);
|
||||||
|
createGraphicalView(getView(), preparedInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable final Object data) {
|
||||||
|
if (calculatorEventType.isOfType(CalculatorEventType.display_state_changed)) {
|
||||||
|
PreparedInput preparedInput = getPreparedInput();
|
||||||
|
if (!preparedInput.isFromInputArgs()) {
|
||||||
|
|
||||||
|
final CalculatorEventHolder.Result result = this.lastEventHolder.apply(calculatorEventData);
|
||||||
|
if (result.isNewAfter()) {
|
||||||
|
preparedInput = prepareInputFromDisplay(((CalculatorDisplayChangeEventData) data).getNewValue(), null);
|
||||||
|
this.preparedInput = preparedInput;
|
||||||
|
|
||||||
|
|
||||||
|
final PreparedInput finalPreparedInput = preparedInput;
|
||||||
|
getUiHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
|
if (!finalPreparedInput.isError()) {
|
||||||
|
createChart(finalPreparedInput);
|
||||||
|
|
||||||
|
final View view = getView();
|
||||||
|
if (view != null) {
|
||||||
|
createGraphicalView(view, finalPreparedInput);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void onError();
|
||||||
|
|
||||||
|
protected abstract void createGraphicalView(@NotNull View view, @NotNull PreparedInput preparedInput);
|
||||||
|
|
||||||
|
protected abstract void createChart(@NotNull PreparedInput preparedInput);
|
||||||
|
|
||||||
|
|
||||||
|
protected double getMaxValue(@Nullable PlotBoundaries plotBoundaries) {
|
||||||
|
return plotBoundaries == null ? DEFAULT_MAX_NUMBER : plotBoundaries.getXMax();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double getMinValue(@Nullable PlotBoundaries plotBoundaries) {
|
||||||
|
return plotBoundaries == null ? DEFAULT_MIN_NUMBER : plotBoundaries.getXMin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************************************
|
||||||
|
*
|
||||||
|
* GETTERS
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Handler getUiHandler() {
|
||||||
|
return uiHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public PreparedInput getPreparedInput() {
|
||||||
|
return preparedInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBgColor() {
|
||||||
|
return bgColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Executor getPlotExecutor() {
|
||||||
|
return plotExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************************************
|
||||||
|
*
|
||||||
|
* MENU
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
|
||||||
|
final FragmentActivity activity = this.getActivity();
|
||||||
|
if (activity != null) {
|
||||||
|
fragmentMenu.onCreateOptionsMenu(activity, menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepareOptionsMenu(Menu menu) {
|
||||||
|
super.onPrepareOptionsMenu(menu);
|
||||||
|
|
||||||
|
final FragmentActivity activity = this.getActivity();
|
||||||
|
if (activity != null) {
|
||||||
|
fragmentMenu.onPrepareOptionsMenu(activity, menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
return super.onOptionsItemSelected(item) || fragmentMenu.onOptionsItemSelected(this.getActivity(), item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
**********************************************************************
|
||||||
|
*
|
||||||
|
* STATIC
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected static PreparedInput prepareInputFromDisplay(@NotNull CalculatorDisplayViewState displayState, @Nullable Bundle savedInstanceState) {
|
||||||
|
try {
|
||||||
|
if (displayState.isValid() && displayState.getResult() != null) {
|
||||||
|
final Generic expression = displayState.getResult();
|
||||||
|
if (CalculatorUtils.isPlotPossible(expression, displayState.getOperation())) {
|
||||||
|
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(expression));
|
||||||
|
final Constant xVariable = variables.get(0);
|
||||||
|
|
||||||
|
final Constant yVariable;
|
||||||
|
if ( variables.size() > 1 ) {
|
||||||
|
yVariable = variables.get(1);
|
||||||
|
} else {
|
||||||
|
yVariable = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Input input = new Input(expression.toString(), xVariable.getName(), yVariable == null ? null : yVariable.getName());
|
||||||
|
return prepareInput(input, false, savedInstanceState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Log.e(TAG, e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PreparedInput.newErrorInstance(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static PreparedInput prepareInput(@NotNull Input input, boolean fromInputArgs, @Nullable Bundle savedInstanceState) {
|
||||||
|
PreparedInput result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final PreparedExpression preparedExpression = ToJsclTextProcessor.getInstance().process(input.getExpression());
|
||||||
|
final Generic expression = Expression.valueOf(preparedExpression.getExpression());
|
||||||
|
final Constant xVar = new Constant(input.getXVariableName());
|
||||||
|
|
||||||
|
final Constant yVar;
|
||||||
|
if (input.getYVariableName() != null) {
|
||||||
|
yVar = new Constant(input.getYVariableName());
|
||||||
|
} else {
|
||||||
|
yVar = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlotBoundaries plotBoundaries = null;
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
plotBoundaries = (PlotBoundaries) savedInstanceState.getSerializable(PLOT_BOUNDARIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( plotBoundaries == null ) {
|
||||||
|
plotBoundaries = PlotBoundaries.newDefaultInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
result = PreparedInput.newInstance(input, expression, xVar, yVar, fromInputArgs, plotBoundaries);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
result = PreparedInput.newErrorInstance(fromInputArgs);
|
||||||
|
Locator.getInstance().getNotifier().showMessage(e);
|
||||||
|
} catch (CalculatorParseException e) {
|
||||||
|
result = PreparedInput.newErrorInstance(fromInputArgs);
|
||||||
|
Locator.getInstance().getNotifier().showMessage(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static enum PlotMenu implements IdentifiableMenuItem<MenuItem> {
|
||||||
|
|
||||||
|
preferences(R.id.menu_plot_settings) {
|
||||||
|
@Override
|
||||||
|
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
|
||||||
|
context.startActivity(new Intent(context, CalculatorPlotPreferenceActivity.class));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final int itemId;
|
||||||
|
|
||||||
|
private PlotMenu(int itemId) {
|
||||||
|
this.itemId = itemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Integer getItemId() {
|
||||||
|
return itemId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class PlotBoundaries implements Serializable {
|
||||||
|
|
||||||
|
private double xMin;
|
||||||
|
private double xMax;
|
||||||
|
private double yMin;
|
||||||
|
private double yMax;
|
||||||
|
|
||||||
|
public PlotBoundaries() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlotBoundaries(@NotNull XYMultipleSeriesRenderer renderer) {
|
||||||
|
this.xMin = renderer.getXAxisMin();
|
||||||
|
this.yMin = renderer.getYAxisMin();
|
||||||
|
this.xMax = renderer.getXAxisMax();
|
||||||
|
this.yMax = renderer.getYAxisMax();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getXMin() {
|
||||||
|
return xMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getXMax() {
|
||||||
|
return xMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getYMin() {
|
||||||
|
return yMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getYMax() {
|
||||||
|
return yMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "PlotBoundaries{" +
|
||||||
|
"yMax=" + yMax +
|
||||||
|
", yMin=" + yMin +
|
||||||
|
", xMax=" + xMax +
|
||||||
|
", xMin=" + xMin +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static PlotBoundaries newDefaultInstance() {
|
||||||
|
PlotBoundaries plotBoundaries = new PlotBoundaries();
|
||||||
|
plotBoundaries.xMin = DEFAULT_MIN_NUMBER;
|
||||||
|
plotBoundaries.yMin = DEFAULT_MIN_NUMBER;
|
||||||
|
plotBoundaries.xMax = DEFAULT_MAX_NUMBER;
|
||||||
|
plotBoundaries.yMax = DEFAULT_MAX_NUMBER;
|
||||||
|
return plotBoundaries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PreparedInput {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Input input;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Generic expression;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Constant xVariable;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Constant yVariable;
|
||||||
|
|
||||||
|
private boolean fromInputArgs;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private PlotBoundaries plotBoundaries = PlotBoundaries.newDefaultInstance();
|
||||||
|
|
||||||
|
private PreparedInput() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static PreparedInput newInstance(@NotNull Input input,
|
||||||
|
@NotNull Generic expression,
|
||||||
|
@NotNull Constant xVariable,
|
||||||
|
@Nullable Constant yVariable,
|
||||||
|
boolean fromInputArgs,
|
||||||
|
@NotNull PlotBoundaries plotBoundaries) {
|
||||||
|
final PreparedInput result = new PreparedInput();
|
||||||
|
|
||||||
|
result.input = input;
|
||||||
|
result.expression = expression;
|
||||||
|
result.xVariable = xVariable;
|
||||||
|
result.yVariable = yVariable;
|
||||||
|
result.fromInputArgs = fromInputArgs;
|
||||||
|
result.plotBoundaries = plotBoundaries;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static PreparedInput newErrorInstance(boolean fromInputArgs) {
|
||||||
|
final PreparedInput result = new PreparedInput();
|
||||||
|
|
||||||
|
result.input = null;
|
||||||
|
result.expression = null;
|
||||||
|
result.xVariable = null;
|
||||||
|
result.yVariable = null;
|
||||||
|
result.fromInputArgs = fromInputArgs;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFromInputArgs() {
|
||||||
|
return fromInputArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Input getInput() {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Generic getExpression() {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public PlotBoundaries getPlotBoundaries() {
|
||||||
|
return plotBoundaries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Constant getXVariable() {
|
||||||
|
return xVariable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Constant getYVariable() {
|
||||||
|
return yVariable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isError() {
|
||||||
|
return input == null || expression == null || xVariable == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Input implements Serializable {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String expression;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private String xVariableName;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String yVariableName;
|
||||||
|
|
||||||
|
public Input(@NotNull String expression,
|
||||||
|
@NotNull String xVariableName,
|
||||||
|
@Nullable String yVariableName) {
|
||||||
|
this.expression = expression;
|
||||||
|
this.xVariableName = xVariableName;
|
||||||
|
this.yVariableName = yVariableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String getExpression() {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String getXVariableName() {
|
||||||
|
return xVariableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getYVariableName() {
|
||||||
|
return yVariableName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import arity.calculator.Graph2dView;
|
||||||
|
import arity.calculator.Graph3dView;
|
||||||
|
import arity.calculator.GraphView;
|
||||||
|
import jscl.math.Generic;
|
||||||
|
import jscl.math.function.Constant;
|
||||||
|
import org.javia.arity.Complex;
|
||||||
|
import org.javia.arity.Function;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: serso
|
||||||
|
* Date: 12/30/12
|
||||||
|
* Time: 4:43 PM
|
||||||
|
*/
|
||||||
|
public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private GraphView graphView;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected PlotBoundaries getPlotBoundaries() {
|
||||||
|
if ( graphView != null ) {
|
||||||
|
// todo serso: return plot boundaries
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createGraphicalView(@NotNull View root, @NotNull PreparedInput preparedInput) {
|
||||||
|
// remove old
|
||||||
|
final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
|
||||||
|
|
||||||
|
if (graphView instanceof View) {
|
||||||
|
graphContainer.removeView((View) graphView);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preparedInput.isError()) {
|
||||||
|
final Generic expression = preparedInput.getExpression();
|
||||||
|
final Constant xVariable = preparedInput.getXVariable();
|
||||||
|
final Constant yVariable = preparedInput.getYVariable();
|
||||||
|
|
||||||
|
final int arity = yVariable == null ? 1 : 2;
|
||||||
|
|
||||||
|
final List<Function> functions = new ArrayList<Function>();
|
||||||
|
functions.add(new Function() {
|
||||||
|
@Override
|
||||||
|
public int arity() {
|
||||||
|
return arity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double eval(double x) {
|
||||||
|
return PlotUtils.calculatorExpression(expression, xVariable, x).realPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double eval(double x, double y) {
|
||||||
|
return PlotUtils.calculatorExpression(expression, xVariable, x, yVariable, y).realPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Complex eval(Complex x) {
|
||||||
|
jscl.math.numeric.Complex result = PlotUtils.calculatorExpression(expression, xVariable, x.re);
|
||||||
|
return new Complex(result.realPart(), result.imaginaryPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Complex eval(Complex x, Complex y) {
|
||||||
|
jscl.math.numeric.Complex result = PlotUtils.calculatorExpression(expression, xVariable, x.re, yVariable, y.re);
|
||||||
|
return new Complex(result.realPart(), result.imaginaryPart());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (functions.size() == 1) {
|
||||||
|
final Function f = functions.get(0);
|
||||||
|
graphView = f.arity() == 1 ? new Graph2dView(getActivity()) : new Graph3dView(getActivity());
|
||||||
|
graphView.setFunction(f);
|
||||||
|
} else {
|
||||||
|
graphView = new Graph2dView(this.getActivity());
|
||||||
|
((Graph2dView) graphView).setFunctions(functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
graphContainer.addView((View) graphView);
|
||||||
|
} else {
|
||||||
|
onError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createChart(@NotNull PreparedInput preparedInput) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (this.graphView != null) {
|
||||||
|
this.graphView.onResume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onError() {
|
||||||
|
final View root = getView();
|
||||||
|
if (root != null && graphView instanceof View) {
|
||||||
|
final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
|
||||||
|
graphContainer.removeView((View) graphView);
|
||||||
|
}
|
||||||
|
this.graphView = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (this.graphView != null) {
|
||||||
|
this.graphView.onPause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package org.solovyev.android.calculator.plot;
|
|||||||
import android.app.ActionBar;
|
import android.app.ActionBar;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.solovyev.android.calculator.CalculatorFragmentActivity;
|
import org.solovyev.android.calculator.CalculatorFragmentActivity;
|
||||||
import org.solovyev.android.calculator.R;
|
import org.solovyev.android.calculator.R;
|
||||||
@ -29,6 +30,11 @@ public class CalculatorPlotActivity extends CalculatorFragmentActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
|
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
|
||||||
getActivityHelper().setFragment(this, CalculatorFragmentType.plotter, arguments, R.id.main_layout);
|
getActivityHelper().setFragment(this, getPlotterFragmentType(), arguments, R.id.main_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static CalculatorFragmentType getPlotterFragmentType() {
|
||||||
|
return CalculatorFragmentType.plotter_2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,23 +6,12 @@
|
|||||||
|
|
||||||
package org.solovyev.android.calculator.plot;
|
package org.solovyev.android.calculator.plot;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.FragmentActivity;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import com.actionbarsherlock.view.Menu;
|
|
||||||
import com.actionbarsherlock.view.MenuInflater;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
import jscl.math.Expression;
|
|
||||||
import jscl.math.Generic;
|
import jscl.math.Generic;
|
||||||
import jscl.math.function.Constant;
|
import jscl.math.function.Constant;
|
||||||
import jscl.text.ParseException;
|
|
||||||
import org.achartengine.chart.XYChart;
|
import org.achartengine.chart.XYChart;
|
||||||
import org.achartengine.model.XYMultipleSeriesDataset;
|
import org.achartengine.model.XYMultipleSeriesDataset;
|
||||||
import org.achartengine.model.XYSeries;
|
import org.achartengine.model.XYSeries;
|
||||||
@ -32,47 +21,16 @@ import org.achartengine.tools.ZoomEvent;
|
|||||||
import org.achartengine.tools.ZoomListener;
|
import org.achartengine.tools.ZoomListener;
|
||||||
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.CalculatorApplication;
|
|
||||||
import org.solovyev.android.calculator.CalculatorDisplayChangeEventData;
|
|
||||||
import org.solovyev.android.calculator.CalculatorDisplayViewState;
|
|
||||||
import org.solovyev.android.calculator.CalculatorEventData;
|
|
||||||
import org.solovyev.android.calculator.CalculatorEventHolder;
|
|
||||||
import org.solovyev.android.calculator.CalculatorEventListener;
|
|
||||||
import org.solovyev.android.calculator.CalculatorEventType;
|
|
||||||
import org.solovyev.android.calculator.CalculatorFragment;
|
|
||||||
import org.solovyev.android.calculator.CalculatorParseException;
|
|
||||||
import org.solovyev.android.calculator.CalculatorPreferences;
|
import org.solovyev.android.calculator.CalculatorPreferences;
|
||||||
import org.solovyev.android.calculator.CalculatorUtils;
|
|
||||||
import org.solovyev.android.calculator.Locator;
|
|
||||||
import org.solovyev.android.calculator.PreparedExpression;
|
|
||||||
import org.solovyev.android.calculator.R;
|
import org.solovyev.android.calculator.R;
|
||||||
import org.solovyev.android.calculator.ToJsclTextProcessor;
|
|
||||||
import org.solovyev.android.menu.ActivityMenu;
|
|
||||||
import org.solovyev.android.menu.IdentifiableMenuItem;
|
|
||||||
import org.solovyev.android.menu.ListActivityMenu;
|
|
||||||
import org.solovyev.android.sherlock.menu.SherlockMenuHelper;
|
|
||||||
import org.solovyev.common.MutableObject;
|
import org.solovyev.common.MutableObject;
|
||||||
import org.solovyev.common.collections.CollectionsUtils;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: serso
|
* User: serso
|
||||||
* Date: 12/1/11
|
* Date: 12/1/11
|
||||||
* Time: 12:40 AM
|
* Time: 12:40 AM
|
||||||
*/
|
*/
|
||||||
public class CalculatorPlotFragment extends CalculatorFragment implements CalculatorEventListener {
|
public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
|
||||||
|
|
||||||
private static final String TAG = CalculatorPlotFragment.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final int DEFAULT_MIN_NUMBER = -10;
|
|
||||||
|
|
||||||
private static final int DEFAULT_MAX_NUMBER = 10;
|
|
||||||
|
|
||||||
public static final String INPUT = "plotter_input";
|
|
||||||
private static final String PLOT_BOUNDARIES = "plot_boundaries";
|
|
||||||
|
|
||||||
public static final long EVAL_DELAY_MILLIS = 200;
|
public static final long EVAL_DELAY_MILLIS = 200;
|
||||||
|
|
||||||
@ -85,98 +43,7 @@ public class CalculatorPlotFragment extends CalculatorFragment implements Calcul
|
|||||||
@Nullable
|
@Nullable
|
||||||
private MyGraphicalView graphicalView;
|
private MyGraphicalView graphicalView;
|
||||||
|
|
||||||
// thread which calculated data for graph view
|
protected void createChart(@NotNull PreparedInput preparedInput) {
|
||||||
@NotNull
|
|
||||||
private final Executor plotExecutor = Executors.newSingleThreadExecutor();
|
|
||||||
|
|
||||||
// thread for applying UI changes
|
|
||||||
@NotNull
|
|
||||||
private final Handler uiHandler = new Handler();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private PreparedInput preparedInput;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Input input;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final CalculatorEventHolder lastEventHolder = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId());
|
|
||||||
|
|
||||||
private int bgColor;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private ActivityMenu<Menu, MenuItem> fragmentMenu = ListActivityMenu.fromResource(R.menu.plot_menu, PlotMenu.class, SherlockMenuHelper.getInstance());
|
|
||||||
|
|
||||||
public CalculatorPlotFragment() {
|
|
||||||
super(CalculatorApplication.getInstance().createFragmentHelper(R.layout.plot_fragment, R.string.c_graph, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
final Bundle arguments = getArguments();
|
|
||||||
|
|
||||||
if (arguments != null) {
|
|
||||||
input = (Input) arguments.getSerializable(INPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == null) {
|
|
||||||
this.bgColor = getResources().getColor(R.color.cpp_pane_background);
|
|
||||||
} else {
|
|
||||||
this.bgColor = getResources().getColor(android.R.color.transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static PreparedInput prepareInputFromDisplay(@NotNull CalculatorDisplayViewState displayState, @Nullable Bundle savedInstanceState) {
|
|
||||||
try {
|
|
||||||
if (displayState.isValid() && displayState.getResult() != null) {
|
|
||||||
final Generic expression = displayState.getResult();
|
|
||||||
if (CalculatorUtils.isPlotPossible(expression, displayState.getOperation())) {
|
|
||||||
final Constant constant = CollectionsUtils.getFirstCollectionElement(CalculatorUtils.getNotSystemConstants(expression));
|
|
||||||
|
|
||||||
final Input input = new Input(expression.toString(), constant.getName());
|
|
||||||
return prepareInput(input, false, savedInstanceState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Log.e(TAG, e.getLocalizedMessage(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return PreparedInput.newErrorInstance(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static PreparedInput prepareInput(@NotNull Input input, boolean fromInputArgs, @Nullable Bundle savedInstanceState) {
|
|
||||||
PreparedInput result;
|
|
||||||
|
|
||||||
try {
|
|
||||||
final PreparedExpression preparedExpression = ToJsclTextProcessor.getInstance().process(input.getExpression());
|
|
||||||
final Generic expression = Expression.valueOf(preparedExpression.getExpression());
|
|
||||||
final Constant variable = new Constant(input.getVariableName());
|
|
||||||
|
|
||||||
PlotBoundaries plotBoundaries = null;
|
|
||||||
if ( savedInstanceState != null ) {
|
|
||||||
plotBoundaries = (PlotBoundaries)savedInstanceState.getSerializable(PLOT_BOUNDARIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = PreparedInput.newInstance(input, expression, variable, fromInputArgs, plotBoundaries);
|
|
||||||
} catch (ParseException e) {
|
|
||||||
result = PreparedInput.newErrorInstance(fromInputArgs);
|
|
||||||
Locator.getInstance().getNotifier().showMessage(e);
|
|
||||||
} catch (CalculatorParseException e) {
|
|
||||||
result = PreparedInput.newErrorInstance(fromInputArgs);
|
|
||||||
Locator.getInstance().getNotifier().showMessage(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createChart() {
|
|
||||||
if (!preparedInput.isError()) {
|
|
||||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
|
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
|
||||||
final Boolean interpolate = CalculatorPreferences.Graph.interpolate.getPreference(preferences);
|
final Boolean interpolate = CalculatorPreferences.Graph.interpolate.getPreference(preferences);
|
||||||
final GraphLineColor realLineColor = CalculatorPreferences.Graph.lineColorReal.getPreference(preferences);
|
final GraphLineColor realLineColor = CalculatorPreferences.Graph.lineColorReal.getPreference(preferences);
|
||||||
@ -184,54 +51,34 @@ public class CalculatorPlotFragment extends CalculatorFragment implements Calcul
|
|||||||
|
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
try {
|
try {
|
||||||
this.chart = PlotUtils.prepareChart(getMinValue(null), getMaxValue(null), preparedInput.getExpression(), preparedInput.getVariable(), bgColor, interpolate, realLineColor.getColor(), imagLineColor.getColor());
|
this.chart = PlotUtils.prepareChart(getMinValue(null), getMaxValue(null), preparedInput.getExpression(), preparedInput.getXVariable(), getBgColor(), interpolate, realLineColor.getColor(), imagLineColor.getColor());
|
||||||
} catch (ArithmeticException e) {
|
} catch (ArithmeticException e) {
|
||||||
PlotUtils.handleArithmeticException(e, CalculatorPlotFragment.this);
|
PlotUtils.handleArithmeticException(e, CalculatorPlotFragment.this);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
onError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
protected PlotBoundaries getPlotBoundaries() {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
|
|
||||||
if (input == null) {
|
|
||||||
this.preparedInput = prepareInputFromDisplay(Locator.getInstance().getDisplay().getViewState(), savedInstanceState);
|
|
||||||
} else {
|
|
||||||
this.preparedInput = prepareInput(input, true, savedInstanceState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle out) {
|
|
||||||
super.onSaveInstanceState(out);
|
|
||||||
|
|
||||||
if (chart != null) {
|
if (chart != null) {
|
||||||
out.putSerializable(PLOT_BOUNDARIES, new PlotBoundaries(chart.getRenderer()));
|
return new PlotBoundaries(chart.getRenderer());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected void createGraphicalView(@NotNull View root, @Nullable PreparedInput preparedInput) {
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
|
|
||||||
createChart();
|
|
||||||
createGraphicalView(getView(), this.preparedInput.getPlotBoundaries());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createGraphicalView(@NotNull View root, @Nullable PlotBoundaries plotBoundaries) {
|
|
||||||
final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
|
final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
|
||||||
|
|
||||||
if (graphicalView != null) {
|
if (graphicalView != null) {
|
||||||
graphContainer.removeView(graphicalView);
|
graphContainer.removeView(graphicalView);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preparedInput.isError()) {
|
if (!getPreparedInput().isError()) {
|
||||||
final XYChart chart = this.chart;
|
final XYChart chart = this.chart;
|
||||||
assert chart != null;
|
assert chart != null;
|
||||||
|
|
||||||
|
final PlotBoundaries plotBoundaries = preparedInput.getPlotBoundaries();
|
||||||
double minValue = getMinValue(plotBoundaries);
|
double minValue = getMinValue(plotBoundaries);
|
||||||
double maxValue = getMaxValue(plotBoundaries);
|
double maxValue = getMaxValue(plotBoundaries);
|
||||||
|
|
||||||
@ -249,20 +96,20 @@ public class CalculatorPlotFragment extends CalculatorFragment implements Calcul
|
|||||||
maxY = Math.max(maxY, series.getMaxY());
|
maxY = Math.max(maxY, series.getMaxY());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plotBoundaries == null) {
|
if (preparedInput == null) {
|
||||||
chart.getRenderer().setXAxisMin(Math.max(minX, minValue));
|
chart.getRenderer().setXAxisMin(Math.max(minX, minValue));
|
||||||
chart.getRenderer().setYAxisMin(Math.max(minY, minValue));
|
chart.getRenderer().setYAxisMin(Math.max(minY, minValue));
|
||||||
chart.getRenderer().setXAxisMax(Math.min(maxX, maxValue));
|
chart.getRenderer().setXAxisMax(Math.min(maxX, maxValue));
|
||||||
chart.getRenderer().setYAxisMax(Math.min(maxY, maxValue));
|
chart.getRenderer().setYAxisMax(Math.min(maxY, maxValue));
|
||||||
} else {
|
} else {
|
||||||
chart.getRenderer().setXAxisMin(plotBoundaries.xMin);
|
chart.getRenderer().setXAxisMin(plotBoundaries.getXMin());
|
||||||
chart.getRenderer().setYAxisMin(plotBoundaries.yMin);
|
chart.getRenderer().setYAxisMin(plotBoundaries.getYMin());
|
||||||
chart.getRenderer().setXAxisMax(plotBoundaries.xMax);
|
chart.getRenderer().setXAxisMax(plotBoundaries.getXMax());
|
||||||
chart.getRenderer().setYAxisMax(plotBoundaries.yMax);
|
chart.getRenderer().setYAxisMax(plotBoundaries.getYMax());
|
||||||
}
|
}
|
||||||
|
|
||||||
graphicalView = new MyGraphicalView(this.getActivity(), chart);
|
graphicalView = new MyGraphicalView(this.getActivity(), chart);
|
||||||
graphicalView.setBackgroundColor(this.bgColor);
|
graphicalView.setBackgroundColor(this.getBgColor());
|
||||||
|
|
||||||
graphicalView.addZoomListener(new ZoomListener() {
|
graphicalView.addZoomListener(new ZoomListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -292,14 +139,6 @@ public class CalculatorPlotFragment extends CalculatorFragment implements Calcul
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getMaxValue(@Nullable PlotBoundaries plotBoundaries) {
|
|
||||||
return plotBoundaries == null ? DEFAULT_MAX_NUMBER : plotBoundaries.xMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
private double getMinValue(@Nullable PlotBoundaries plotBoundaries) {
|
|
||||||
return plotBoundaries == null ? DEFAULT_MIN_NUMBER : plotBoundaries.xMin;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void updateDataSets(@NotNull final XYChart chart) {
|
private void updateDataSets(@NotNull final XYChart chart) {
|
||||||
updateDataSets(chart, EVAL_DELAY_MILLIS);
|
updateDataSets(chart, EVAL_DELAY_MILLIS);
|
||||||
@ -309,10 +148,13 @@ public class CalculatorPlotFragment extends CalculatorFragment implements Calcul
|
|||||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
|
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
|
||||||
final GraphLineColor imagLineColor = CalculatorPreferences.Graph.lineColorImag.getPreference(preferences);
|
final GraphLineColor imagLineColor = CalculatorPreferences.Graph.lineColorImag.getPreference(preferences);
|
||||||
|
|
||||||
final Generic expression = preparedInput.getExpression();
|
final PreparedInput preparedInput = getPreparedInput();
|
||||||
final Constant variable = preparedInput.getVariable();
|
|
||||||
|
|
||||||
if (expression != null && variable != null) {
|
final Generic expression = preparedInput.getExpression();
|
||||||
|
final Constant variable = preparedInput.getXVariable();
|
||||||
|
final MyGraphicalView graphicalView = this.graphicalView;
|
||||||
|
|
||||||
|
if (expression != null && variable != null && graphicalView != null) {
|
||||||
pendingOperation.setObject(new Runnable() {
|
pendingOperation.setObject(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -321,7 +163,7 @@ public class CalculatorPlotFragment extends CalculatorFragment implements Calcul
|
|||||||
//lock all operations with history
|
//lock all operations with history
|
||||||
if (pendingOperation.getObject() == this) {
|
if (pendingOperation.getObject() == this) {
|
||||||
|
|
||||||
plotExecutor.execute(new Runnable() {
|
getPlotExecutor().execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final XYMultipleSeriesRenderer dr = chart.getRenderer();
|
final XYMultipleSeriesRenderer dr = chart.getRenderer();
|
||||||
@ -349,7 +191,7 @@ public class CalculatorPlotFragment extends CalculatorFragment implements Calcul
|
|||||||
PlotUtils.handleArithmeticException(e, CalculatorPlotFragment.this);
|
PlotUtils.handleArithmeticException(e, CalculatorPlotFragment.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
uiHandler.post(new Runnable() {
|
getUiHandler().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
graphicalView.repaint();
|
graphicalView.repaint();
|
||||||
@ -366,37 +208,12 @@ public class CalculatorPlotFragment extends CalculatorFragment implements Calcul
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uiHandler.postDelayed(pendingOperation.getObject(), millisToWait);
|
getUiHandler().postDelayed(pendingOperation.getObject(), millisToWait);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
|
private final MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable final Object data) {
|
|
||||||
if (calculatorEventType.isOfType(CalculatorEventType.display_state_changed)) {
|
|
||||||
if (!preparedInput.isFromInputArgs()) {
|
|
||||||
|
|
||||||
final CalculatorEventHolder.Result result = this.lastEventHolder.apply(calculatorEventData);
|
|
||||||
if (result.isNewAfter()) {
|
|
||||||
this.preparedInput = prepareInputFromDisplay(((CalculatorDisplayChangeEventData) data).getNewValue(), null);
|
|
||||||
createChart();
|
|
||||||
|
|
||||||
uiHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
final View view = getView();
|
|
||||||
if (view != null) {
|
|
||||||
createGraphicalView(view, preparedInput.getPlotBoundaries());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* public void zoomInClickHandler(@NotNull View v) {
|
/* public void zoomInClickHandler(@NotNull View v) {
|
||||||
this.graphicalView.zoomIn();
|
this.graphicalView.zoomIn();
|
||||||
@ -406,196 +223,8 @@ public class CalculatorPlotFragment extends CalculatorFragment implements Calcul
|
|||||||
this.graphicalView.zoomOut();
|
this.graphicalView.zoomOut();
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
/*
|
|
||||||
**********************************************************************
|
|
||||||
*
|
|
||||||
* MENU
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
|
|
||||||
final FragmentActivity activity = this.getActivity();
|
|
||||||
if (activity != null) {
|
|
||||||
fragmentMenu.onCreateOptionsMenu(activity, menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrepareOptionsMenu(Menu menu) {
|
|
||||||
super.onPrepareOptionsMenu(menu);
|
|
||||||
|
|
||||||
final FragmentActivity activity = this.getActivity();
|
|
||||||
if (activity != null) {
|
|
||||||
fragmentMenu.onPrepareOptionsMenu(activity, menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
return super.onOptionsItemSelected(item) || fragmentMenu.onOptionsItemSelected(this.getActivity(), item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onError() {
|
public void onError() {
|
||||||
this.chart = null;
|
this.chart = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
**********************************************************************
|
|
||||||
*
|
|
||||||
* STATIC
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
private static enum PlotMenu implements IdentifiableMenuItem<MenuItem> {
|
|
||||||
|
|
||||||
preferences(R.id.menu_plot_settings) {
|
|
||||||
@Override
|
|
||||||
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
|
|
||||||
context.startActivity(new Intent(context, CalculatorPlotPreferenceActivity.class));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final int itemId;
|
|
||||||
|
|
||||||
private PlotMenu(int itemId) {
|
|
||||||
this.itemId = itemId;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Integer getItemId() {
|
|
||||||
return itemId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class PlotBoundaries implements Serializable {
|
|
||||||
|
|
||||||
private double xMin;
|
|
||||||
private double xMax;
|
|
||||||
private double yMin;
|
|
||||||
private double yMax;
|
|
||||||
|
|
||||||
public PlotBoundaries() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlotBoundaries(@NotNull XYMultipleSeriesRenderer renderer) {
|
|
||||||
this.xMin = renderer.getXAxisMin();
|
|
||||||
this.yMin = renderer.getYAxisMin();
|
|
||||||
this.xMax = renderer.getXAxisMax();
|
|
||||||
this.yMax = renderer.getYAxisMax();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "PlotBoundaries{" +
|
|
||||||
"yMax=" + yMax +
|
|
||||||
", yMin=" + yMin +
|
|
||||||
", xMax=" + xMax +
|
|
||||||
", xMin=" + xMin +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PreparedInput {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Input input;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Generic expression;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Constant variable;
|
|
||||||
|
|
||||||
private boolean fromInputArgs;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private PlotBoundaries plotBoundaries = null;
|
|
||||||
|
|
||||||
private PreparedInput() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static PreparedInput newInstance(@NotNull Input input, @NotNull Generic expression, @NotNull Constant variable, boolean fromInputArgs, @Nullable PlotBoundaries plotBoundaries) {
|
|
||||||
final PreparedInput result = new PreparedInput();
|
|
||||||
|
|
||||||
result.input = input;
|
|
||||||
result.expression = expression;
|
|
||||||
result.variable = variable;
|
|
||||||
result.fromInputArgs = fromInputArgs;
|
|
||||||
result.plotBoundaries = plotBoundaries;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static PreparedInput newErrorInstance(boolean fromInputArgs) {
|
|
||||||
final PreparedInput result = new PreparedInput();
|
|
||||||
|
|
||||||
result.input = null;
|
|
||||||
result.expression = null;
|
|
||||||
result.variable = null;
|
|
||||||
result.fromInputArgs = fromInputArgs;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFromInputArgs() {
|
|
||||||
return fromInputArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Input getInput() {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Generic getExpression() {
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public PlotBoundaries getPlotBoundaries() {
|
|
||||||
return plotBoundaries;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Constant getVariable() {
|
|
||||||
return variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isError() {
|
|
||||||
return input == null || expression == null || variable == null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Input implements Serializable {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private String expression;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private String variableName;
|
|
||||||
|
|
||||||
public Input(@NotNull String expression, @NotNull String variableName) {
|
|
||||||
this.expression = expression;
|
|
||||||
this.variableName = variableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getExpression() {
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getVariableName() {
|
|
||||||
return variableName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ public final class PlotUtils {
|
|||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleArithmeticException(@NotNull ArithmeticException e, @NotNull CalculatorPlotFragment calculatorPlotFragment) {
|
static void handleArithmeticException(@NotNull ArithmeticException e, @NotNull AbstractCalculatorPlotFragment calculatorPlotFragment) {
|
||||||
String message = e.getLocalizedMessage();
|
String message = e.getLocalizedMessage();
|
||||||
if (StringUtils.isEmpty(message)) {
|
if (StringUtils.isEmpty(message)) {
|
||||||
message = e.getMessage();
|
message = e.getMessage();
|
||||||
@ -371,9 +371,20 @@ public final class PlotUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant variable, double x) {
|
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant xVar, double x) {
|
||||||
try {
|
try {
|
||||||
return unwrap(expression.substitute(variable, Expression.valueOf(x)).numeric());
|
return unwrap(expression.substitute(xVar, Expression.valueOf(x)).numeric());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant xVar, double x, @NotNull Constant yVar, double y) {
|
||||||
|
try {
|
||||||
|
Generic tmp = expression.substitute(xVar, Expression.valueOf(x));
|
||||||
|
tmp = tmp.substitute(yVar, Expression.valueOf(y));
|
||||||
|
return unwrap(tmp.numeric());
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
return NaN;
|
return NaN;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,8 @@ public final class CalculatorUtils {
|
|||||||
boolean result = false;
|
boolean result = false;
|
||||||
|
|
||||||
if (operation == JsclOperation.simplify) {
|
if (operation == JsclOperation.simplify) {
|
||||||
if (getNotSystemConstants(expression).size() == 1) {
|
int size = getNotSystemConstants(expression).size();
|
||||||
|
if (size == 1 || size == 2) {
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user