diff --git a/android-app-core/src/main/java/org/solovyev/android/AndroidUtils2.java b/android-app-core/src/main/java/org/solovyev/android/AndroidUtils2.java index ac8251b6..bfc6b5ed 100644 --- a/android-app-core/src/main/java/org/solovyev/android/AndroidUtils2.java +++ b/android-app-core/src/main/java/org/solovyev/android/AndroidUtils2.java @@ -5,8 +5,16 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.os.Environment; +import android.util.Log; import org.jetbrains.annotations.NotNull; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + /** * User: Solovyev_S * Date: 03.10.12 @@ -55,4 +63,43 @@ public final class AndroidUtils2 { int componentEnabledSetting = pm.getComponentEnabledSetting(new ComponentName(context, componentClass)); return componentEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_ENABLED || componentEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; } + + public static String saveBitmap(@NotNull Bitmap bitmap, + @NotNull String path, + @NotNull String fileName) { + final File sdcardPath = Environment.getExternalStorageDirectory(); + final File filePath = new File(sdcardPath, path); + + filePath.mkdirs(); + + final String fullFileName = fileName + "_" + System.currentTimeMillis() + ".png"; + + final File file = new File(path, fullFileName); + if (!file.exists()) { + final String name = file.getAbsolutePath(); + + FileOutputStream fos = null; + try { + fos = new FileOutputStream(name); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + } catch (FileNotFoundException e) { + Log.e("AndroidUtils", e.getMessage(), e); + } catch (IOException e) { + Log.e("AndroidUtils", e.getMessage(), e); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + Log.e("AndroidUtils", e.getMessage(), e); + } + } + } + + return name; + } + + return null; + } } diff --git a/android-app/src/main/java/arity/calculator/Calculator.java b/android-app/src/main/java/arity/calculator/Calculator.java index 13ae3df2..07a34704 100755 --- a/android-app/src/main/java/arity/calculator/Calculator.java +++ b/android-app/src/main/java/arity/calculator/Calculator.java @@ -3,7 +3,6 @@ package arity.calculator; import android.app.Activity; -import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Build; @@ -18,8 +17,9 @@ 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 org.solovyev.android.calculator.plot.Graph2dView; +import org.solovyev.android.calculator.plot.Graph3dView; import java.util.ArrayList; @@ -183,9 +183,6 @@ public class Calculator extends Activity implements TextWatcher, //OnClickListener public void onClick(View target) { - if (target == graphView || target == graph3dView) { - startActivity(new Intent(this, ShowGraph.class)); - } } // OnItemClickListener @@ -323,7 +320,7 @@ public class Calculator extends Activity implements TextWatcher, showGraph(null); } } else { - graphView.setFunctions(auxFuncs); + //graphView.setFunctionPlotDefs(auxFuncs); if (graphView.getVisibility() != View.VISIBLE) { if (isAlphaVisible) { isAlphaVisible = false; @@ -376,7 +373,7 @@ public class Calculator extends Activity implements TextWatcher, } else { // graphedFunction = f; if (f.arity() == 1) { - graphView.setFunction(f); + //graphView.setFunctionPlotDefs(Arrays.asList(f)); if (graphView.getVisibility() != View.VISIBLE) { if (isAlphaVisible) { isAlphaVisible = false; @@ -389,7 +386,7 @@ public class Calculator extends Activity implements TextWatcher, graphView.setVisibility(View.VISIBLE); } } else { - graph3dView.setFunction(f); + //graph3dView.setFunctionPlotDefs(Arrays.asList(f)); if (graph3dView.getVisibility() != View.VISIBLE) { if (isAlphaVisible) { isAlphaVisible = false; diff --git a/android-app/src/main/java/arity/calculator/CalculatorEditable.java b/android-app/src/main/java/arity/calculator/CalculatorEditable.java deleted file mode 100755 index ea1bfc64..00000000 --- a/android-app/src/main/java/arity/calculator/CalculatorEditable.java +++ /dev/null @@ -1,85 +0,0 @@ -// 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); - } -} diff --git a/android-app/src/main/java/arity/calculator/Data.java b/android-app/src/main/java/arity/calculator/Data.java deleted file mode 100755 index 4f32c8fe..00000000 --- a/android-app/src/main/java/arity/calculator/Data.java +++ /dev/null @@ -1,145 +0,0 @@ -// 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(); - } -} diff --git a/android-app/src/main/java/arity/calculator/Help.java b/android-app/src/main/java/arity/calculator/Help.java deleted file mode 100755 index a4fc0a4c..00000000 --- a/android-app/src/main/java/arity/calculator/Help.java +++ /dev/null @@ -1,16 +0,0 @@ -// 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"); - } -} diff --git a/android-app/src/main/java/arity/calculator/ShowGraph.java b/android-app/src/main/java/arity/calculator/ShowGraph.java deleted file mode 100755 index 4fd627f1..00000000 --- a/android-app/src/main/java/arity/calculator/ShowGraph.java +++ /dev/null @@ -1,69 +0,0 @@ -// 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 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; - }*/ -} diff --git a/android-app/src/main/java/arity/calculator/Util.java b/android-app/src/main/java/arity/calculator/Util.java deleted file mode 100755 index 6ec6b48e..00000000 --- a/android-app/src/main/java/arity/calculator/Util.java +++ /dev/null @@ -1,70 +0,0 @@ -// 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); - } -} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorArityPlotFragment.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorArityPlotFragment.java index 7ee927ea..3bcf3625 100644 --- a/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorArityPlotFragment.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/CalculatorArityPlotFragment.java @@ -1,16 +1,16 @@ package org.solovyev.android.calculator.plot; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.preference.PreferenceManager; 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.CalculatorPreferences; import org.solovyev.android.calculator.R; import java.util.ArrayList; @@ -39,6 +39,12 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment @Override protected void createGraphicalView(@NotNull View root, @NotNull PreparedInput preparedInput) { + + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity()); + + final GraphLineColor realLineColor = CalculatorPreferences.Graph.lineColorReal.getPreference(preferences); + final GraphLineColor imagLineColor = CalculatorPreferences.Graph.lineColorImag.getPreference(preferences); + // remove old final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout); @@ -53,45 +59,28 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment final int arity = yVariable == null ? 1 : 2; - final List functions = new ArrayList(); - functions.add(new Function() { - @Override - public int arity() { - return arity; - } + final List functions = new ArrayList(); - @Override - public double eval(double x) { - return PlotUtils.calculatorExpression(expression, xVariable, x).realPart(); - } + functions.add(FunctionPlotDef.newInstance(new RealArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(realLineColor.getColor(), FunctionLineStyle.solid, 3f))); - @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); + if (arity == 1) { + functions.add(FunctionPlotDef.newInstance(new ImaginaryArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(imagLineColor.getColor(), FunctionLineStyle.solid, 3f))); } + switch (arity) { + case 1: + graphView = new Graph2dView(getActivity()); + break; + case 2: + graphView = new Graph3dView(getActivity()); + break; + default: + throw new IllegalArgumentException("Unsupported arity: " + arity); + } + + graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor())); + graphView.setFunctionPlotDefs(functions); + graphContainer.addView((View) graphView); } else { onError(); @@ -128,4 +117,79 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment this.graphView.onPause(); } } + + /* + ********************************************************************** + * + * STATIC + * + ********************************************************************** + */ + + private static abstract class AbstractArityFunction extends Function { + + protected final int arity; + + @NotNull + protected final Generic expression; + + @NotNull + protected final Constant xVariable; + + @Nullable + protected final Constant yVariable; + + public AbstractArityFunction(int arity, @NotNull Generic expression, @NotNull Constant xVariable, @Nullable Constant yVariable) { + this.arity = arity; + this.expression = expression; + this.xVariable = xVariable; + this.yVariable = yVariable; + } + + @Override + public final int arity() { + return arity; + } + + } + + private static class RealArityFunction extends AbstractArityFunction { + + private RealArityFunction(int arity, + @NotNull Generic expression, + @NotNull Constant xVariable, + @Nullable Constant yVariable) { + super(arity, expression, xVariable, yVariable); + } + + @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(); + } + } + + private static class ImaginaryArityFunction extends AbstractArityFunction { + + private ImaginaryArityFunction(int arity, + @NotNull Generic expression, + @NotNull Constant xVariable, + @Nullable Constant yVariable) { + super(arity, expression, xVariable, yVariable); + } + + @Override + public double eval(double x) { + return PlotUtils.calculatorExpression(expression, xVariable, x).imaginaryPart(); + } + + @Override + public double eval(double x, double y) { + return PlotUtils.calculatorExpression(expression, xVariable, x, yVariable, y).imaginaryPart(); + } + } } diff --git a/android-app/src/main/java/arity/calculator/FPS.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/FPS.java similarity index 89% rename from android-app/src/main/java/arity/calculator/FPS.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/FPS.java index fbd743be..473d5773 100755 --- a/android-app/src/main/java/arity/calculator/FPS.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/FPS.java @@ -1,4 +1,4 @@ -package arity.calculator; +package org.solovyev.android.calculator.plot; class FPS { private int drawCnt; diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionLineDef.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionLineDef.java new file mode 100644 index 00000000..0475c083 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionLineDef.java @@ -0,0 +1,91 @@ +package org.solovyev.android.calculator.plot; + +import android.graphics.Color; +import android.graphics.Paint; +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 1/5/13 + * Time: 7:41 PM + */ +public class FunctionLineDef { + + /* + ********************************************************************** + * + * CONSTANTS + * + ********************************************************************** + */ + + @NotNull + private static final Float DEFAULT_LINE_WIDTH = -1f; + + /* + ********************************************************************** + * + * FIELDS + * + ********************************************************************** + */ + + private int lineColor = Color.WHITE; + + @NotNull + private FunctionLineStyle lineStyle = FunctionLineStyle.solid; + + private float lineWidth = -DEFAULT_LINE_WIDTH; + + private FunctionLineDef() { + } + + @NotNull + public static FunctionLineDef newInstance(int lineColor, @NotNull FunctionLineStyle lineStyle) { + final FunctionLineDef result = new FunctionLineDef(); + result.lineColor = lineColor; + result.lineStyle = lineStyle; + return result; + } + + @NotNull + public static FunctionLineDef newInstance(int lineColor, @NotNull FunctionLineStyle lineStyle, float lineWidth) { + final FunctionLineDef result = new FunctionLineDef(); + result.lineColor = lineColor; + result.lineStyle = lineStyle; + result.lineWidth = lineWidth; + return result; + } + + @NotNull + public static FunctionLineDef newDefaultInstance() { + return new FunctionLineDef(); + } + + + public int getLineColor() { + return lineColor; + } + + @NotNull + public FunctionLineStyle getLineStyle() { + return lineStyle; + } + + public float getLineWidth() { + return lineWidth; + } + + public void applyToPaint(@NotNull Paint paint) { + paint.setColor(lineColor); + paint.setStyle(Paint.Style.STROKE); + + if ( lineWidth == DEFAULT_LINE_WIDTH ) { + paint.setStrokeWidth(0); + } else { + paint.setStrokeWidth(lineWidth); + } + + lineStyle.applyToPaint(paint); + } +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionLineStyle.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionLineStyle.java new file mode 100644 index 00000000..ecd73a77 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionLineStyle.java @@ -0,0 +1,44 @@ +package org.solovyev.android.calculator.plot; + +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 1/5/13 + * Time: 7:37 PM + */ +public enum FunctionLineStyle { + + solid { + @Override + public void applyToPaint(@NotNull Paint paint) { + paint.setPathEffect(null); + } + }, + + dashed { + @Override + public void applyToPaint(@NotNull Paint paint) { + paint.setPathEffect(new DashPathEffect(new float[] {10, 20}, 0)); + } + }, + + dotted { + @Override + public void applyToPaint(@NotNull Paint paint) { + paint.setPathEffect(new DashPathEffect(new float[] {5, 1}, 0)); + } + }, + + dash_dotted { + @Override + public void applyToPaint(@NotNull Paint paint) { + paint.setPathEffect(new DashPathEffect(new float[] {10, 20, 5, 1}, 0)); + } + }; + + public abstract void applyToPaint(@NotNull Paint paint); + +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionPlotDef.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionPlotDef.java new file mode 100644 index 00000000..c3e747cb --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionPlotDef.java @@ -0,0 +1,46 @@ +package org.solovyev.android.calculator.plot; + +import org.javia.arity.Function; +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 1/5/13 + * Time: 7:35 PM + */ +public class FunctionPlotDef { + + @NotNull + private Function function; + + @NotNull + private FunctionLineDef lineDef; + + private FunctionPlotDef() { + } + + @NotNull + public static FunctionPlotDef newInstance(@NotNull Function function) { + return newInstance(function, FunctionLineDef.newDefaultInstance()); + } + + @NotNull + public static FunctionPlotDef newInstance(@NotNull Function function, @NotNull FunctionLineDef lineDef) { + final FunctionPlotDef result = new FunctionPlotDef(); + + result.function = function; + result.lineDef = lineDef; + + return result; + } + + @NotNull + public Function getFunction() { + return function; + } + + @NotNull + public FunctionLineDef getLineDef() { + return lineDef; + } +} diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionViewDef.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionViewDef.java new file mode 100644 index 00000000..75c8430f --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/FunctionViewDef.java @@ -0,0 +1,76 @@ +package org.solovyev.android.calculator.plot; + +import android.graphics.Color; +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 1/5/13 + * Time: 9:11 PM + */ +public class FunctionViewDef { + + /* + ********************************************************************** + * + * CONSTANTS + * + ********************************************************************** + */ + + private static final int DEFAULT_AXIS_COLOR = 0xff00a000; + private static final int DEFAULT_GRID_COLOR = 0xff004000; + private static final int DEFAULT_BACKGROUND_COLOR = Color.BLACK; + + /* + ********************************************************************** + * + * FIELDS + * + ********************************************************************** + */ + + private int axisColor = DEFAULT_AXIS_COLOR; + + private int axisLabelsColor = DEFAULT_AXIS_COLOR; + + private int gridColor = DEFAULT_GRID_COLOR; + + private int backgroundColor = DEFAULT_BACKGROUND_COLOR; + + private FunctionViewDef() { + } + + private FunctionViewDef(int axisColor, int axisLabelColor, int gridColor, int backgroundColor) { + this.axisColor = axisColor; + this.axisLabelsColor = axisLabelColor; + this.gridColor = gridColor; + this.backgroundColor = backgroundColor; + } + + @NotNull + public static FunctionViewDef newDefaultInstance() { + return new FunctionViewDef(); + } + + @NotNull + public static FunctionViewDef newInstance(int axisColor, int axisLabelColor, int gridColor, int backgroundColor) { + return new FunctionViewDef(axisColor, axisLabelColor, gridColor, backgroundColor); + } + + public int getAxisColor() { + return axisColor; + } + + public int getAxisLabelsColor() { + return axisLabelsColor; + } + + public int getGridColor() { + return gridColor; + } + + public int getBackgroundColor() { + return backgroundColor; + } +} diff --git a/android-app/src/main/java/arity/calculator/GLView.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GLView.java similarity index 86% rename from android-app/src/main/java/arity/calculator/GLView.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/GLView.java index 15f3798e..a9b405b0 100755 --- a/android-app/src/main/java/arity/calculator/GLView.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GLView.java @@ -1,6 +1,6 @@ // Copyright (C) 2009 Mihai Preda -package arity.calculator; +package org.solovyev.android.calculator.plot; import android.content.Context; import android.graphics.Bitmap; @@ -9,12 +9,14 @@ import android.os.Message; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView; +import org.solovyev.android.AndroidUtils2; 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; +import java.nio.ShortBuffer; abstract class GLView extends SurfaceView implements SurfaceHolder.Callback { private boolean hasSurface; @@ -33,8 +35,8 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback { public String captureScreenshot() { Bitmap bitmap = getRawPixels(gl, width, height); - Util.bitmapBGRtoRGB(bitmap, width, height); - return Util.saveBitmap(bitmap, GraphView.SCREENSHOT_DIR, "calculator"); + bitmapBGRtoRGB(bitmap, width, height); + return AndroidUtils2.saveBitmap(bitmap, GraphView.SCREENSHOT_DIR, "calculator"); } private static Bitmap getRawPixels(GL10 gl, int width, int height) { @@ -73,7 +75,6 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback { } public void onResume() { - Calculator.log("onResume " + this); paused = false; if (hasSurface) { initGL(); @@ -81,7 +82,6 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback { } public void onPause() { - Calculator.log("onPause " + this); deinitGL(); } @@ -125,10 +125,8 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback { 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) { @@ -138,11 +136,9 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback { } 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; @@ -153,14 +149,12 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback { } public void surfaceDestroyed(SurfaceHolder holder) { - Calculator.log("surfaceDestroyed " + this); hasSurface = false; deinitGL(); } public void startLooping() { if (!mIsLooping) { - Calculator.log("start looping"); mIsLooping = true; glDraw(); } @@ -168,7 +162,6 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback { public void stopLooping() { if (mIsLooping) { - Calculator.log("stop looping"); mIsLooping = false; } } @@ -180,4 +173,18 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback { public void requestDraw() { handler.sendEmptyMessage(1); } + + 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); + } } diff --git a/android-app/src/main/java/arity/calculator/Graph2dView.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dView.java similarity index 73% rename from android-app/src/main/java/arity/calculator/Graph2dView.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dView.java index b54847ab..8715e3ee 100755 --- a/android-app/src/main/java/arity/calculator/Graph2dView.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph2dView.java @@ -1,6 +1,7 @@ // Copyright (C) 2009-2010 Mihai Preda -package arity.calculator; +package org.solovyev.android.calculator.plot; + import android.content.Context; import android.graphics.*; @@ -10,22 +11,29 @@ import android.view.View; import android.widget.Scroller; import android.widget.ZoomButtonsController; import org.javia.arity.Function; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.AndroidUtils2; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -public class Graph2dView extends View implements - GraphView, - ZoomButtonsController.OnZoomListener, - TouchHandler.TouchHandlerInterface { +public class Graph2dView extends View implements GraphView { private int width, height; private Matrix matrix = new Matrix(); private Paint paint = new Paint(), textPaint = new Paint(), fillPaint = new Paint(); - private ArrayList funcs = new ArrayList(); - 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; + + @NotNull + private GraphViewHelper graphViewHelper = GraphViewHelper.newDefaultInstance(); + + private final GraphData next = GraphData.newEmptyInstance(); + + private final GraphData endGraph = GraphData.newEmptyInstance(); + + @NotNull + private List graphs = new ArrayList(graphViewHelper.getFunctionPlotDefs().size()); + private float gwidth = 8; private float currentX, currentY; private float lastMinX; @@ -36,13 +44,6 @@ public class Graph2dView extends View implements 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, @@ -70,33 +71,35 @@ public class Graph2dView extends View implements 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"); + return AndroidUtils2.saveBitmap(bitmap, GraphView.SCREENSHOT_DIR, "calculator"); } - private void clearAllGraph() { - for (int i = 0; i < GRAPHS_SIZE; ++i) { - graphs[i].clear(); + private void clearAllGraphs() { + for (GraphData graph : graphs) { + graph.clear(); + } + + while ( graphViewHelper.getFunctionPlotDefs().size() > graphs.size() ) { + graphs.add(GraphData.newEmptyInstance()); } } - public void setFunctions(List fs) { - funcs.clear(); - for (Function f : fs) { - int arity = f.arity(); - if (arity == 0 || arity == 1) { - funcs.add(f); + @Override + public void init(@NotNull FunctionViewDef functionViewDef) { + this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.emptyList()); + } + + public void setFunctionPlotDefs(@NotNull List functionPlotDefs) { + + for (FunctionPlotDef functionPlotDef: functionPlotDefs) { + final int arity = functionPlotDef.getFunction().arity(); + if (arity != 0 && arity != 1) { + throw new IllegalArgumentException("Function must have arity 0 or 1 for 2d plot!"); } } - clearAllGraph(); - invalidate(); - } - public void setFunction(Function f) { - funcs.clear(); - if (f != null) { - funcs.add(f); - } - clearAllGraph(); + this.graphViewHelper = this.graphViewHelper.copy(functionPlotDefs); + clearAllGraphs(); invalidate(); } @@ -133,12 +136,12 @@ public class Graph2dView extends View implements protected void onSizeChanged(int w, int h, int ow, int oh) { width = w; height = h; - clearAllGraph(); + clearAllGraphs(); // points = new float[w+w]; } protected void onDraw(Canvas canvas) { - if (funcs.size() == 0) { + if (graphViewHelper.getFunctionPlotDefs().size() == 0) { return; } if (scroller.computeScrollOffset()) { @@ -184,9 +187,14 @@ public class Graph2dView extends View implements 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(); + private void computeGraph(@NotNull Function function, + float minX, + float maxX, + float minY, + float maxY, + @NotNull GraphData graph) { + if (function.arity() == 0) { + float v = (float) function.eval(); if (v < -10000f) { v = -10000f; } @@ -217,7 +225,7 @@ public class Graph2dView extends View implements } } if (graph.empty()) { - graph.push(minX, eval(f, minX)); + graph.push(minX, eval(function, minX)); } float leftX, leftY; float rightX = graph.topX(), rightY = graph.topY(); @@ -230,7 +238,7 @@ public class Graph2dView extends View implements } if (next.empty()) { float x = leftX + maxStep; - next.push(x, eval(f, x)); + next.push(x, eval(function, x)); ++nEval; } rightX = next.topX(); @@ -243,7 +251,7 @@ public class Graph2dView extends View implements float dx = rightX - leftX; float middleX = (leftX + rightX) / 2; - float middleY = eval(f, middleX); + float middleY = eval(function, middleX); ++nEval; boolean middleIsOutside = (middleY < leftY && middleY < rightY) || (leftY < middleY && rightY < middleY); if (dx < minStep) { @@ -289,30 +297,32 @@ public class Graph2dView extends View implements endGraph.clear(); } - private static Path path = new Path(); + private static void graphToPath(@NotNull GraphData graph, @NotNull Path path) { + + final int size = graph.getSize(); + final float[] xs = graph.getXs(); + final float[] ys = graph.getYs(); - 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) { + + boolean newCurve = true; + + for (int i = 0; i < size; i++) { + + final float y = ys[i]; + final float x = xs[i]; + + if (y != y) { + newCurve = true; + } else { // !NaN + if (newCurve) { path.moveTo(x, y); - first = false; + newCurve = false; } else { path.lineTo(x, y); } - } else { - first = true; } } - return path; } private static final float NTICKS = 15; @@ -382,10 +392,10 @@ public class Graph2dView extends View implements float halfw = ywidth / 2; boundMinY = minY - halfw; boundMaxY = maxY + halfw; - clearAllGraph(); + clearAllGraphs(); } - canvas.drawColor(0xff000000); + canvas.drawColor(graphViewHelper.getFunctionViewDef().getBackgroundColor()); paint.setStrokeWidth(0); paint.setAntiAlias(false); @@ -412,34 +422,46 @@ public class Graph2dView extends View implements 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); + + + { + // GRID + + paint.setPathEffect(new DashPathEffect(new float[]{5, 10}, 0)); + paint.setColor(graphViewHelper.getFunctionViewDef().getGridColor()); + + float step = stepFactor(gwidth); + // Calculator.log("width " + gwidth + " step " + step); + float v = ((int) (minX / step)) * step; + textPaint.setColor(graphViewHelper.getFunctionViewDef().getAxisLabelsColor()); + 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.setPathEffect(null); } - 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); - } - } + // AXIS - paint.setColor(COL_AXIS); + paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor()); if (drawYAxis) { canvas.drawLine(x0, 0, x0, height, paint); } @@ -450,15 +472,23 @@ public class Graph2dView extends View implements 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]); + final List functionPlotDefs = graphViewHelper.getFunctionPlotDefs(); + + // create path once + final Path path = new Path(); + + for (int i = 0; i < functionPlotDefs.size(); i++) { + final FunctionPlotDef fpd = functionPlotDefs.get(i); + computeGraph(fpd.getFunction(), minX, maxX, boundMinY, boundMaxY, graphs.get(i)); + + graphToPath(graphs.get(i), path); + path.transform(matrix); - paint.setColor(COL_GRAPH[i]); + + fpd.getLineDef().applyToPaint(paint); + canvas.drawPath(path, paint); } lastMinX = minX; @@ -485,7 +515,7 @@ public class Graph2dView extends View implements } private void invalidateGraphs() { - clearAllGraph(); + clearAllGraphs(); boundMinY = boundMaxY = 0; invalidate(); } diff --git a/android-app/src/main/java/arity/calculator/Graph3d.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3d.java similarity index 53% rename from android-app/src/main/java/arity/calculator/Graph3d.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3d.java index 2f15ca63..e630e2c8 100755 --- a/android-app/src/main/java/arity/calculator/Graph3d.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3d.java @@ -1,6 +1,6 @@ // Copyright (C) 2009-2010 Mihai Preda -package arity.calculator; +package org.solovyev.android.calculator.plot; import org.javia.arity.Function; @@ -12,41 +12,46 @@ import java.nio.FloatBuffer; import java.nio.ShortBuffer; class Graph3d { - private final int N = Calculator.useHighQuality3d ? 36 : 24; + + private final int N; + private final boolean useHighQuality3d; 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]; + + Graph3d(GL11 gl, boolean useHighQuality3d) { + this.useHighQuality3d = useHighQuality3d; + this.N = useHighQuality3d ? 36 : 24; + + 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); + 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)); + 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); - } + 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)); - + //Calculator.log("VBOs support: " + useVBO + " version " + gl.glGetString(GL10.GL_VERSION)); + if (useVBO) { int[] out = new int[3]; - gl.glGenBuffers(3, out, 0); + gl.glGenBuffers(3, out, 0); vertexVbo = out[0]; - colorVbo = out[1]; + colorVbo = out[1]; vertexElementVbo = out[2]; } } @@ -78,34 +83,34 @@ class Graph3d { } public void update(GL11 gl, Function f, float zoom) { - final int NTICK = Calculator.useHighQuality3d ? 5 : 0; - final float size = 4*zoom; + final int NTICK = 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; + //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"); + //Calculator.log("Graph3d update"); float sizeX = maxX - minX; float sizeY = maxY - minY; - float stepX = sizeX / (N-1); - float stepY = sizeY / (N-1); + 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) { + 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) { + 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; + vertices[pos + 1] = y; + vertices[pos + 2] = z; if (z == z) { // not NAN sum += z * z; ++nRealPoints; @@ -117,125 +122,133 @@ class Graph3d { 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) { + 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; + 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; + 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 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; + 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; + vertices[p] = i; + vertices[p + 1] = baseSize; + vertices[p + 2] = 0; p += 3; - vertices[p] = -baseSize; vertices[p+1] = i; vertices[p+2] = 0; + vertices[p] = -baseSize; + vertices[p + 1] = i; + vertices[p + 2] = 0; p += 3; - vertices[p] = baseSize; vertices[p+1] = i; vertices[p+2] = 0; + vertices[p] = baseSize; + vertices[p + 1] = i; + vertices[p + 2] = 0; p += 3; } - for (int i = colorBase; i < colorBase+8*4; i += 4) { + 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; + colors[i + 1] = 0; + colors[i + 2] = (byte) 255; + colors[i + 3] = (byte) 255; } - base += 8*3; - colorBase += 8*4; + 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, + 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) { + 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; + 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] = i - tick; + vertices[p + 1] = -offset; + vertices[p + 2] = -offset; - vertices[p+3] = i+tick; - vertices[p+4] = offset; - vertices[p+5] = 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] = -offset; + vertices[p + 1] = i - tick; + vertices[p + 2] = -offset; - vertices[p+3] = offset; - vertices[p+4] = i+tick; - vertices[p+5] = 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] = -offset; + vertices[p + 1] = -offset; + vertices[p + 2] = i - tick; - vertices[p+3] = offset; - vertices[p+4] = offset; - vertices[p+5] = 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) { + for (int i = colorBase + NTICK * 6 * 4 - 1; i >= colorBase; --i) { colors[i] = (byte) 255; } vertexBuf = buildBuffer(vertices); - colorBuf = buildBuffer(colors); + 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); + 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; - + 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.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)); + return (byte) (v <= 0 ? 0 : v >= 1 ? 255 : (int) (v * 255)); } public void draw(GL11 gl) { @@ -249,16 +262,20 @@ class Graph3d { 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, 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); + gl.glDrawElements(GL10.GL_LINE_STRIP, N * N, GL10.GL_UNSIGNED_SHORT, verticeIdx); } - final int N2 = N*N; + final int N2 = N * N; gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, N2); gl.glDrawArrays(GL10.GL_LINES, N2, nVertex - N2); } + + public boolean isUseHighQuality3d() { + return useHighQuality3d; + } } diff --git a/android-app/src/main/java/arity/calculator/Graph3dView.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3dView.java similarity index 89% rename from android-app/src/main/java/arity/calculator/Graph3dView.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3dView.java index 41880c6a..c061dc40 100755 --- a/android-app/src/main/java/arity/calculator/Graph3dView.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/Graph3dView.java @@ -1,21 +1,23 @@ // Copyright (C) 2009 Mihai Preda -package arity.calculator; +package org.solovyev.android.calculator.plot; import android.content.Context; import android.opengl.Matrix; +import android.os.Build; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.ZoomButtonsController; import org.javia.arity.Function; +import org.jetbrains.annotations.NotNull; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL11; +import java.util.List; -public class Graph3dView extends GLView implements - GraphView, - ZoomButtonsController.OnZoomListener, - TouchHandler.TouchHandlerInterface { +public class Graph3dView extends GLView implements GraphView { + + private boolean useHighQuality3d = Build.VERSION.SDK_INT >= 5; private float lastTouchX, lastTouchY; private TouchHandler touchHandler; @@ -160,8 +162,16 @@ public class Graph3dView extends GLView implements return angleX < -limit || angleX > limit || angleY < -limit || angleY > limit; } - public void setFunction(Function f) { - function = f; + @Override + public void init(@NotNull FunctionViewDef functionViewDef) { + } + + public void setFunctionPlotDefs(@NotNull List functionPlotDefs) { + if (functionPlotDefs.size() > 0) { + function = functionPlotDefs.get(0).getFunction(); + } else { + function = null; + } zoomLevel = 1; isDirty = true; } @@ -171,9 +181,9 @@ public class Graph3dView extends GLView implements 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.glShadeModel(useHighQuality3d ? GL10.GL_SMOOTH : GL10.GL_FLAT); gl.glDisable(GL10.GL_LIGHTING); - graph = new Graph3d((GL11) gl); + graph = new Graph3d((GL11) gl, useHighQuality3d); isDirty = true; angleX = .5f; angleY = 0; @@ -195,9 +205,9 @@ public class Graph3dView extends GLView implements isDirty = false; } - if (fps.incFrame()) { + /*if (fps.incFrame()) { Calculator.log("f/s " + fps.getValue()); - } + }*/ gl.glClear(GL10.GL_COLOR_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW); @@ -244,6 +254,6 @@ public class Graph3dView extends GLView implements for (int i = 0; i < 16; ++i) { b.append(m[i]).append(' '); } - Calculator.log(name + ' ' + b.toString()); + //Calculator.log(name + ' ' + b.toString()); } } diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphData.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphData.java new file mode 100755 index 00000000..c91115e1 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphData.java @@ -0,0 +1,170 @@ +// Copyright (C) 2009 Mihai Preda + +package org.solovyev.android.calculator.plot; + +import org.jetbrains.annotations.NotNull; + +class GraphData { + + private int size = 0; + + private int allocatedSize = 4; + private float[] xs = new float[allocatedSize]; + private float[] ys = new float[allocatedSize]; + + private GraphData() { + } + + @NotNull + static GraphData newEmptyInstance() { + return new GraphData(); + } + + void swap(@NotNull GraphData that) { + float savedXs[] = that.xs; + float savedYs[] = that.ys; + int savedSize = that.size; + int savedAllocatedSize = that.allocatedSize; + + that.xs = this.xs; + that.ys = this.ys; + that.size = this.size; + that.allocatedSize = this.allocatedSize; + + this.xs = savedXs; + this.ys = savedYs; + this.size = savedSize; + this.allocatedSize = savedAllocatedSize; + } + + void push(float x, float y) { + if (size >= allocatedSize) { + makeSpace(size + 1); + } + + xs[size] = x; + ys[size] = y; + ++size; + } + + private void makeSpace(int spaceSize) { + int oldAllocatedSize = allocatedSize; + while (spaceSize > allocatedSize) { + allocatedSize += allocatedSize; + } + + if (oldAllocatedSize != allocatedSize) { + float[] a = new float[allocatedSize]; + System.arraycopy(xs, 0, a, 0, this.size); + xs = a; + a = new float[allocatedSize]; + System.arraycopy(ys, 0, a, 0, this.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(GraphData 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(); + } + + public float[] getXs() { + return xs; + } + + public float[] getYs() { + return ys; + } + + public int getSize() { + return size; + } +} diff --git a/android-app/src/main/java/arity/calculator/GraphView.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphView.java similarity index 54% rename from android-app/src/main/java/arity/calculator/GraphView.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/GraphView.java index 01d2fabc..d17a148a 100755 --- a/android-app/src/main/java/arity/calculator/GraphView.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphView.java @@ -1,22 +1,25 @@ // Copyright (C) 2009-2010 Mihai Preda -package arity.calculator; +package org.solovyev.android.calculator.plot; -import org.javia.arity.Function; +import android.widget.ZoomButtonsController; +import org.jetbrains.annotations.NotNull; -public interface GraphView { +import java.util.List; + +public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHandler.TouchHandlerInterface { static final String SCREENSHOT_DIR = "/screenshots"; - public void setFunction(Function f); + public void init(@NotNull FunctionViewDef functionViewDef); + + public void setFunctionPlotDefs(@NotNull List functionPlotDefs); public void onPause(); public void onResume(); public String captureScreenshot(); - void setId(int id); - /* ********************************************************************** * diff --git a/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphViewHelper.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphViewHelper.java new file mode 100644 index 00000000..9f0a1796 --- /dev/null +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/GraphViewHelper.java @@ -0,0 +1,59 @@ +package org.solovyev.android.calculator.plot; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +/** + * User: serso + * Date: 1/5/13 + * Time: 8:06 PM + */ +public class GraphViewHelper { + + @NotNull + private FunctionViewDef functionViewDef = FunctionViewDef.newDefaultInstance(); + + @NotNull + private List functionPlotDefs = Collections.emptyList(); + + private GraphViewHelper() { + } + + @NotNull + public static GraphViewHelper newDefaultInstance() { + return new GraphViewHelper(); + } + + @NotNull + public static GraphViewHelper newInstance(@NotNull FunctionViewDef functionViewDef, + @NotNull List functionPlotDefs) { + final GraphViewHelper result = new GraphViewHelper(); + + result.functionViewDef = functionViewDef; + result.functionPlotDefs = Collections.unmodifiableList(functionPlotDefs); + + return result; + } + + @NotNull + public GraphViewHelper copy(@NotNull List newFunctionPlotDefs) { + final GraphViewHelper result = new GraphViewHelper(); + + result.functionViewDef = functionViewDef; + result.functionPlotDefs = Collections.unmodifiableList(newFunctionPlotDefs); + + return result; + } + + @NotNull + public List getFunctionPlotDefs() { + return functionPlotDefs; + } + + @NotNull + public FunctionViewDef getFunctionViewDef() { + return functionViewDef; + } +} diff --git a/android-app/src/main/java/arity/calculator/MotionEventWrap.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrap.java similarity index 77% rename from android-app/src/main/java/arity/calculator/MotionEventWrap.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrap.java index cb3c5b9e..990e5f49 100755 --- a/android-app/src/main/java/arity/calculator/MotionEventWrap.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrap.java @@ -1,11 +1,12 @@ // Copyright (C) 2010 Mihai Preda -package arity.calculator; +package org.solovyev.android.calculator.plot; +import android.os.Build; import android.view.MotionEvent; class MotionEventWrap { - private static final boolean IS_API_5 = Util.SDK_VERSION >= 5; + private static final boolean IS_API_5 = Build.VERSION.SDK_INT >= 5; static int getPointerCount(MotionEvent event) { return IS_API_5 ? MotionEventWrapNew.getPointerCount(event) : 1; diff --git a/android-app/src/main/java/arity/calculator/MotionEventWrapNew.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrapNew.java similarity index 89% rename from android-app/src/main/java/arity/calculator/MotionEventWrapNew.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrapNew.java index 06dbd9ae..e8fe7a1b 100755 --- a/android-app/src/main/java/arity/calculator/MotionEventWrapNew.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/MotionEventWrapNew.java @@ -1,6 +1,6 @@ // Copyright (C) 2010 Mihai Preda -package arity.calculator; +package org.solovyev.android.calculator.plot; import android.view.MotionEvent; diff --git a/android-app/src/main/java/arity/calculator/TouchHandler.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/TouchHandler.java similarity index 98% rename from android-app/src/main/java/arity/calculator/TouchHandler.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/TouchHandler.java index bbaa7aca..f5d2865b 100755 --- a/android-app/src/main/java/arity/calculator/TouchHandler.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/TouchHandler.java @@ -1,6 +1,6 @@ // Copyright (C) 2009-2010 Mihai Preda -package arity.calculator; +package org.solovyev.android.calculator.plot; import android.view.MotionEvent; import android.view.VelocityTracker; diff --git a/android-app/src/main/java/arity/calculator/ZoomTracker.java b/android-app/src/main/java/org/solovyev/android/calculator/plot/ZoomTracker.java similarity index 96% rename from android-app/src/main/java/arity/calculator/ZoomTracker.java rename to android-app/src/main/java/org/solovyev/android/calculator/plot/ZoomTracker.java index 81857327..ec6cc6f8 100755 --- a/android-app/src/main/java/arity/calculator/ZoomTracker.java +++ b/android-app/src/main/java/org/solovyev/android/calculator/plot/ZoomTracker.java @@ -1,6 +1,6 @@ // Copyright (C) 2010 Mihai Preda -package arity.calculator; +package org.solovyev.android.calculator.plot; class ZoomTracker { private float sx1, sy1, sx2, sy2;