package com.numix.calculator; import java.util.ArrayList; import java.util.List; import org.achartengine.GraphicalView; import org.achartengine.model.XYSeries; import org.achartengine.util.MathHelper; import org.javia.arity.SyntaxException; import android.os.AsyncTask; public class GraphModule { Logic mLogic; GraphModule(Logic logic) { this.mLogic = logic; } void updateGraphCatchErrors(Graph g) { try { updateGraph(g); } catch(Exception e) { e.printStackTrace(); } } void updateGraph(final Graph g) { if(g == null) return; final String eq = mLogic.getText(); if(eq.isEmpty()) { XYSeries series = new XYSeries(""); try { clearGraph(g); g.getDataset().addSeries(series); } catch(NullPointerException e) { e.printStackTrace(); } if(mLogic.mGraphDisplay != null) mLogic.mGraphDisplay.repaint(); return; } if(Logic.isOperator(eq.charAt(eq.length() - 1)) || mLogic.displayContainsMatrices() || eq.endsWith("(")) return; new GraphTask(g, mLogic).execute(eq); } private static void clearGraph(Graph g) { int seriesCount = g.getDataset().getSeriesCount(); for(int i = 0; i < seriesCount; i++) { g.getDataset().removeSeries(0); } } public static class GraphTask extends AsyncTask<String, String, GraphicalView> { private final Graph mGraph; private final Logic mLogic; public GraphTask(Graph graph, Logic logic) { mGraph = graph; mLogic = logic; } @Override protected GraphicalView doInBackground(String... eq) { final String[] equation = eq[0].split("="); if(equation.length != 2) return null; // Translate into decimal equation[0] = mLogic.convertToDecimal(mLogic.localize(equation[0])); equation[1] = mLogic.convertToDecimal(mLogic.localize(equation[1])); final double minY = mGraph.getRenderer().getYAxisMin(); final double maxY = mGraph.getRenderer().getYAxisMax(); final double minX = mGraph.getRenderer().getXAxisMin(); final double maxX = mGraph.getRenderer().getXAxisMax(); try { final List<XYSeries> series = new ArrayList<XYSeries>(); series.add(new XYSeries("")); double lastX = (maxX - minX) / 2 + minX; double lastY = (maxY - minY) / 2 + minY; if(equation[0].equals(mLogic.mY) && !equation[1].contains(mLogic.mY)) { for(double x = minX; x <= maxX; x += (0.00125 * (maxX - minX))) { if(graphChanged(mGraph, eq[0], minX, maxX, minY, maxY)) return null; try { mLogic.mSymbols.define(mLogic.mX, x); double y = mLogic.mSymbols.eval(equation[1]); if(pointIsNaN(lastY, y, maxY, minY)) { series.get(0).add(x, MathHelper.NULL_VALUE); } else { series.get(0).add(x, y); } lastY = y; } catch(SyntaxException e) { e.printStackTrace(); } } } else if(equation[0].equals(mLogic.mX) && !equation[1].contains(mLogic.mX)) { for(double y = minY; y <= maxY; y += (0.00125 * (maxY - minY))) { if(graphChanged(mGraph, eq[0], minX, maxX, minY, maxY)) return null; try { mLogic.mSymbols.define(mLogic.mY, y); double x = mLogic.mSymbols.eval(equation[1]); if(pointIsNaN(lastX, x, maxX, minX)) { series.get(0).add(MathHelper.NULL_VALUE, y); } else { series.get(0).add(x, y); } lastX = x; } catch(SyntaxException e) { e.printStackTrace(); } } } else if(equation[1].equals(mLogic.mY) && !equation[0].contains(mLogic.mY)) { for(double x = minX; x <= maxX; x += (0.00125 * (maxX - minX))) { if(graphChanged(mGraph, eq[0], minX, maxX, minY, maxY)) return null; try { mLogic.mSymbols.define(mLogic.mX, x); double y = mLogic.mSymbols.eval(equation[0]); if(pointIsNaN(lastY, y, maxY, minY)) { series.get(0).add(x, MathHelper.NULL_VALUE); } else { series.get(0).add(x, y); } lastY = y; } catch(SyntaxException e) { e.printStackTrace(); } } } else if(equation[1].equals(mLogic.mX) && !equation[0].contains(mLogic.mX)) { for(double y = minY; y <= maxY; y += (0.00125 * (maxY - minY))) { if(graphChanged(mGraph, eq[0], minX, maxX, minY, maxY)) return null; try { mLogic.mSymbols.define(mLogic.mY, y); double x = mLogic.mSymbols.eval(equation[0]); if(pointIsNaN(lastX, x, maxX, minX)) { series.get(0).add(MathHelper.NULL_VALUE, y); } else { series.get(0).add(x, y); } lastX = x; } catch(SyntaxException e) { e.printStackTrace(); } } } else { for(double x = minX; x <= maxX; x += (0.01 * (maxX - minX))) { List<Double> values = new ArrayList<Double>(); for(double y = maxY; y >= minY; y -= (0.01 * (maxY - minY))) { if(graphChanged(mGraph, eq[0], minX, maxX, minY, maxY)) return null; try { mLogic.mSymbols.define(mLogic.mX, x); mLogic.mSymbols.define(mLogic.mY, y); Double leftSide = mLogic.mSymbols.eval(equation[0]); Double rightSide = mLogic.mSymbols.eval(equation[1]); // TODO increase scale of graph as zooming // out if(leftSide < 0 && rightSide < 0) { if(leftSide * 0.97 >= rightSide && leftSide * 1.03 <= rightSide) { values.add(y); } } else { if(leftSide * 0.97 <= rightSide && leftSide * 1.03 >= rightSide) { values.add(y); } } } catch(SyntaxException e) { e.printStackTrace(); } } int color = mGraph.getRenderer().getSeriesRendererAt(0).getColor(); while(values.size() > series.size()) { series.add(new XYSeries("")); Graph.addSeriesRenderer(color, mGraph.getRenderer()); } for(int i = 0; i < values.size(); i++) { // TODO find closest value to previous one series.get(i).add(x, values.get(i)); } // // TODO needs a lot of work. very broken // for(Double d : values) { // // find closest value to previous one per // // series // XYSeries closestSeries = series.get(0); // for(XYSeries s : series) { // if(tolerance(closestSeries.getY(closestSeries.getItemCount() // - 1), d) > tolerance(s.getY(s.getItemCount() - // 1), d)) { // closestSeries = s; // } // } // closestSeries.add(x, d); // } } } clearGraph(mGraph); for(XYSeries s : series) { mGraph.getDataset().addSeries(s); } } catch(Exception e) { e.printStackTrace(); } return mLogic.mGraphDisplay; } private double tolerance(double result, double truth) { return(100.0 * Math.abs(truth - result) / Math.abs(truth)); } boolean graphChanged(Graph graph, String equation, double minX, double maxX, double minY, double maxY) { return !equation.equals(mLogic.getText()) || minY != graph.getRenderer().getYAxisMin() || maxY != graph.getRenderer().getYAxisMax() || minX != graph.getRenderer().getXAxisMin() || maxX != graph.getRenderer().getXAxisMax(); } boolean pointIsNaN(double lastV, double v, double max, double min) { return v == Double.NaN || v == Double.POSITIVE_INFINITY || v == Double.NEGATIVE_INFINITY || lastV > max && v < min || v > max && lastV < min; } @Override protected void onPostExecute(GraphicalView result) { super.onPostExecute(result); if(result != null) { result.repaint(); } } } }