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_button_text">Remove icon</string>
<string name="cpp_this_change_may_require_reboot">This change may require reboot</string>
<string name="cpp_plot_3d">3D</string>
</resources>

View File

@ -85,7 +85,13 @@ public enum CalculatorDisplayMenuItem implements LabeledMenuItem<CalculatorDispl
assert generic != null;
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;
if ( variables.size() > 1 ) {

View File

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

View File

@ -8,8 +8,14 @@
<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"
a:title="@string/c_settings"
a:icon="@drawable/ab_settings"
a:showAsAction="always"/>
</menu>

View File

@ -48,9 +48,9 @@ public class AndroidCalculatorNotifier implements CalculatorNotifier {
@Override
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);
}*/
}
}
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,
@NotNull Generic generic,
@NotNull Constant xVariable,
@Nullable Constant xVariable,
@Nullable Constant yVariable){
final Intent intent = new Intent();
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.setClass(context, CalculatorPlotActivity.class);
AndroidUtils2.addFlags(intent, false, context);

View File

@ -75,7 +75,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
private PreparedInput preparedInput;
@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
@NotNull
@ -150,32 +150,34 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
final CalculatorEventHolder.Result result = this.lastEventHolder.apply(calculatorEventData);
if (result.isNewAfter()) {
preparedInput = prepareInputFromDisplay(((CalculatorDisplayChangeEventData) data).getNewValue(), null);
this.preparedInput = preparedInput;
final PreparedInput finalPreparedInput = preparedInput;
getUiHandler().post(new Runnable() {
@Override
public void run() {
if (!finalPreparedInput.isError()) {
createChart(finalPreparedInput);
final View view = getView();
if (view != null) {
createGraphicalView(view, finalPreparedInput);
}
} else {
onError();
}
}
});
onNewPreparedInput(preparedInput);
}
}
}
}
private void onNewPreparedInput(@NotNull PreparedInput preparedInput) {
this.preparedInput = preparedInput;
final PreparedInput finalPreparedInput = preparedInput;
getUiHandler().post(new Runnable() {
@Override
public void run() {
if (!finalPreparedInput.isError()) {
createChart(finalPreparedInput);
final View view = getView();
if (view != null) {
createGraphicalView(view, finalPreparedInput);
}
} else {
onError();
}
}
});
}
protected abstract void onError();
protected abstract void createGraphicalView(@NotNull View view, @NotNull PreparedInput preparedInput);
@ -230,12 +232,32 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
public void onCreateOptionsMenu(Menu menu, MenuInflater 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();
if (activity != null) {
fragmentMenu.onCreateOptionsMenu(activity, menu);
}
}
protected abstract boolean is3dPlotSupported();
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
@ -266,7 +288,13 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
final Generic expression = displayState.getResult();
if (CalculatorUtils.isPlotPossible(expression, displayState.getOperation())) {
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(expression));
final Constant xVariable = variables.get(0);
final Constant xVariable;
if ( variables.size() > 0 ) {
xVariable = variables.get(0);
} else {
xVariable = null;
}
final Constant yVariable;
if ( variables.size() > 1 ) {
@ -275,7 +303,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
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);
}
}
@ -293,7 +321,13 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
try {
final PreparedExpression preparedExpression = ToJsclTextProcessor.getInstance().process(input.getExpression());
final Generic expression = Expression.valueOf(preparedExpression.getExpression());
final Constant xVar = new Constant(input.getXVariableName());
final Constant xVar;
if (input.getXVariableName() != null) {
xVar = new Constant(input.getXVariableName());
} else {
xVar = null;
}
final Constant yVar;
if (input.getYVariableName() != null) {
@ -416,6 +450,8 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
private boolean fromInputArgs;
private boolean force3d = false;
@NotNull
private PlotBoundaries plotBoundaries = PlotBoundaries.newDefaultInstance();
@ -425,7 +461,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
@NotNull
public static PreparedInput newInstance(@NotNull Input input,
@NotNull Generic expression,
@NotNull Constant xVariable,
@Nullable Constant xVariable,
@Nullable Constant yVariable,
boolean fromInputArgs,
@NotNull PlotBoundaries plotBoundaries) {
@ -454,6 +490,17 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
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() {
return fromInputArgs;
}
@ -483,8 +530,12 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
return yVariable;
}
public boolean isForce3d() {
return force3d;
}
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
private String expression;
@NotNull
@Nullable
private String xVariableName;
@Nullable
private String yVariableName;
public Input(@NotNull String expression,
@NotNull String xVariableName,
@Nullable String xVariableName,
@Nullable String yVariableName) {
this.expression = expression;
this.xVariableName = xVariableName;
@ -512,7 +563,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
return expression;
}
@NotNull
@Nullable
public String getXVariableName() {
return xVariableName;
}

View File

@ -57,19 +57,21 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
final Constant xVariable = preparedInput.getXVariable();
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>();
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) {
case 0:
case 1:
graphView = new Graph2dView(getActivity());
if (preparedInput.isForce3d()) {
graphView = new Graph3dView(getActivity());
} else {
graphView = new Graph2dView(getActivity());
}
break;
case 2:
graphView = new Graph3dView(getActivity());
@ -91,6 +93,11 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
protected void createChart(@NotNull PreparedInput preparedInput) {
}
@Override
protected boolean is3dPlotSupported() {
return true;
}
@Override
public void onResume() {
@ -133,19 +140,35 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
@NotNull
protected final Generic expression;
@NotNull
@Nullable
protected final Constant xVariable;
@Nullable
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.expression = expression;
this.xVariable = xVariable;
this.yVariable = yVariable;
}
@Override
public final double eval() {
if (constant == null) {
constant = eval0();
}
return constant;
}
protected abstract double eval0();
@Override
public final int arity() {
return arity;
@ -157,11 +180,16 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
private RealArityFunction(int arity,
@NotNull Generic expression,
@NotNull Constant xVariable,
@Nullable Constant xVariable,
@Nullable Constant yVariable) {
super(arity, expression, xVariable, yVariable);
}
@Override
public double eval0() {
return PlotUtils.calculatorExpression(expression).realPart();
}
@Override
public double eval(double x) {
return PlotUtils.calculatorExpression(expression, xVariable, x).realPart();
@ -177,11 +205,16 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
private ImaginaryArityFunction(int arity,
@NotNull Generic expression,
@NotNull Constant xVariable,
@Nullable Constant xVariable,
@Nullable Constant yVariable) {
super(arity, expression, xVariable, yVariable);
}
@Override
public double eval0() {
return PlotUtils.calculatorExpression(expression).imaginaryPart();
}
@Override
public double eval(double x) {
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
@Override
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;
@NotNull
@ -57,6 +60,16 @@ public class FunctionLineDef {
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
public static FunctionLineDef newDefaultInstance() {
return new FunctionLineDef();
@ -76,6 +89,11 @@ public class FunctionLineDef {
return lineWidth;
}
@NotNull
public FunctionLineColorType getLineColorType() {
return lineColorType;
}
public void applyToPaint(@NotNull Paint paint) {
paint.setColor(lineColor);
paint.setStyle(Paint.Style.STROKE);

View File

@ -7,8 +7,10 @@ import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.AndroidUtils2;
import javax.microedition.khronos.egl.*;
@ -23,14 +25,15 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
private boolean paused;
private EGL10 egl;
private EGLDisplay display;
private EGLConfig config;
private EGLConfig config;
private EGLSurface surface;
private EGLContext eglContext;
private GL11 gl;
protected int width, height;
private boolean mIsLooping;
private volatile boolean looping;
abstract void onDrawFrame(GL10 gl);
abstract void onSurfaceCreated(GL10 gl, int width, int height);
public String captureScreenshot() {
@ -48,15 +51,22 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
buf.asIntBuffer().get(data);
buf = null;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
bitmap.setPixels(data, size-width, -width, 0, 0, width, height);
bitmap.setPixels(data, size - width, -width, 0, 0, width, height);
return bitmap;
}
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
glDraw();
@NotNull
private final Handler uiHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
glDraw();
break;
default:
Log.e("GLView", "Incorrect message id: " + msg.what);
}
};
}
};
public GLView(Context context) {
super(context);
@ -69,11 +79,11 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
}
private void init() {
SurfaceHolder holder = getHolder();
final SurfaceHolder holder = getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
holder.addCallback(this);
}
public void onResume() {
paused = false;
if (hasSurface) {
@ -90,7 +100,7 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] ver = new int[2];
egl.eglInitialize(display, ver);
int[] configSpec = {EGL10.EGL_NONE};
EGLConfig[] configOut = new EGLConfig[1];
int[] nConfig = new int[1];
@ -129,7 +139,7 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
if (egl.eglGetError() == EGL11.EGL_CONTEXT_LOST) {
paused = true;
}
if (mIsLooping) {
if (looping) {
requestDraw();
}
}
@ -137,9 +147,9 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
public void surfaceCreated(SurfaceHolder holder) {
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
this.width = width;
this.width = width;
this.height = height;
boolean doInit = !hasSurface && !paused;
hasSurface = true;
@ -154,24 +164,24 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
}
public void startLooping() {
if (!mIsLooping) {
mIsLooping = true;
if (!looping) {
looping = true;
glDraw();
}
}
public void stopLooping() {
if (mIsLooping) {
mIsLooping = false;
if (looping) {
looping = false;
}
}
public boolean isLooping() {
return mIsLooping;
return looping;
}
public void requestDraw() {
handler.sendEmptyMessage(1);
uiHandler.sendEmptyMessage(1);
}
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) {
//BGR-565 to RGB-565
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();
bitmap.copyPixelsFromBuffer(buf);

View File

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

View File

@ -3,16 +3,18 @@
package org.solovyev.android.calculator.plot;
import android.content.Context;
import android.graphics.Color;
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.ArrayList;
import java.util.Collections;
import java.util.List;
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 float zoomLevel = 1, targetZoom, zoomStep = 0, currentZoom;
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) {
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 angleX, angleY;
private boolean isDirty;
private Function function;
private static final float DISTANCE = 15f;
void setRotation(float x, float y) {
@ -164,14 +170,18 @@ public class Graph3dView extends GLView implements GraphView {
@Override
public void init(@NotNull FunctionViewDef functionViewDef) {
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<FunctionPlotDef>emptyList());
}
public void setFunctionPlotDefs(@NotNull List<FunctionPlotDef> functionPlotDefs) {
if (functionPlotDefs.size() > 0) {
function = functionPlotDefs.get(0).getFunction();
} else {
function = null;
for (FunctionPlotDef functionPlotDef: functionPlotDefs) {
final int arity = functionPlotDef.getFunction().arity();
if (arity != 0 && arity != 1 && arity != 2) {
throw new IllegalArgumentException("Function must have arity 0 or 1 or 2 for 3d plot!");
}
}
this.graphViewHelper = this.graphViewHelper.copy(functionPlotDefs);
zoomLevel = 1;
isDirty = true;
}
@ -180,10 +190,13 @@ public class Graph3dView extends GLView implements GraphView {
public void onSurfaceCreated(GL10 gl, int width, int height) {
gl.glDisable(GL10.GL_DITHER);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0, 0, 0, 1);
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.glDisable(GL10.GL_LIGHTING);
graph = new Graph3d((GL11) gl, useHighQuality3d);
ensureGraphsSize((GL11) gl);
isDirty = true;
angleX = .5f;
angleY = 0;
@ -201,7 +214,11 @@ public class Graph3dView extends GLView implements GraphView {
currentZoom = zoomLevel;
}
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;
}
@ -233,7 +250,15 @@ public class Graph3dView extends GLView implements GraphView {
Matrix.multiplyMM(matrix3, 0, matrix2, 0, matrix1, 0);
gl.glMultMatrixf(matrix3, 0);
System.arraycopy(matrix3, 0, matrix1, 0, 16);
graph.draw(gl);
for (Graph3d graph : graphs) {
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) {

View File

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

View File

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

View File

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