new plotter
This commit is contained in:
parent
2c0803da74
commit
b0608b8204
@ -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>
|
@ -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 ) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -57,6 +57,11 @@ public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean is3dPlotSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected PlotBoundaries getPlotBoundaries() {
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
||||
|
@ -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.*;
|
||||
@ -28,9 +30,10 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
|
||||
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,7 +79,7 @@ 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);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -139,7 +149,7 @@ abstract class GLView extends SurfaceView implements SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -162,6 +162,9 @@ public enum CalculatorEventType {
|
||||
//org.solovyev.android.calculator.plot.PlotInput
|
||||
plot_graph,
|
||||
|
||||
|
||||
plot_graph_3d,
|
||||
|
||||
//String
|
||||
show_evaluation_error;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user