new plotter

This commit is contained in:
Sergey Solovyev 2013-01-06 00:59:35 +04:00
parent 2c0803da74
commit b0608b8204
17 changed files with 350 additions and 115 deletions

View File

@ -282,5 +282,6 @@
<string name="cpp_onscreen_remove_icon_message">You can remove second icon in applications\' list from application settings or by pressing next button</string> <string name="cpp_onscreen_remove_icon_message">You can remove second icon in applications\' list from application settings or by pressing next button</string>
<string name="cpp_onscreen_remove_icon_button_text">Remove icon</string> <string name="cpp_onscreen_remove_icon_button_text">Remove icon</string>
<string name="cpp_this_change_may_require_reboot">This change may require reboot</string> <string name="cpp_this_change_may_require_reboot">This change may require reboot</string>
<string name="cpp_plot_3d">3D</string>
</resources> </resources>

View File

@ -85,7 +85,13 @@ public enum CalculatorDisplayMenuItem implements LabeledMenuItem<CalculatorDispl
assert generic != null; assert generic != null;
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(generic)); final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(generic));
final Constant xVariable = variables.get(0);
final Constant xVariable;
if ( variables.size() > 0 ) {
xVariable = variables.get(0);
} else {
xVariable = null;
}
final Constant yVariable; final Constant yVariable;
if ( variables.size() > 1 ) { if ( variables.size() > 1 ) {

View File

@ -15,7 +15,7 @@ public class PlotInput {
@NotNull @NotNull
private Generic function; private Generic function;
@NotNull @Nullable
private Constant xVariable; private Constant xVariable;
@Nullable @Nullable
@ -26,7 +26,7 @@ public class PlotInput {
@NotNull @NotNull
public static PlotInput newInstance(@NotNull Generic function, public static PlotInput newInstance(@NotNull Generic function,
@NotNull Constant xVariable, @Nullable Constant xVariable,
@Nullable Constant yVariable) { @Nullable Constant yVariable) {
PlotInput result = new PlotInput(); PlotInput result = new PlotInput();
@ -42,7 +42,7 @@ public class PlotInput {
return function; return function;
} }
@NotNull @Nullable
public Constant getXVariable() { public Constant getXVariable() {
return xVariable; return xVariable;
} }

View File

@ -8,8 +8,14 @@
<menu xmlns:a="http://schemas.android.com/apk/res/android"> <menu xmlns:a="http://schemas.android.com/apk/res/android">
<item a:id="@+id/menu_plot_3d"
a:title="@string/cpp_plot_3d"
a:icon="@drawable/ab_icon"
a:showAsAction="always"/>
<item a:id="@+id/menu_plot_settings" <item a:id="@+id/menu_plot_settings"
a:title="@string/c_settings" a:title="@string/c_settings"
a:icon="@drawable/ab_settings" a:icon="@drawable/ab_settings"
a:showAsAction="always"/> a:showAsAction="always"/>
</menu> </menu>

View File

@ -48,9 +48,9 @@ public class AndroidCalculatorNotifier implements CalculatorNotifier {
@Override @Override
public void showDebugMessage(@Nullable final String tag, @NotNull final String message) { public void showDebugMessage(@Nullable final String tag, @NotNull final String message) {
/*if (AndroidUtils.isDebuggable(application)) { if (AndroidUtils.isDebuggable(application)) {
showMessageInUiThread(tag == null ? message : tag + ": " + message); showMessageInUiThread(tag == null ? message : tag + ": " + message);
}*/ }
} }
private void showMessageInUiThread(@NotNull final String message) { private void showMessageInUiThread(@NotNull final String message) {

View File

@ -102,11 +102,11 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
public static void plotGraph(@NotNull final Context context, public static void plotGraph(@NotNull final Context context,
@NotNull Generic generic, @NotNull Generic generic,
@NotNull Constant xVariable, @Nullable Constant xVariable,
@Nullable Constant yVariable){ @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));
final AbstractCalculatorPlotFragment.Input input = new CalculatorPlotFragment.Input(generic.toString(), xVariable.getName(), yVariable == null ? null : yVariable.getName()); final AbstractCalculatorPlotFragment.Input input = new CalculatorPlotFragment.Input(generic.toString(), xVariable == null ? null : xVariable.getName(), yVariable == null ? null : yVariable.getName());
intent.putExtra(CalculatorPlotFragment.INPUT, input); 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);

View File

@ -75,7 +75,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
private PreparedInput preparedInput; private PreparedInput preparedInput;
@NotNull @NotNull
private ActivityMenu<Menu, MenuItem> fragmentMenu = ListActivityMenu.fromResource(R.menu.plot_menu, PlotMenu.class, SherlockMenuHelper.getInstance()); private ActivityMenu<Menu, MenuItem> fragmentMenu;
// thread which calculated data for graph view // thread which calculated data for graph view
@NotNull @NotNull
@ -150,8 +150,14 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
final CalculatorEventHolder.Result result = this.lastEventHolder.apply(calculatorEventData); final CalculatorEventHolder.Result result = this.lastEventHolder.apply(calculatorEventData);
if (result.isNewAfter()) { if (result.isNewAfter()) {
preparedInput = prepareInputFromDisplay(((CalculatorDisplayChangeEventData) data).getNewValue(), null); preparedInput = prepareInputFromDisplay(((CalculatorDisplayChangeEventData) data).getNewValue(), null);
this.preparedInput = preparedInput; onNewPreparedInput(preparedInput);
}
}
}
}
private void onNewPreparedInput(@NotNull PreparedInput preparedInput) {
this.preparedInput = preparedInput;
final PreparedInput finalPreparedInput = preparedInput; final PreparedInput finalPreparedInput = preparedInput;
getUiHandler().post(new Runnable() { getUiHandler().post(new Runnable() {
@ -172,10 +178,6 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
}); });
} }
}
}
}
protected abstract void onError(); protected abstract void onError();
protected abstract void createGraphicalView(@NotNull View view, @NotNull PreparedInput preparedInput); protected abstract void createGraphicalView(@NotNull View view, @NotNull PreparedInput preparedInput);
@ -230,12 +232,32 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
final List<IdentifiableMenuItem<MenuItem>> menuItems = new ArrayList<IdentifiableMenuItem<MenuItem>>();
menuItems.add(PlotMenu.preferences);
if ( is3dPlotSupported() ) {
menuItems.add(new IdentifiableMenuItem<MenuItem>() {
@NotNull
@Override
public Integer getItemId() {
return R.id.menu_plot_3d;
}
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
onNewPreparedInput(PreparedInput.force3dInstance(preparedInput));
}
});
}
fragmentMenu = ListActivityMenu.fromResource(R.menu.plot_menu, menuItems, SherlockMenuHelper.getInstance());
final FragmentActivity activity = this.getActivity(); final FragmentActivity activity = this.getActivity();
if (activity != null) { if (activity != null) {
fragmentMenu.onCreateOptionsMenu(activity, menu); fragmentMenu.onCreateOptionsMenu(activity, menu);
} }
} }
protected abstract boolean is3dPlotSupported();
@Override @Override
public void onPrepareOptionsMenu(Menu menu) { public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu); super.onPrepareOptionsMenu(menu);
@ -266,7 +288,13 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
final Generic expression = displayState.getResult(); final Generic expression = displayState.getResult();
if (CalculatorUtils.isPlotPossible(expression, displayState.getOperation())) { if (CalculatorUtils.isPlotPossible(expression, displayState.getOperation())) {
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(expression)); final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(expression));
final Constant xVariable = variables.get(0);
final Constant xVariable;
if ( variables.size() > 0 ) {
xVariable = variables.get(0);
} else {
xVariable = null;
}
final Constant yVariable; final Constant yVariable;
if ( variables.size() > 1 ) { if ( variables.size() > 1 ) {
@ -275,7 +303,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
yVariable = null; yVariable = null;
} }
final Input input = new Input(expression.toString(), xVariable.getName(), yVariable == null ? null : yVariable.getName()); final Input input = new Input(expression.toString(), xVariable == null ? null : xVariable.getName(), yVariable == null ? null : yVariable.getName());
return prepareInput(input, false, savedInstanceState); return prepareInput(input, false, savedInstanceState);
} }
} }
@ -293,7 +321,13 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
try { try {
final PreparedExpression preparedExpression = ToJsclTextProcessor.getInstance().process(input.getExpression()); final PreparedExpression preparedExpression = ToJsclTextProcessor.getInstance().process(input.getExpression());
final Generic expression = Expression.valueOf(preparedExpression.getExpression()); final Generic expression = Expression.valueOf(preparedExpression.getExpression());
final Constant xVar = new Constant(input.getXVariableName());
final Constant xVar;
if (input.getXVariableName() != null) {
xVar = new Constant(input.getXVariableName());
} else {
xVar = null;
}
final Constant yVar; final Constant yVar;
if (input.getYVariableName() != null) { if (input.getYVariableName() != null) {
@ -416,6 +450,8 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
private boolean fromInputArgs; private boolean fromInputArgs;
private boolean force3d = false;
@NotNull @NotNull
private PlotBoundaries plotBoundaries = PlotBoundaries.newDefaultInstance(); private PlotBoundaries plotBoundaries = PlotBoundaries.newDefaultInstance();
@ -425,7 +461,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
@NotNull @NotNull
public static PreparedInput newInstance(@NotNull Input input, public static PreparedInput newInstance(@NotNull Input input,
@NotNull Generic expression, @NotNull Generic expression,
@NotNull Constant xVariable, @Nullable Constant xVariable,
@Nullable Constant yVariable, @Nullable Constant yVariable,
boolean fromInputArgs, boolean fromInputArgs,
@NotNull PlotBoundaries plotBoundaries) { @NotNull PlotBoundaries plotBoundaries) {
@ -454,6 +490,17 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
return result; return result;
} }
@NotNull
public static PreparedInput force3dInstance(final PreparedInput that) {
if (!that.isError()) {
final PreparedInput result = PreparedInput.newInstance(that.input, that.expression, that.xVariable, that.yVariable, that.fromInputArgs, that.plotBoundaries);
result.force3d = true;
return result;
} else {
return that;
}
}
public boolean isFromInputArgs() { public boolean isFromInputArgs() {
return fromInputArgs; return fromInputArgs;
} }
@ -483,8 +530,12 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
return yVariable; return yVariable;
} }
public boolean isForce3d() {
return force3d;
}
public boolean isError() { public boolean isError() {
return input == null || expression == null || xVariable == null; return input == null || expression == null;
} }
} }
@ -493,14 +544,14 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
@NotNull @NotNull
private String expression; private String expression;
@NotNull @Nullable
private String xVariableName; private String xVariableName;
@Nullable @Nullable
private String yVariableName; private String yVariableName;
public Input(@NotNull String expression, public Input(@NotNull String expression,
@NotNull String xVariableName, @Nullable String xVariableName,
@Nullable String yVariableName) { @Nullable String yVariableName) {
this.expression = expression; this.expression = expression;
this.xVariableName = xVariableName; this.xVariableName = xVariableName;
@ -512,7 +563,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
return expression; return expression;
} }
@NotNull @Nullable
public String getXVariableName() { public String getXVariableName() {
return xVariableName; return xVariableName;
} }

View File

@ -57,19 +57,21 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
final Constant xVariable = preparedInput.getXVariable(); final Constant xVariable = preparedInput.getXVariable();
final Constant yVariable = preparedInput.getYVariable(); final Constant yVariable = preparedInput.getYVariable();
final int arity = yVariable == null ? 1 : 2; final int arity = xVariable == null ? 0 : (yVariable == null ? 1 : 2);
final List<FunctionPlotDef> functions = new ArrayList<FunctionPlotDef>(); final List<FunctionPlotDef> functions = new ArrayList<FunctionPlotDef>();
functions.add(FunctionPlotDef.newInstance(new RealArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(realLineColor.getColor(), FunctionLineStyle.solid, 3f))); functions.add(FunctionPlotDef.newInstance(new RealArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(realLineColor.getColor(), FunctionLineStyle.solid, 3f)));
if (arity == 1) {
functions.add(FunctionPlotDef.newInstance(new ImaginaryArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(imagLineColor.getColor(), FunctionLineStyle.solid, 3f))); functions.add(FunctionPlotDef.newInstance(new ImaginaryArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(imagLineColor.getColor(), FunctionLineStyle.solid, 3f)));
}
switch (arity) { switch (arity) {
case 0:
case 1: case 1:
if (preparedInput.isForce3d()) {
graphView = new Graph3dView(getActivity());
} else {
graphView = new Graph2dView(getActivity()); graphView = new Graph2dView(getActivity());
}
break; break;
case 2: case 2:
graphView = new Graph3dView(getActivity()); graphView = new Graph3dView(getActivity());
@ -91,6 +93,11 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
protected void createChart(@NotNull PreparedInput preparedInput) { protected void createChart(@NotNull PreparedInput preparedInput) {
} }
@Override
protected boolean is3dPlotSupported() {
return true;
}
@Override @Override
public void onResume() { public void onResume() {
@ -133,19 +140,35 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
@NotNull @NotNull
protected final Generic expression; protected final Generic expression;
@NotNull @Nullable
protected final Constant xVariable; protected final Constant xVariable;
@Nullable @Nullable
protected final Constant yVariable; protected final Constant yVariable;
public AbstractArityFunction(int arity, @NotNull Generic expression, @NotNull Constant xVariable, @Nullable Constant yVariable) { @Nullable
private Double constant = null;
public AbstractArityFunction(int arity,
@NotNull Generic expression,
@Nullable Constant xVariable,
@Nullable Constant yVariable) {
this.arity = arity; this.arity = arity;
this.expression = expression; this.expression = expression;
this.xVariable = xVariable; this.xVariable = xVariable;
this.yVariable = yVariable; this.yVariable = yVariable;
} }
@Override
public final double eval() {
if (constant == null) {
constant = eval0();
}
return constant;
}
protected abstract double eval0();
@Override @Override
public final int arity() { public final int arity() {
return arity; return arity;
@ -157,11 +180,16 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
private RealArityFunction(int arity, private RealArityFunction(int arity,
@NotNull Generic expression, @NotNull Generic expression,
@NotNull Constant xVariable, @Nullable Constant xVariable,
@Nullable Constant yVariable) { @Nullable Constant yVariable) {
super(arity, expression, xVariable, yVariable); super(arity, expression, xVariable, yVariable);
} }
@Override
public double eval0() {
return PlotUtils.calculatorExpression(expression).realPart();
}
@Override @Override
public double eval(double x) { public double eval(double x) {
return PlotUtils.calculatorExpression(expression, xVariable, x).realPart(); return PlotUtils.calculatorExpression(expression, xVariable, x).realPart();
@ -177,11 +205,16 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
private ImaginaryArityFunction(int arity, private ImaginaryArityFunction(int arity,
@NotNull Generic expression, @NotNull Generic expression,
@NotNull Constant xVariable, @Nullable Constant xVariable,
@Nullable Constant yVariable) { @Nullable Constant yVariable) {
super(arity, expression, xVariable, yVariable); super(arity, expression, xVariable, yVariable);
} }
@Override
public double eval0() {
return PlotUtils.calculatorExpression(expression).imaginaryPart();
}
@Override @Override
public double eval(double x) { public double eval(double x) {
return PlotUtils.calculatorExpression(expression, xVariable, x).imaginaryPart(); return PlotUtils.calculatorExpression(expression, xVariable, x).imaginaryPart();

View File

@ -57,6 +57,11 @@ public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
} }
} }
@Override
protected boolean is3dPlotSupported() {
return false;
}
@Nullable @Nullable
@Override @Override
protected PlotBoundaries getPlotBoundaries() { protected PlotBoundaries getPlotBoundaries() {

View File

@ -0,0 +1,12 @@
package org.solovyev.android.calculator.plot;
/**
* User: serso
* Date: 1/5/13
* Time: 10:45 PM
*/
public enum FunctionLineColorType {
color_map,
solid;
}

View File

@ -30,6 +30,9 @@ public class FunctionLineDef {
********************************************************************** **********************************************************************
*/ */
@NotNull
private FunctionLineColorType lineColorType = FunctionLineColorType.solid;
private int lineColor = Color.WHITE; private int lineColor = Color.WHITE;
@NotNull @NotNull
@ -57,6 +60,16 @@ public class FunctionLineDef {
return result; return result;
} }
@NotNull
public static FunctionLineDef newInstance(int lineColor, @NotNull FunctionLineStyle lineStyle, float lineWidth, @NotNull FunctionLineColorType lineColorType) {
final FunctionLineDef result = new FunctionLineDef();
result.lineColor = lineColor;
result.lineColorType = lineColorType;
result.lineStyle = lineStyle;
result.lineWidth = lineWidth;
return result;
}
@NotNull @NotNull
public static FunctionLineDef newDefaultInstance() { public static FunctionLineDef newDefaultInstance() {
return new FunctionLineDef(); return new FunctionLineDef();
@ -76,6 +89,11 @@ public class FunctionLineDef {
return lineWidth; return lineWidth;
} }
@NotNull
public FunctionLineColorType getLineColorType() {
return lineColorType;
}
public void applyToPaint(@NotNull Paint paint) { public void applyToPaint(@NotNull Paint paint) {
paint.setColor(lineColor); paint.setColor(lineColor);
paint.setStyle(Paint.Style.STROKE); paint.setStyle(Paint.Style.STROKE);

View File

@ -7,8 +7,10 @@ import android.graphics.Bitmap;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.AndroidUtils2; import org.solovyev.android.AndroidUtils2;
import javax.microedition.khronos.egl.*; import javax.microedition.khronos.egl.*;
@ -28,9 +30,10 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
private EGLContext eglContext; private EGLContext eglContext;
private GL11 gl; private GL11 gl;
protected int width, height; protected int width, height;
private boolean mIsLooping; private volatile boolean looping;
abstract void onDrawFrame(GL10 gl); abstract void onDrawFrame(GL10 gl);
abstract void onSurfaceCreated(GL10 gl, int width, int height); abstract void onSurfaceCreated(GL10 gl, int width, int height);
public String captureScreenshot() { public String captureScreenshot() {
@ -48,13 +51,20 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
buf.asIntBuffer().get(data); buf.asIntBuffer().get(data);
buf = null; buf = null;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
bitmap.setPixels(data, size-width, -width, 0, 0, width, height); bitmap.setPixels(data, size - width, -width, 0, 0, width, height);
return bitmap; return bitmap;
} }
private Handler handler = new Handler() { @NotNull
private final Handler uiHandler = new Handler() {
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
glDraw(); glDraw();
break;
default:
Log.e("GLView", "Incorrect message id: " + msg.what);
}
} }
}; };
@ -69,7 +79,7 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
} }
private void init() { private void init() {
SurfaceHolder holder = getHolder(); final SurfaceHolder holder = getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
holder.addCallback(this); holder.addCallback(this);
} }
@ -129,7 +139,7 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
if (egl.eglGetError() == EGL11.EGL_CONTEXT_LOST) { if (egl.eglGetError() == EGL11.EGL_CONTEXT_LOST) {
paused = true; paused = true;
} }
if (mIsLooping) { if (looping) {
requestDraw(); requestDraw();
} }
} }
@ -154,24 +164,24 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
} }
public void startLooping() { public void startLooping() {
if (!mIsLooping) { if (!looping) {
mIsLooping = true; looping = true;
glDraw(); glDraw();
} }
} }
public void stopLooping() { public void stopLooping() {
if (mIsLooping) { if (looping) {
mIsLooping = false; looping = false;
} }
} }
public boolean isLooping() { public boolean isLooping() {
return mIsLooping; return looping;
} }
public void requestDraw() { public void requestDraw() {
handler.sendEmptyMessage(1); uiHandler.sendEmptyMessage(1);
} }
static void bitmapBGRtoRGB(Bitmap bitmap, int width, int height) { static void bitmapBGRtoRGB(Bitmap bitmap, int width, int height) {
@ -182,7 +192,7 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
//BGR-565 to RGB-565 //BGR-565 to RGB-565
short v = data[i]; short v = data[i];
data[i] = (short) (((v&0x1f) << 11) | (v&0x7e0) | ((v&0xf800) >> 11)); data[i] = (short) (((v & 0x1f) << 11) | (v & 0x7e0) | ((v & 0xf800) >> 11));
} }
buf.rewind(); buf.rewind();
bitmap.copyPixelsFromBuffer(buf); bitmap.copyPixelsFromBuffer(buf);

View File

@ -2,7 +2,9 @@
package org.solovyev.android.calculator.plot; package org.solovyev.android.calculator.plot;
import android.graphics.Color;
import org.javia.arity.Function; import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull;
import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11; import javax.microedition.khronos.opengles.GL11;
@ -82,17 +84,20 @@ class Graph3d {
return bb; return bb;
} }
public void update(GL11 gl, Function f, float zoom) { public void update(@NotNull GL11 gl, @NotNull FunctionPlotDef fpd, float zoom) {
final Function function = fpd.getFunction();
final FunctionLineDef lineDef = fpd.getLineDef();
final int NTICK = useHighQuality3d ? 5 : 0; final int NTICK = useHighQuality3d ? 5 : 0;
final float size = 4 * zoom; final float size = 4 * zoom;
final float minX = -size, maxX = size, minY = -size, maxY = size; final float minX = -size, maxX = size, minY = -size, maxY = size;
//Calculator.log("update VBOs " + vertexVbo + ' ' + colorVbo + ' ' + vertexElementVbo); //Calculator.log("update VBOs " + vertexVbo + ' ' + colorVbo + ' ' + vertexElementVbo);
nVertex = N * N + 6 + 8 + NTICK * 6; nVertex = N * N + 6 + 8 + NTICK * 6;
int nFloats = nVertex * 3;
float vertices[] = new float[nFloats]; final float vertices[] = new float[nVertex * 3];
byte colors[] = new byte[nVertex << 2]; final byte colors[] = new byte[nVertex * 4];
if (f != null) {
if (fpd != null) {
//Calculator.log("Graph3d update"); //Calculator.log("Graph3d update");
float sizeX = maxX - minX; float sizeX = maxX - minX;
float sizeY = maxY - minY; float sizeY = maxY - minY;
@ -103,43 +108,74 @@ class Graph3d {
float y = minY; float y = minY;
float x = minX - stepX; float x = minX - stepX;
int nRealPoints = 0; int nRealPoints = 0;
final int arity = function.arity();
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; float xinc = (i & 1) == 0 ? stepX : -stepX;
x += xinc; 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);
final float z;
switch (arity) {
case 2:
z = (float) function.eval(x, y);
break;
case 1:
// todo serso: optimize (can be calculated once before loop)
z = (float) function.eval(x);
break;
case 0:
// todo serso: optimize (can be calculated once)
z = (float) function.eval();
break;
default:
throw new IllegalArgumentException();
}
vertices[pos] = x; vertices[pos] = x;
vertices[pos + 1] = y; vertices[pos + 1] = y;
vertices[pos + 2] = z; vertices[pos + 2] = z;
if (z == z) { // not NAN
if (!Float.isNaN(z)) {
sum += z * z; sum += z * z;
++nRealPoints; ++nRealPoints;
} }
} }
} }
float maxAbs = (float) Math.sqrt(sum / nRealPoints); float maxAbs = (float) Math.sqrt(sum / nRealPoints);
maxAbs *= .9f; maxAbs *= .9f;
maxAbs = Math.min(maxAbs, 15); maxAbs = Math.min(maxAbs, 15);
maxAbs = Math.max(maxAbs, .001f); maxAbs = Math.max(maxAbs, .001f);
final int lineColor = lineDef.getLineColor();
final int limitColor = N * N * 4; final int limitColor = N * N * 4;
for (int i = 0, j = 2; i < limitColor; i += 4, j += 3) { for (int i = 0, j = 2; i < limitColor; i += 4, j += 3) {
float z = vertices[j]; final float z = vertices[j];
if (z == z) {
if (!Float.isNaN(z)) {
if (lineDef.getLineColorType() == FunctionLineColorType.color_map) {
final float a = z / maxAbs; final float a = z / maxAbs;
final float abs = a < 0 ? -a : a; final float abs = a < 0 ? -a : a;
colors[i] = floatToByte(a); colors[i] = floatToByte(a);
colors[i + 1] = floatToByte(1 - abs * .3f); colors[i + 1] = floatToByte(1 - abs * .3f);
colors[i + 2] = floatToByte(-a); colors[i + 2] = floatToByte(-a);
} else {
colors[i] = (byte) Color.red(lineColor);
colors[i + 1] = (byte) Color.green(lineColor);
colors[i + 2] = (byte) Color.blue(lineColor);
}
colors[i + 3] = (byte) 255; colors[i + 3] = (byte) 255;
} else { } else {
vertices[j] = 0; vertices[j] = 0;
z = 0;
colors[i] = 0; colors[i] = 0;
colors[i + 1] = 0; colors[i + 1] = 0;
colors[i + 2] = 0; colors[i + 2] = 0;
colors[i + 3] = 0; colors[i + 3] = 0;
} }
} }
} }
int base = N * N * 3; int base = N * N * 3;
@ -248,7 +284,15 @@ class Graph3d {
} }
private byte floatToByte(float v) { private byte floatToByte(float v) {
return (byte) (v <= 0 ? 0 : v >= 1 ? 255 : (int) (v * 255)); if (v <= 0) {
return (byte) 0;
} else {
if (v >= 1) {
return (byte) 255;
} else {
return (byte) (v * 255);
}
}
} }
public void draw(GL11 gl) { public void draw(GL11 gl) {

View File

@ -3,16 +3,18 @@
package org.solovyev.android.calculator.plot; package org.solovyev.android.calculator.plot;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.opengl.Matrix; import android.opengl.Matrix;
import android.os.Build; import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.widget.ZoomButtonsController; import android.widget.ZoomButtonsController;
import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11; import javax.microedition.khronos.opengles.GL11;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
public class Graph3dView extends GLView implements GraphView { public class Graph3dView extends GLView implements GraphView {
@ -24,7 +26,12 @@ public class Graph3dView extends GLView implements GraphView {
private ZoomButtonsController zoomController = new ZoomButtonsController(this); private ZoomButtonsController zoomController = new ZoomButtonsController(this);
private float zoomLevel = 1, targetZoom, zoomStep = 0, currentZoom; private float zoomLevel = 1, targetZoom, zoomStep = 0, currentZoom;
private FPS fps = new FPS(); private FPS fps = new FPS();
private Graph3d graph;
@NotNull
private List<Graph3d> graphs = new ArrayList<Graph3d>();
@NotNull
private GraphViewHelper graphViewHelper = GraphViewHelper.newDefaultInstance();
public Graph3dView(Context context, AttributeSet attrs) { public Graph3dView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
@ -149,7 +156,6 @@ public class Graph3dView extends GLView implements GraphView {
private float[] matrix1 = new float[16], matrix2 = new float[16], matrix3 = new float[16]; private float[] matrix1 = new float[16], matrix2 = new float[16], matrix3 = new float[16];
private float angleX, angleY; private float angleX, angleY;
private boolean isDirty; private boolean isDirty;
private Function function;
private static final float DISTANCE = 15f; private static final float DISTANCE = 15f;
void setRotation(float x, float y) { void setRotation(float x, float y) {
@ -164,14 +170,18 @@ public class Graph3dView extends GLView implements GraphView {
@Override @Override
public void init(@NotNull FunctionViewDef functionViewDef) { public void init(@NotNull FunctionViewDef functionViewDef) {
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<FunctionPlotDef>emptyList());
} }
public void setFunctionPlotDefs(@NotNull List<FunctionPlotDef> functionPlotDefs) { public void setFunctionPlotDefs(@NotNull List<FunctionPlotDef> functionPlotDefs) {
if (functionPlotDefs.size() > 0) { for (FunctionPlotDef functionPlotDef: functionPlotDefs) {
function = functionPlotDefs.get(0).getFunction(); final int arity = functionPlotDef.getFunction().arity();
} else { if (arity != 0 && arity != 1 && arity != 2) {
function = null; throw new IllegalArgumentException("Function must have arity 0 or 1 or 2 for 3d plot!");
} }
}
this.graphViewHelper = this.graphViewHelper.copy(functionPlotDefs);
zoomLevel = 1; zoomLevel = 1;
isDirty = true; isDirty = true;
} }
@ -180,10 +190,13 @@ public class Graph3dView extends GLView implements GraphView {
public void onSurfaceCreated(GL10 gl, int width, int height) { public void onSurfaceCreated(GL10 gl, int width, int height) {
gl.glDisable(GL10.GL_DITHER); gl.glDisable(GL10.GL_DITHER);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0, 0, 0, 1);
final int backgroundColor = graphViewHelper.getFunctionViewDef().getBackgroundColor();
gl.glClearColor(Color.red(backgroundColor) / 255f, Color.green(backgroundColor) / 255f, Color.blue(backgroundColor) / 255f, Color.alpha(backgroundColor) / 255f);
gl.glShadeModel(useHighQuality3d ? GL10.GL_SMOOTH : GL10.GL_FLAT); gl.glShadeModel(useHighQuality3d ? GL10.GL_SMOOTH : GL10.GL_FLAT);
gl.glDisable(GL10.GL_LIGHTING); gl.glDisable(GL10.GL_LIGHTING);
graph = new Graph3d((GL11) gl, useHighQuality3d); ensureGraphsSize((GL11) gl);
isDirty = true; isDirty = true;
angleX = .5f; angleX = .5f;
angleY = 0; angleY = 0;
@ -201,7 +214,11 @@ public class Graph3dView extends GLView implements GraphView {
currentZoom = zoomLevel; currentZoom = zoomLevel;
} }
if (isDirty) { if (isDirty) {
graph.update(gl, function, zoomLevel); ensureGraphsSize(gl);
for (int i = 0; i < graphViewHelper.getFunctionPlotDefs().size(); i++) {
graphs.get(i).update(gl, graphViewHelper.getFunctionPlotDefs().get(i), zoomLevel);
}
isDirty = false; isDirty = false;
} }
@ -233,8 +250,16 @@ public class Graph3dView extends GLView implements GraphView {
Matrix.multiplyMM(matrix3, 0, matrix2, 0, matrix1, 0); Matrix.multiplyMM(matrix3, 0, matrix2, 0, matrix1, 0);
gl.glMultMatrixf(matrix3, 0); gl.glMultMatrixf(matrix3, 0);
System.arraycopy(matrix3, 0, matrix1, 0, 16); System.arraycopy(matrix3, 0, matrix1, 0, 16);
for (Graph3d graph : graphs) {
graph.draw(gl); graph.draw(gl);
} }
}
private void ensureGraphsSize(@NotNull GL11 gl) {
while (graphViewHelper.getFunctionPlotDefs().size() > graphs.size()) {
graphs.add(new Graph3d(gl, useHighQuality3d));
}
}
private void initFrustum(GL10 gl, float distance) { private void initFrustum(GL10 gl, float distance) {
gl.glMatrixMode(GL10.GL_PROJECTION); gl.glMatrixMode(GL10.GL_PROJECTION);

View File

@ -51,7 +51,7 @@ public final class PlotUtils {
public static boolean addXY(double minValue, public static boolean addXY(double minValue,
double maxValue, double maxValue,
@NotNull Generic expression, @NotNull Generic expression,
@NotNull Constant variable, @Nullable Constant variable,
@NotNull MyXYSeries realSeries, @NotNull MyXYSeries realSeries,
@Nullable MyXYSeries imagSeries, @Nullable MyXYSeries imagSeries,
boolean addExtra, boolean addExtra,
@ -82,7 +82,7 @@ public final class PlotUtils {
boolean needToCalculateRealY = realSeries.needToAdd(step, x); boolean needToCalculateRealY = realSeries.needToAdd(step, x);
if (needToCalculateRealY) { if (needToCalculateRealY) {
final Complex c = calculatorExpression(expression, variable, x); final Complex c = variable == null ? calculatorExpression(expression) : calculatorExpression(expression, variable, x);
Double y = prepareY(c.realPart()); Double y = prepareY(c.realPart());
if (y != null) { if (y != null) {
@ -106,7 +106,7 @@ public final class PlotUtils {
} else { } else {
boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(step, x); boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(step, x);
if (needToCalculateImagY) { if (needToCalculateImagY) {
final Complex c = calculatorExpression(expression, variable, x); final Complex c = variable == null ? calculatorExpression(expression) : calculatorExpression(expression, variable, x);
Double y = prepareY(c.imaginaryPart()); Double y = prepareY(c.imaginaryPart());
if (y != null) { if (y != null) {
imag.moveToNextPoint(x, y); imag.moveToNextPoint(x, y);
@ -128,20 +128,28 @@ public final class PlotUtils {
} }
@NotNull @NotNull
static String getImagFunctionName(@NotNull Constant variable) { static String getImagFunctionName(@Nullable Constant variable) {
if (variable != null) {
return "g(" + variable.getName() + ")" + " = " + "Im(ƒ(" + variable.getName() + "))"; return "g(" + variable.getName() + ")" + " = " + "Im(ƒ(" + variable.getName() + "))";
} else {
return "g = Im(ƒ)";
}
} }
@NotNull @NotNull
private static String getRealFunctionName(@NotNull Generic expression, @NotNull Constant variable) { private static String getRealFunctionName(@NotNull Generic expression, @Nullable Constant variable) {
if (variable != null) {
return "ƒ(" + variable.getName() + ")" + " = " + expression.toString(); return "ƒ(" + variable.getName() + ")" + " = " + expression.toString();
} else {
return "ƒ = " + expression.toString();
}
} }
@NotNull @NotNull
static XYChart prepareChart(final double minValue, static XYChart prepareChart(final double minValue,
final double maxValue, final double maxValue,
@NotNull final Generic expression, @NotNull final Generic expression,
@NotNull final Constant variable, @Nullable final Constant variable,
int bgColor, int bgColor,
boolean interpolate, boolean interpolate,
int realLineColor, int realLineColor,
@ -159,8 +167,12 @@ public final class PlotUtils {
final XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(); final XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
renderer.setShowGrid(true); renderer.setShowGrid(true);
renderer.setXTitle(variable.getName()); renderer.setXTitle(variable != null ? variable.getName() : null);
if (variable != null) {
renderer.setYTitle("f(" + variable.getName() + ")"); renderer.setYTitle("f(" + variable.getName() + ")");
} else {
renderer.setYTitle("f");
}
renderer.setChartTitleTextSize(25); renderer.setChartTitleTextSize(25);
renderer.setAxisTitleTextSize(25); renderer.setAxisTitleTextSize(25);
renderer.setLabelsTextSize(25); renderer.setLabelsTextSize(25);
@ -370,6 +382,15 @@ public final class PlotUtils {
} }
} }
@NotNull
public static Complex calculatorExpression(@NotNull Generic expression) {
try {
return unwrap(expression.numeric());
} catch (RuntimeException e) {
return NaN;
}
}
@NotNull @NotNull
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant xVar, double x) { public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant xVar, double x) {
try { try {

View File

@ -162,6 +162,9 @@ public enum CalculatorEventType {
//org.solovyev.android.calculator.plot.PlotInput //org.solovyev.android.calculator.plot.PlotInput
plot_graph, plot_graph,
plot_graph_3d,
//String //String
show_evaluation_error; show_evaluation_error;

View File

@ -44,9 +44,9 @@ public final class CalculatorUtils {
public static boolean isPlotPossible(@NotNull Generic expression, @NotNull JsclOperation operation) { public static boolean isPlotPossible(@NotNull Generic expression, @NotNull JsclOperation operation) {
boolean result = false; boolean result = false;
if (operation == JsclOperation.simplify) { if (operation == JsclOperation.simplify || operation == JsclOperation.numeric) {
int size = getNotSystemConstants(expression).size(); int size = getNotSystemConstants(expression).size();
if (size == 1 || size == 2) { if (size == 0 || size == 1 || size == 2) {
result = true; result = true;
} }
} }