WarpPI/src/main/java/org/warp/picalculator/gui/screens/MathInputScreen.java

677 lines
21 KiB
Java
Raw Normal View History

package org.warp.picalculator.gui.screens;
2016-09-02 20:32:37 +02:00
import java.io.IOException;
2016-09-02 20:32:37 +02:00
import java.io.PrintWriter;
import java.io.StringWriter;
2017-01-16 17:57:09 +01:00
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
2016-09-02 20:32:37 +02:00
import org.warp.picalculator.Error;
import org.warp.picalculator.Errors;
2016-09-02 20:32:37 +02:00
import org.warp.picalculator.Utils;
import org.warp.picalculator.device.Keyboard;
import org.warp.picalculator.device.Keyboard.Key;
import org.warp.picalculator.gui.DisplayManager;
import org.warp.picalculator.gui.expression.InputContext;
2017-04-12 13:04:35 +02:00
import org.warp.picalculator.gui.expression.blocks.Block;
import org.warp.picalculator.gui.expression.blocks.BlockContainer;
import org.warp.picalculator.gui.expression.containers.InputContainer;
import org.warp.picalculator.gui.expression.containers.NormalInputContainer;
import org.warp.picalculator.gui.expression.containers.NormalOutputContainer;
import org.warp.picalculator.gui.expression.containers.OutputContainer;
import org.warp.picalculator.gui.graphicengine.BinaryFont;
import org.warp.picalculator.gui.graphicengine.Renderer;
2017-01-16 17:57:09 +01:00
import org.warp.picalculator.math.AngleMode;
import org.warp.picalculator.math.Function;
import org.warp.picalculator.math.FunctionDynamic;
import org.warp.picalculator.math.FunctionOperator;
import org.warp.picalculator.math.FunctionSingle;
import org.warp.picalculator.math.MathContext;
import org.warp.picalculator.math.MathematicalSymbols;
import org.warp.picalculator.math.functions.Expression;
import org.warp.picalculator.math.functions.Number;
2017-01-16 17:57:09 +01:00
import org.warp.picalculator.math.functions.Variable;
import org.warp.picalculator.math.functions.Variable.VariableValue;
import org.warp.picalculator.math.functions.equations.Equation;
import org.warp.picalculator.math.parser.MathParser;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
2016-11-02 21:56:40 +01:00
2016-12-04 22:22:16 +01:00
public class MathInputScreen extends Screen {
2016-09-02 20:32:37 +02:00
public MathContext calc;
public InputContext ic;
public InputContainer userInput;
public OutputContainer result;
2016-09-02 20:32:37 +02:00
public int errorLevel = 0; // 0 = nessuno, 1 = risultato, 2 = tutto
boolean mustRefresh = true;
2016-12-04 22:22:16 +01:00
public MathInputScreen() {
2016-09-02 20:32:37 +02:00
super();
canBeInHistory = true;
}
2016-09-02 20:32:37 +02:00
@Override
public void created() throws InterruptedException {
ic = new InputContext();
calc = new MathContext();
try {
BlockContainer.initializeFonts(DisplayManager.engine.loadFont("ex"), DisplayManager.engine.loadFont("big"));
} catch (final IOException e) {
e.printStackTrace();
System.exit(1);
}
userInput = new NormalInputContainer(ic);
result = new NormalOutputContainer();
calc.init();
2016-09-02 20:32:37 +02:00
}
@Override
public void init() throws InterruptedException {
2016-10-02 16:01:41 +02:00
/* Fine caricamento */
2016-09-02 20:32:37 +02:00
}
2016-09-02 20:32:37 +02:00
@Override
public void beforeRender(float dt) {
if (DisplayManager.error == null) {
DisplayManager.renderer.glClearColor(0xFFc5c2af);
} else {
DisplayManager.renderer.glClearColor(0xFFDC3C32);
}
if (userInput.beforeRender(dt)) {
mustRefresh = true;
}
2016-09-02 20:32:37 +02:00
}
private static final BinaryFont fontBig = Utils.getFont(false);
@Override
public void renderStatusbar() {
final Renderer renderer = DisplayManager.renderer;
renderer.glColor3f(1, 1, 1);
final int pos = 2;
final int spacersNumb = 1;
int skinN = 0;
if (calc.exactMode) {
skinN = 22;
} else {
skinN = 21;
}
renderer.glFillRect(2 + 18 * pos + 2 * spacersNumb, 2, 16, 16, 16 * skinN, 16 * 0, 16, 16);
}
2016-09-02 20:32:37 +02:00
@Override
public void render() {
fontBig.use(DisplayManager.engine);
final int textColor = 0xFF000000;
final int padding = 4;
DisplayManager.renderer.glColor(textColor);
userInput.draw(DisplayManager.engine, DisplayManager.renderer, padding, padding + 20);
2017-06-05 22:50:33 +02:00
if (!result.isContentEmpty()) {
result.draw(DisplayManager.engine, DisplayManager.renderer, DisplayManager.engine.getWidth() - result.getWidth() - 2, DisplayManager.engine.getHeight() - result.getHeight() - 2);
}
2016-09-02 20:32:37 +02:00
}
@Override
public boolean mustBeRefreshed() {
if (mustRefresh) {
mustRefresh = false;
return true;
} else {
return false;
}
}
@Override
public boolean keyPressed(Key k) {
Utils.debug.println(k.toString());
2016-09-02 20:32:37 +02:00
switch (k) {
case OK:
userInput.toggleExtra();
mustRefresh = true;
2016-09-02 20:32:37 +02:00
return true;
case HISTORY_BACK:
if (userInput.isExtraOpened()) {
userInput.closeExtra();
mustRefresh = true;
2017-01-16 17:57:09 +01:00
return true;
}
default:
if (userInput.isExtraOpened() && userInput.getExtraKeyboardEventListener().keyPressed(k)) {
2017-01-16 17:57:09 +01:00
return true;
} else {
switch (k) {
case STEP:
// if (newExpression.length() > 0) {
// if (firstStep) {
// try {
// try {
// interpreta(true);
// showVariablesDialog(() -> {
// currentExpression = newExpression;
// calc.f2 = calc.f;
// firstStep = false;
// step();
// });
// } catch (final Exception ex) {
// if (Utils.debugOn) {
// ex.printStackTrace();
// }
// throw new Error(Errors.SYNTAX_ERROR);
// }
// } catch (final Error e) {
// final StringWriter sw = new StringWriter();
// final PrintWriter pw = new PrintWriter(sw);
// e.printStackTrace(pw);
// d.errorStackTrace = sw.toString().toUpperCase().replace("\t", " ").replace("\r", "").split("\n");
// DisplayManager.error = e.id.toString();
// System.err.println(e.id);
// }
// } else {
// step();
// }
// }
// return true;
case SIMPLIFY:
if (DisplayManager.error != null) {
//TODO: make the error management a global API rather than being relegated to this screen.
Utils.debug.println("Resetting after error...");
DisplayManager.error = null;
calc.f = null;
calc.f2 = null;
calc.resultsCount = 0;
return true;
} else {
try {
try {
if (!userInput.isAlreadyParsed() && !userInput.isEmpty()) {
Expression expr = MathParser.parseInput(calc, userInput);
if (calc.f == null | calc.f2 == null) {
calc.f = new ObjectArrayList<>();
calc.f2 = new ObjectArrayList<>();
} else {
calc.f.clear();
calc.f2.clear();
}
calc.f.add(expr);
2017-06-05 22:50:33 +02:00
int stop = 0;
boolean done = false;
ObjectArrayList<ObjectArrayList<Function>> resultExpressions = new ObjectArrayList<>();
resultExpressions.add(new ObjectArrayList<Function>(expr.getParameters()));
while (!done && stop < 3000) {
ObjectArrayList<ObjectArrayList<Function>> newResultExpressions = new ObjectArrayList<>();
done = true;
for (ObjectArrayList<Function> resultExpr : resultExpressions) {
ObjectArrayList<Function> newResults = new ObjectArrayList<>();
for (Function f : resultExpr) {
if (f.isSimplified() == false) {
done = false;
if (f instanceof Expression) {
ObjectArrayList<Function> fncResult = ((Expression)f).solve();
for (Function resultItem : fncResult) {
newResultExpressions.add(new ObjectArrayList<Function>(new Function[] {resultItem}));
}
} else {
List<Function> fncResult = f.simplify();
for (Function resultItem : fncResult) {
newResultExpressions.add(new ObjectArrayList<Function>(new Function[] {resultItem}));
}
}
} else {
newResults.add(f);
}
}
if (newResults.isEmpty() == false) {
newResultExpressions.add(newResults);
}
}
resultExpressions = newResultExpressions;
stop++;
}
ObjectArrayList<ObjectArrayList<Block>> resultBlocks = MathParser.parseOutput(calc, resultExpressions);
result.setContentAsMultipleGroups(resultBlocks);
// showVariablesDialog(() -> {
// currentExpression = newExpression;
// simplify();
// });
}
} catch (final Exception ex) {
if (Utils.debugOn) {
ex.printStackTrace();
}
throw new Error(Errors.SYNTAX_ERROR);
}
} catch (final Error e) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
d.errorStackTrace = sw.toString().toUpperCase().replace("\t", " ").replace("\r", "").split("\n");
DisplayManager.error = e.id.toString();
System.err.println(e.id);
}
return true;
}
case NUM0:
typeChar('0');
return true;
case NUM1:
typeChar('1');
return true;
case NUM2:
typeChar('2');
return true;
case NUM3:
typeChar('3');
return true;
case NUM4:
typeChar('4');
return true;
case NUM5:
typeChar('5');
return true;
case NUM6:
typeChar('6');
return true;
case NUM7:
typeChar('7');
return true;
case NUM8:
typeChar('8');
return true;
case NUM9:
typeChar('9');
return true;
case PLUS:
typeChar('+');
return true;
case MINUS:
typeChar('-');
return true;
case PLUS_MINUS:
typeChar('±');
return true;
case MULTIPLY:
typeChar('*');
return true;
case DIVIDE:
typeChar('/');
return true;
case PARENTHESIS_OPEN:
typeChar('(');
return true;
case PARENTHESIS_CLOSE:
typeChar(')');
return true;
case DOT:
typeChar('.');
return true;
case EQUAL:
typeChar('=');
return true;
case SQRT:
typeChar('â’¶');
return true;
case ROOT:
typeChar('√');
return true;
case POWER_OF_2:
typeChar(MathematicalSymbols.POWER_OF_TWO);
return true;
case POWER_OF_x:
typeChar(MathematicalSymbols.POWER);
return true;
case PI:
typeChar(MathematicalSymbols.PI);
return true;
case LETTER_X:
typeChar(MathematicalSymbols.variables[23]);
return true;
case LETTER_Y:
typeChar(MathematicalSymbols.variables[24]);
return true;
case SINE:
typeChar(MathematicalSymbols.SINE);
return true;
case COSINE:
typeChar(MathematicalSymbols.COSINE);
return true;
case TANGENT:
typeChar(MathematicalSymbols.TANGENT);
return true;
case ARCSINE:
typeChar(MathematicalSymbols.ARC_SINE);
return true;
case ARCCOSINE:
typeChar(MathematicalSymbols.ARC_COSINE);
return true;
case ARCTANGENT:
typeChar(MathematicalSymbols.ARC_TANGENT);
return true;
case DELETE:
userInput.del();
mustRefresh = true;
return true;
case LEFT:
userInput.moveLeft();
mustRefresh = true;
return true;
case RIGHT:
userInput.moveRight();
mustRefresh = true;
return true;
case RESET:
userInput.clear();
result.clear();
if (DisplayManager.error != null) {
Utils.debug.println("Resetting after error...");
DisplayManager.error = null;
}
return true;
case SURD_MODE:
calc.exactMode = !calc.exactMode;
if (calc.exactMode == false) {
calc.f2 = solveExpression(calc.f2);
} else {
result.clear();
Keyboard.keyPressed(Key.SIMPLIFY);
}
return true;
case debug1:
DisplayManager.INSTANCE.setScreen(new EmptyScreen());
return true;
case HISTORY_BACK:
// if (DisplayManager.INSTANCE.canGoBack()) {
// if (currentExpression != null && currentExpression.length() > 0 & DisplayManager.sessions[DisplayManager.currentSession + 1] instanceof MathInputScreen) {
// newExpression = currentExpression;
// try {
// interpreta(true);
// } catch (final Error e) {}
// }
// }
return false;
case HISTORY_FORWARD:
// if (DisplayManager.INSTANCE.canGoForward()) {
// if (currentExpression != null && currentExpression.length() > 0 & DisplayManager.sessions[DisplayManager.currentSession - 1] instanceof MathInputScreen) {
// newExpression = currentExpression;
// try {
// interpreta(true);
// } catch (final Error e) {}
// }
// }
return false;
case debug_DEG:
if (calc.angleMode.equals(AngleMode.DEG) == false) {
calc.angleMode = AngleMode.DEG;
return true;
}
return false;
case debug_RAD:
if (calc.angleMode.equals(AngleMode.RAD) == false) {
calc.angleMode = AngleMode.RAD;
return true;
}
return false;
case debug_GRA:
if (calc.angleMode.equals(AngleMode.GRA) == false) {
calc.angleMode = AngleMode.GRA;
return true;
}
return false;
case DRG_CYCLE:
if (calc.angleMode.equals(AngleMode.DEG) == true) {
calc.angleMode = AngleMode.RAD;
} else if (calc.angleMode.equals(AngleMode.RAD) == true) {
calc.angleMode = AngleMode.GRA;
} else {
calc.angleMode = AngleMode.DEG;
}
return true;
default:
return false;
}
2017-01-16 17:57:09 +01:00
}
2016-09-02 20:32:37 +02:00
}
}
private ObjectArrayList<Function> solveExpression(ObjectArrayList<Function> f22) {
2017-01-16 17:57:09 +01:00
try {
try {
return calc.solveExpression(f22);
} catch (final Exception ex) {
2017-01-16 17:57:09 +01:00
if (Utils.debugOn) {
ex.printStackTrace();
}
throw new Error(Errors.SYNTAX_ERROR);
}
} catch (final Error e) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
2017-01-16 17:57:09 +01:00
e.printStackTrace(pw);
d.errorStackTrace = sw.toString().toUpperCase().replace("\t", " ").replace("\r", "").split("\n");
DisplayManager.error = e.id.toString();
2017-01-16 17:57:09 +01:00
System.err.println(e.id);
}
return null;
}
protected void step() {
2017-01-16 17:57:09 +01:00
try {
try {
showVariablesDialog();
ObjectArrayList<Function> results = new ObjectArrayList<>();
final ObjectArrayList<Function> partialResults = new ObjectArrayList<>();
for (final Function f : calc.f2) {
2017-01-16 17:57:09 +01:00
if (f instanceof Equation) {
DisplayManager.INSTANCE.setScreen(new SolveEquationScreen(this));
2017-01-16 17:57:09 +01:00
} else {
results.add(f);
for (final Function itm : results) {
if (itm.isSimplified() == false) {
final List<Function> dt = itm.simplify();
2017-01-16 17:57:09 +01:00
partialResults.addAll(dt);
} else {
partialResults.add(itm);
}
}
results = new ObjectArrayList<>(partialResults);
2017-01-16 17:57:09 +01:00
partialResults.clear();
}
}
2017-01-16 17:57:09 +01:00
if (results.size() == 0) {
2017-01-17 22:32:40 +01:00
calc.resultsCount = 0;
2017-01-16 17:57:09 +01:00
} else {
2017-01-17 22:32:40 +01:00
calc.resultsCount = results.size();
2017-01-16 17:57:09 +01:00
Collections.reverse(results);
// add elements to al, including duplicates
final Set<Function> hs = new LinkedHashSet<>();
2017-01-16 17:57:09 +01:00
hs.addAll(results);
results.clear();
results.addAll(hs);
2017-01-17 22:32:40 +01:00
calc.f2 = results;
2017-01-16 17:57:09 +01:00
}
2017-02-02 12:49:31 +01:00
Utils.debug.println(calc.f2.toString());
} catch (final Exception ex) {
2017-01-16 17:57:09 +01:00
if (Utils.debugOn) {
ex.printStackTrace();
}
throw new Error(Errors.SYNTAX_ERROR);
}
} catch (final Error e) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
2017-01-16 17:57:09 +01:00
e.printStackTrace(pw);
d.errorStackTrace = sw.toString().toUpperCase().replace("\t", " ").replace("\r", "").split("\n");
DisplayManager.error = e.id.toString();
2017-01-16 17:57:09 +01:00
System.err.println(e.id);
}
}
protected void simplify() {
2017-01-16 17:57:09 +01:00
try {
try {
for (final Function f : calc.f) {
2017-01-16 17:57:09 +01:00
if (f instanceof Equation) {
DisplayManager.INSTANCE.setScreen(new SolveEquationScreen(this));
2017-01-16 17:57:09 +01:00
return;
}
}
final ObjectArrayList<Function> results = solveExpression(calc.f);
2017-01-16 17:57:09 +01:00
if (results.size() == 0) {
2017-01-17 22:32:40 +01:00
calc.resultsCount = 0;
2017-01-16 17:57:09 +01:00
} else {
2017-01-17 22:32:40 +01:00
calc.resultsCount = results.size();
2017-01-16 17:57:09 +01:00
Collections.reverse(results);
// add elements to al, including duplicates
final Set<Function> hs = new LinkedHashSet<>();
2017-01-16 17:57:09 +01:00
hs.addAll(results);
results.clear();
results.addAll(hs);
2017-01-17 22:32:40 +01:00
calc.f2 = results;
2017-01-16 17:57:09 +01:00
}
} catch (final Exception ex) {
2017-01-16 17:57:09 +01:00
if (Utils.debugOn) {
ex.printStackTrace();
}
throw new Error(Errors.SYNTAX_ERROR);
}
} catch (final Error e) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
2017-01-16 17:57:09 +01:00
e.printStackTrace(pw);
d.errorStackTrace = sw.toString().toUpperCase().replace("\t", " ").replace("\r", "").split("\n");
DisplayManager.error = e.id.toString();
2017-01-16 17:57:09 +01:00
System.err.println(e.id);
}
}
@SuppressWarnings("unused")
@Deprecated
2016-11-02 21:56:40 +01:00
private void changeEquationScreen() {
throw new UnsupportedOperationException();
//
// if (!userInput.isEmpty()) {
// final MathInputScreen cloned = clone();
// cloned.userInput.setCaretPosition(cloned.userInput.getCaretMaxPosition()-1);
// DisplayManager.INSTANCE.replaceScreen(cloned);
// initialized = false;
// DisplayManager.INSTANCE.setScreen(this);
//
// }
2016-11-02 21:56:40 +01:00
}
public void typeChar(char chr) {
userInput.typeChar(chr);
mustRefresh = true;
2016-09-02 20:32:37 +02:00
}
2016-09-02 20:32:37 +02:00
@Override
public boolean keyReleased(Key k) {
if (k == Key.OK) {
return true;
} else {
if (userInput.isExtraOpened() && userInput.getExtraKeyboardEventListener().keyReleased(k)) {
return true;
} else {
switch (k) {
default:
return false;
}
}
}
2016-09-02 20:32:37 +02:00
}
2017-01-17 22:32:40 +01:00
public void showVariablesDialog() {
showVariablesDialog(null);
}
2017-01-16 17:57:09 +01:00
public void showVariablesDialog(final Runnable runnable) {
final Thread ct = new Thread(() -> {
final ObjectArrayList<Function> knownVarsInFunctions = getKnownVariables(calc.f.toArray(new Function[calc.f.size()]));
for (final VariableValue f : calc.variablesValues) {
if (knownVarsInFunctions.contains(f.v)) {
knownVarsInFunctions.remove(f.v);
2017-01-16 17:57:09 +01:00
}
}
2017-01-16 17:57:09 +01:00
boolean cancelled = false;
for (final Function f : knownVarsInFunctions) {
final ChooseVariableValueScreen cvs = new ChooseVariableValueScreen(this, new VariableValue((Variable) f, new Number(calc, 0)));
DisplayManager.INSTANCE.setScreen(cvs);
2017-01-16 17:57:09 +01:00
try {
while (DisplayManager.screen == cvs) {
2017-01-16 17:57:09 +01:00
Utils.debug.println(Thread.currentThread().getName());
Thread.sleep(200);
}
} catch (final InterruptedException e) {}
2017-01-16 17:57:09 +01:00
if (cvs.resultNumberValue == null) {
cancelled = true;
break;
} else {
2017-01-17 22:32:40 +01:00
final int is = calc.variablesValues.size();
2017-01-16 17:57:09 +01:00
for (int i = 0; i < is; i++) {
2017-01-17 22:32:40 +01:00
if (calc.variablesValues.get(i).v == f) {
calc.variablesValues.remove(i);
2017-01-16 17:57:09 +01:00
}
}
2017-01-17 22:32:40 +01:00
calc.variablesValues.add(new VariableValue((Variable) f, (Number) cvs.resultNumberValue));
2017-01-16 17:57:09 +01:00
}
}
if (!cancelled) {
2017-01-17 22:32:40 +01:00
if (runnable != null) {
runnable.run();
}
2017-01-16 17:57:09 +01:00
}
});
ct.setName("Variables user-input queue thread");
ct.setPriority(Thread.MIN_PRIORITY);
ct.setDaemon(true);
ct.start();
}
private ObjectArrayList<Function> getKnownVariables(Function[] fncs) {
final ObjectArrayList<Function> res = new ObjectArrayList<>();
for (final Function f : fncs) {
if (f instanceof FunctionOperator) {
res.addAll(getKnownVariables(new Function[] { ((FunctionOperator) f).getParameter1(), ((FunctionOperator) f).getParameter2() }));
} else if (f instanceof FunctionDynamic) {
res.addAll(getKnownVariables(((FunctionDynamic) f).getParameters()));
} else if (f instanceof FunctionSingle) {
res.addAll(getKnownVariables(new Function[] { ((FunctionSingle) f).getParameter() }));
2017-01-16 17:57:09 +01:00
} else if (f instanceof Variable) {
if (((Variable) f).getType() == Variable.V_TYPE.COEFFICIENT) {
if (!res.contains(f)) {
res.add(f);
}
2017-01-16 17:57:09 +01:00
}
}
}
return res;
}
2016-11-02 21:56:40 +01:00
@Override
@Deprecated
2016-12-04 22:22:16 +01:00
public MathInputScreen clone() {
throw new UnsupportedOperationException();
// final MathInputScreen es = this;
// final MathInputScreen es2 = new MathInputScreen();
// es2.errorLevel = es.errorLevel;
// es2.mustRefresh = es.mustRefresh;
// es2.calc = Utils.cloner.deepClone(es.calc);
// es2.userInput = Utils.cloner.deepClone(es.userInput);
// es2.result = Utils.cloner.deepClone(es.result);
// return es2;
2016-11-02 21:56:40 +01:00
}
2016-09-02 20:32:37 +02:00
}