new plotter

This commit is contained in:
Sergey Solovyev 2013-01-13 00:26:07 +04:00
parent cbfdf237f0
commit 3f09528f45
42 changed files with 1769 additions and 1046 deletions

View File

@ -2,17 +2,12 @@ package org.solovyev.android.calculator;
import android.content.Context; import android.content.Context;
import jscl.math.Generic; import jscl.math.Generic;
import jscl.math.function.Constant;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.core.R; import org.solovyev.android.calculator.core.R;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.plot.PlotInput;
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog; import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
import org.solovyev.android.menu.LabeledMenuItem; import org.solovyev.android.menu.LabeledMenuItem;
import java.util.ArrayList;
import java.util.List;
/** /**
* User: Solovyev_S * User: Solovyev_S
* Date: 21.09.12 * Date: 21.09.12
@ -81,31 +76,17 @@ public enum CalculatorDisplayMenuItem implements LabeledMenuItem<CalculatorDispl
plot(R.string.c_plot) { plot(R.string.c_plot) {
@Override @Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) { public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
final Generic generic = data.getResult(); final Generic expression = data.getResult();
assert generic != null; assert expression != null;
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(generic)); Locator.getInstance().getPlotter().removeAllUnpinned();
Locator.getInstance().getPlotter().addFunction(expression);
final Constant xVariable; Locator.getInstance().getPlotter().plot();
if ( variables.size() > 0 ) {
xVariable = variables.get(0);
} else {
xVariable = null;
}
final Constant yVariable;
if ( variables.size() > 1 ) {
yVariable = variables.get(1);
} else {
yVariable = null;
}
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.plot_graph, PlotInput.newInstance(generic, xVariable, yVariable), context);
} }
@Override @Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) { protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return CalculatorUtils.isPlotPossible(generic, operation); return Locator.getInstance().getPlotter().isPlotPossible(generic);
} }
}; };

View File

@ -8,7 +8,6 @@ import org.solovyev.android.AndroidUtils;
import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine; import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
import org.solovyev.android.calculator.plot.GraphLineColor; import org.solovyev.android.calculator.plot.GraphLineColor;
import org.solovyev.android.calculator.plot.ParcelablePlotInput;
import org.solovyev.android.prefs.BooleanPreference; import org.solovyev.android.prefs.BooleanPreference;
import org.solovyev.android.prefs.IntegerPreference; import org.solovyev.android.prefs.IntegerPreference;
import org.solovyev.android.prefs.LongPreference; import org.solovyev.android.prefs.LongPreference;
@ -16,10 +15,8 @@ import org.solovyev.android.prefs.Preference;
import org.solovyev.android.prefs.R; import org.solovyev.android.prefs.R;
import org.solovyev.android.prefs.StringPreference; import org.solovyev.android.prefs.StringPreference;
import org.solovyev.android.view.VibratorContainer; import org.solovyev.android.view.VibratorContainer;
import org.solovyev.common.ListMapper;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.util.List;
import java.util.Locale; import java.util.Locale;
/** /**

View File

@ -0,0 +1,164 @@
package org.solovyev.android.calculator.plot;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorPreferences;
import java.util.List;
/**
* User: serso
* Date: 1/12/13
* Time: 11:03 PM
*/
public class AndroidCalculatorPlotter implements CalculatorPlotter, SharedPreferences.OnSharedPreferenceChangeListener {
@NotNull
private final CalculatorPlotter plotter;
public AndroidCalculatorPlotter(@NotNull Context context,
@NotNull CalculatorPlotter plotter) {
this.plotter = plotter;
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
preferences.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(preferences, CalculatorPreferences.Graph.plotImag.getKey());
onSharedPreferenceChanged(preferences, CalculatorPreferences.Graph.lineColorReal.getKey());
onSharedPreferenceChanged(preferences, CalculatorPreferences.Graph.lineColorImag.getKey());
}
@Override
@NotNull
public PlotData getPlotData() {
return plotter.getPlotData();
}
@Override
public boolean addFunction(@NotNull Generic expression) {
return plotter.addFunction(expression);
}
@Override
public boolean addFunction(@NotNull PlotFunction plotFunction) {
return plotter.addFunction(plotFunction);
}
@Override
public boolean addFunction(@NotNull XyFunction xyFunction) {
return plotter.addFunction(xyFunction);
}
@Override
public boolean addFunction(@NotNull XyFunction xyFunction, @NotNull PlotFunctionLineDef functionLineDef) {
return plotter.addFunction(xyFunction, functionLineDef);
}
@Override
public boolean updateFunction(@NotNull PlotFunction newFunction) {
return plotter.updateFunction(newFunction);
}
@Override
public boolean updateFunction(@NotNull XyFunction xyFunction, @NotNull PlotFunctionLineDef functionLineDef) {
return plotter.updateFunction(xyFunction, functionLineDef);
}
@Override
public boolean removeFunction(@NotNull PlotFunction plotFunction) {
return plotter.removeFunction(plotFunction);
}
@Override
public boolean removeFunction(@NotNull XyFunction xyFunction) {
return plotter.removeFunction(xyFunction);
}
@Override
public void pin(@NotNull PlotFunction plotFunction) {
plotter.pin(plotFunction);
}
@Override
public void unpin(@NotNull PlotFunction plotFunction) {
plotter.unpin(plotFunction);
}
@Override
public void show(@NotNull PlotFunction plotFunction) {
plotter.show(plotFunction);
}
@Override
public void hide(@NotNull PlotFunction plotFunction) {
plotter.hide(plotFunction);
}
@Override
public void clearAllFunctions() {
plotter.clearAllFunctions();
}
@Override
@NotNull
public List<PlotFunction> getFunctions() {
return plotter.getFunctions();
}
@Override
@NotNull
public List<PlotFunction> getVisibleFunctions() {
return plotter.getVisibleFunctions();
}
@Override
public void plot() {
plotter.plot();
}
@Override
public boolean isPlotPossible(@NotNull Generic expression) {
return plotter.isPlotPossible(expression);
}
@Override
public void setPlot3d(boolean plot3d) {
plotter.setPlot3d(plot3d);
}
@Override
public void removeAllUnpinned() {
plotter.removeAllUnpinned();
}
@Override
public void setPlotImag(boolean plotImag) {
plotter.setPlotImag(plotImag);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (CalculatorPreferences.Graph.plotImag.getKey().equals(key)) {
setPlotImag(CalculatorPreferences.Graph.plotImag.getPreference(preferences));
}
if (CalculatorPreferences.Graph.lineColorReal.getKey().equals(key)) {
setRealLineColor(CalculatorPreferences.Graph.lineColorReal.getPreference(preferences));
}
if (CalculatorPreferences.Graph.lineColorImag.getKey().equals(key)) {
setImagLineColor(CalculatorPreferences.Graph.lineColorImag.getPreference(preferences));
}
}
public void setImagLineColor(@NotNull GraphLineColor imagLineColor) {
plotter.setImagLineColor(imagLineColor);
}
public void setRealLineColor(@NotNull GraphLineColor realLineColor) {
plotter.setRealLineColor(realLineColor);
}
}

View File

@ -0,0 +1,63 @@
package org.solovyev.android.calculator.plot;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 1/5/13
* Time: 7:37 PM
*/
public enum AndroidPlotLineStyle {
solid(PlotLineStyle.solid) {
@Override
public void applyToPaint(@NotNull Paint paint) {
paint.setPathEffect(null);
}
},
dashed(PlotLineStyle.dashed) {
@Override
public void applyToPaint(@NotNull Paint paint) {
paint.setPathEffect(new DashPathEffect(new float[]{10, 20}, 0));
}
},
dotted(PlotLineStyle.dotted) {
@Override
public void applyToPaint(@NotNull Paint paint) {
paint.setPathEffect(new DashPathEffect(new float[]{5, 1}, 0));
}
},
dash_dotted(PlotLineStyle.dash_dotted) {
@Override
public void applyToPaint(@NotNull Paint paint) {
paint.setPathEffect(new DashPathEffect(new float[]{10, 20, 5, 1}, 0));
}
};
@NotNull
private final PlotLineStyle plotLineStyle;
AndroidPlotLineStyle(@NotNull PlotLineStyle plotLineStyle) {
this.plotLineStyle = plotLineStyle;
}
public abstract void applyToPaint(@NotNull Paint paint);
@Nullable
public static AndroidPlotLineStyle valueOf(@NotNull PlotLineStyle plotLineStyle) {
for (AndroidPlotLineStyle androidPlotLineStyle : values()) {
if ( androidPlotLineStyle.plotLineStyle == plotLineStyle ) {
return androidPlotLineStyle;
}
}
return null;
}
}

View File

@ -1,156 +0,0 @@
package org.solovyev.android.calculator.plot;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Parcel;
import android.os.Parcelable;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 1/5/13
* Time: 7:41 PM
*/
public class FunctionLineDef implements Parcelable {
/*
**********************************************************************
*
* CONSTANTS
*
**********************************************************************
*/
@NotNull
private static final Float DEFAULT_LINE_WIDTH = -1f;
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
private static final Creator<FunctionLineDef> CREATOR = new Creator<FunctionLineDef>() {
@Override
public FunctionLineDef createFromParcel(@NotNull Parcel in) {
return fromParcel(in);
}
@Override
public FunctionLineDef[] newArray(int size) {
return new FunctionLineDef[size];
}
};
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@NotNull
private FunctionLineColorType lineColorType = FunctionLineColorType.solid;
private int lineColor = Color.WHITE;
@NotNull
private FunctionLineStyle lineStyle = FunctionLineStyle.solid;
private float lineWidth = -DEFAULT_LINE_WIDTH;
private FunctionLineDef() {
}
@NotNull
public static FunctionLineDef newInstance(int lineColor, @NotNull FunctionLineStyle lineStyle) {
final FunctionLineDef result = new FunctionLineDef();
result.lineColor = lineColor;
result.lineStyle = lineStyle;
return result;
}
@NotNull
public static FunctionLineDef newInstance(int lineColor, @NotNull FunctionLineStyle lineStyle, float lineWidth) {
final FunctionLineDef result = new FunctionLineDef();
result.lineColor = lineColor;
result.lineStyle = lineStyle;
result.lineWidth = lineWidth;
return result;
}
@NotNull
public static FunctionLineDef 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;
}
public static FunctionLineDef fromParcel(@NotNull Parcel in) {
final FunctionLineDef result = new FunctionLineDef();
result.lineColorType = (FunctionLineColorType) in.readSerializable();
result.lineColor = in.readInt();
result.lineStyle = (FunctionLineStyle) in.readSerializable();
result.lineWidth = in.readFloat();
return result;
}
@NotNull
public static FunctionLineDef newDefaultInstance() {
return new FunctionLineDef();
}
public int getLineColor() {
return lineColor;
}
@NotNull
public FunctionLineStyle getLineStyle() {
return lineStyle;
}
public float getLineWidth() {
return lineWidth;
}
@NotNull
public FunctionLineColorType getLineColorType() {
return lineColorType;
}
public void applyToPaint(@NotNull Paint paint) {
paint.setColor(lineColor);
paint.setStyle(Paint.Style.STROKE);
if ( lineWidth == DEFAULT_LINE_WIDTH ) {
paint.setStrokeWidth(0);
} else {
paint.setStrokeWidth(lineWidth);
}
lineStyle.applyToPaint(paint);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NotNull Parcel out, int flags) {
out.writeSerializable(lineColorType);
out.writeInt(lineColor);
out.writeSerializable(lineStyle);
out.writeFloat(lineWidth);
}
}

View File

@ -1,44 +0,0 @@
package org.solovyev.android.calculator.plot;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 1/5/13
* Time: 7:37 PM
*/
public enum FunctionLineStyle {
solid {
@Override
public void applyToPaint(@NotNull Paint paint) {
paint.setPathEffect(null);
}
},
dashed {
@Override
public void applyToPaint(@NotNull Paint paint) {
paint.setPathEffect(new DashPathEffect(new float[] {10, 20}, 0));
}
},
dotted {
@Override
public void applyToPaint(@NotNull Paint paint) {
paint.setPathEffect(new DashPathEffect(new float[] {5, 1}, 0));
}
},
dash_dotted {
@Override
public void applyToPaint(@NotNull Paint paint) {
paint.setPathEffect(new DashPathEffect(new float[] {10, 20, 5, 1}, 0));
}
};
public abstract void applyToPaint(@NotNull Paint paint);
}

View File

@ -1,131 +0,0 @@
package org.solovyev.android.calculator.plot;
import android.os.Parcel;
import android.os.Parcelable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ParcelablePlotInput implements Parcelable {
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
@NotNull
public static Creator<ParcelablePlotInput> CREATOR = new Creator<ParcelablePlotInput>() {
@Override
public ParcelablePlotInput createFromParcel(@NotNull Parcel in) {
return fromParcel(in);
}
@Override
public ParcelablePlotInput[] newArray(int size) {
return new ParcelablePlotInput[size];
}
};
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@NotNull
private String expression;
@Nullable
private String xVariableName;
@Nullable
private String yVariableName;
@Nullable
private FunctionLineDef lineDef;
public ParcelablePlotInput(@NotNull String expression,
@Nullable String xVariableName,
@Nullable String yVariableName) {
this(expression, xVariableName, yVariableName, null);
}
public ParcelablePlotInput(@NotNull String expression,
@Nullable String xVariableName,
@Nullable String yVariableName,
@Nullable FunctionLineDef lineDef) {
this.expression = expression;
this.xVariableName = xVariableName;
this.yVariableName = yVariableName;
this.lineDef = lineDef;
}
@NotNull
public static ParcelablePlotInput fromParcel(@NotNull Parcel in) {
final String expression = in.readString();
final String xVariableName = in.readString();
final String yVariableName = in.readString();
final FunctionLineDef lineDef = in.readParcelable(Thread.currentThread().getContextClassLoader());
return new ParcelablePlotInput(expression, xVariableName, yVariableName, lineDef);
}
@NotNull
public String getExpression() {
return expression;
}
@Nullable
public String getXVariableName() {
return xVariableName;
}
@Nullable
public String getYVariableName() {
return yVariableName;
}
@Nullable
public FunctionLineDef getLineDef() {
return lineDef;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NotNull Parcel out, int flags) {
out.writeString(expression);
out.writeString(xVariableName);
out.writeString(yVariableName);
out.writeParcelable(lineDef, 0);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ParcelablePlotInput)) return false;
final ParcelablePlotInput that = (ParcelablePlotInput) o;
if (!expression.equals(that.expression)) return false;
if (xVariableName != null ? !xVariableName.equals(that.xVariableName) : that.xVariableName != null)
return false;
if (yVariableName != null ? !yVariableName.equals(that.yVariableName) : that.yVariableName != null)
return false;
return true;
}
@Override
public int hashCode() {
int result = expression.hashCode();
result = 31 * result + (xVariableName != null ? xVariableName.hashCode() : 0);
result = 31 * result + (yVariableName != null ? yVariableName.hashCode() : 0);
return result;
}
}

View File

@ -13,12 +13,12 @@ import org.solovyev.android.view.UpdatableViewBuilder;
public class ParcelablePlotInputListItem implements ListItem { public class ParcelablePlotInputListItem implements ListItem {
@NotNull @NotNull
private ParcelablePlotInput plotInput; private XyFunction plotInput;
@NotNull @NotNull
private UpdatableViewBuilder<TextView> viewBuilder; private UpdatableViewBuilder<TextView> viewBuilder;
public ParcelablePlotInputListItem(@NotNull ParcelablePlotInput plotInput) { public ParcelablePlotInputListItem(@NotNull XyFunction plotInput) {
this.plotInput = plotInput; this.plotInput = plotInput;
// todo serso: use correct tag // todo serso: use correct tag
this.viewBuilder = TextViewBuilder.newInstance(R.layout.plot_functions_fragment_list_item, null); this.viewBuilder = TextViewBuilder.newInstance(R.layout.plot_functions_fragment_list_item, null);
@ -52,6 +52,6 @@ public class ParcelablePlotInputListItem implements ListItem {
} }
private void fill(@NotNull TextView textView) { private void fill(@NotNull TextView textView) {
textView.setText(plotInput.getExpression()); textView.setText(plotInput.getExpressionString());
} }
} }

View File

@ -1,54 +0,0 @@
package org.solovyev.android.calculator.plot;
import jscl.math.Generic;
import jscl.math.function.Constant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 12/1/12
* Time: 5:09 PM
*/
public class PlotInput {
@NotNull
private Generic function;
@Nullable
private Constant xVariable;
@Nullable
private Constant yVariable;
public PlotInput() {
}
@NotNull
public static PlotInput newInstance(@NotNull Generic function,
@Nullable Constant xVariable,
@Nullable Constant yVariable) {
PlotInput result = new PlotInput();
result.function = function;
result.xVariable = xVariable;
result.yVariable = yVariable;
return result;
}
@NotNull
public Generic getFunction() {
return function;
}
@Nullable
public Constant getXVariable() {
return xVariable;
}
@Nullable
public Constant getYVariable() {
return yVariable;
}
}

View File

@ -11,8 +11,6 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragmentActivity; import com.actionbarsherlock.app.SherlockFragmentActivity;
import jscl.math.Generic;
import jscl.math.function.Constant;
import org.achartengine.ChartFactory; import org.achartengine.ChartFactory;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -21,16 +19,9 @@ import org.solovyev.android.calculator.about.CalculatorAboutActivity;
import org.solovyev.android.calculator.function.FunctionEditDialogFragment; import org.solovyev.android.calculator.function.FunctionEditDialogFragment;
import org.solovyev.android.calculator.help.CalculatorHelpActivity; import org.solovyev.android.calculator.help.CalculatorHelpActivity;
import org.solovyev.android.calculator.history.CalculatorHistoryActivity; import org.solovyev.android.calculator.history.CalculatorHistoryActivity;
import org.solovyev.android.calculator.math.edit.CalculatorFunctionsActivity; import org.solovyev.android.calculator.math.edit.*;
import org.solovyev.android.calculator.math.edit.CalculatorOperatorsActivity;
import org.solovyev.android.calculator.math.edit.CalculatorVarsActivity;
import org.solovyev.android.calculator.math.edit.CalculatorVarsFragment;
import org.solovyev.android.calculator.math.edit.VarEditDialogFragment;
import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity; import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity;
import org.solovyev.android.calculator.plot.CalculatorPlotActivity; import org.solovyev.android.calculator.plot.CalculatorPlotActivity;
import org.solovyev.android.calculator.plot.CalculatorPlotFragment;
import org.solovyev.android.calculator.plot.ParcelablePlotInput;
import org.solovyev.android.calculator.plot.PlotInput;
import org.solovyev.common.msg.Message; import org.solovyev.common.msg.Message;
import org.solovyev.common.msg.MessageType; import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.StringUtils; import org.solovyev.common.text.StringUtils;
@ -104,14 +95,9 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
context.startActivity(intent); context.startActivity(intent);
} }
public static void plotGraph(@NotNull final Context context, public static void plotGraph(@NotNull final Context context){
@NotNull Generic generic,
@Nullable Constant xVariable,
@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 ParcelablePlotInput input = new ParcelablePlotInput(generic.toString(), xVariable == null ? null : xVariable.getName(), yVariable == null ? null : yVariable.getName());
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);
context.startActivity(intent); context.startActivity(intent);
@ -218,12 +204,10 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
}); });
break; break;
case plot_graph: case plot_graph:
final PlotInput plotInput = (PlotInput) data;
assert plotInput != null;
App.getInstance().getUiThreadExecutor().execute(new Runnable() { App.getInstance().getUiThreadExecutor().execute(new Runnable() {
@Override @Override
public void run() { public void run() {
plotGraph(context, plotInput.getFunction(), plotInput.getXVariable(), plotInput.getYVariable()); plotGraph(context);
} }
}); });
break; break;

View File

@ -19,6 +19,8 @@ import org.solovyev.android.calculator.external.AndroidExternalListenersContaine
import org.solovyev.android.calculator.history.AndroidCalculatorHistory; import org.solovyev.android.calculator.history.AndroidCalculatorHistory;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine; import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
import org.solovyev.android.calculator.onscreen.CalculatorOnscreenStartActivity; import org.solovyev.android.calculator.onscreen.CalculatorOnscreenStartActivity;
import org.solovyev.android.calculator.plot.AndroidCalculatorPlotter;
import org.solovyev.android.calculator.plot.CalculatorPlotterImpl;
import org.solovyev.common.msg.MessageType; import org.solovyev.common.msg.MessageType;
import java.util.ArrayList; import java.util.ArrayList;
@ -118,7 +120,8 @@ public class CalculatorApplication extends android.app.Application implements Sh
new AndroidCalculatorLogger(), new AndroidCalculatorLogger(),
new AndroidCalculatorPreferenceService(this), new AndroidCalculatorPreferenceService(this),
new AndroidCalculatorKeyboard(this, new CalculatorKeyboardImpl(calculator)), new AndroidCalculatorKeyboard(this, new CalculatorKeyboardImpl(calculator)),
new AndroidExternalListenersContainer(calculator)); new AndroidExternalListenersContainer(calculator),
new AndroidCalculatorPlotter(this, new CalculatorPlotterImpl(calculator)));
Locator.getInstance().getCalculator().init(); Locator.getInstance().getCalculator().init();

View File

@ -14,7 +14,6 @@ import org.solovyev.android.calculator.math.edit.CalculatorOperatorsFragment;
import org.solovyev.android.calculator.math.edit.CalculatorVarsFragment; import org.solovyev.android.calculator.math.edit.CalculatorVarsFragment;
import org.solovyev.android.calculator.matrix.CalculatorMatrixEditFragment; import org.solovyev.android.calculator.matrix.CalculatorMatrixEditFragment;
import org.solovyev.android.calculator.plot.CalculatorArityPlotFragment; import org.solovyev.android.calculator.plot.CalculatorArityPlotFragment;
import org.solovyev.android.calculator.plot.CalculatorPlotFragment;
import org.solovyev.android.calculator.plot.CalculatorPlotFunctionsFragment; import org.solovyev.android.calculator.plot.CalculatorPlotFunctionsFragment;
/** /**
@ -32,8 +31,7 @@ public enum CalculatorFragmentType {
variables(CalculatorVarsFragment.class, R.layout.vars_fragment, R.string.c_vars), variables(CalculatorVarsFragment.class, R.layout.vars_fragment, R.string.c_vars),
functions(CalculatorFunctionsFragment.class, R.layout.math_entities_fragment, R.string.c_functions), functions(CalculatorFunctionsFragment.class, R.layout.math_entities_fragment, R.string.c_functions),
operators(CalculatorOperatorsFragment.class, R.layout.math_entities_fragment, R.string.c_operators), operators(CalculatorOperatorsFragment.class, R.layout.math_entities_fragment, R.string.c_operators),
plotter(CalculatorPlotFragment.class, R.layout.plot_fragment, R.string.c_graph), plotter(CalculatorArityPlotFragment.class, R.layout.plot_fragment, R.string.c_graph),
plotter_2(CalculatorArityPlotFragment.class, R.layout.plot_fragment, R.string.c_graph),
// todo serso: strings // todo serso: strings
plotter_functions(CalculatorPlotFunctionsFragment.class, R.layout.plot_functions_fragment, R.string.c_graph), plotter_functions(CalculatorPlotFunctionsFragment.class, R.layout.plot_functions_fragment, R.string.c_graph),

View File

@ -2,35 +2,18 @@ package org.solovyev.android.calculator.plot;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Paint;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View; import android.view.View;
import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.MenuItem;
import jscl.math.Expression;
import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.text.ParseException;
import org.achartengine.renderer.XYMultipleSeriesRenderer; import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorApplication; import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.CalculatorDisplayChangeEventData;
import org.solovyev.android.calculator.CalculatorDisplayViewState;
import org.solovyev.android.calculator.CalculatorEventData;
import org.solovyev.android.calculator.CalculatorEventHolder;
import org.solovyev.android.calculator.CalculatorEventListener;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.CalculatorFragment;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.CalculatorUtils;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.PreparedExpression;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.ToJsclTextProcessor;
import org.solovyev.android.menu.ActivityMenu; import org.solovyev.android.menu.ActivityMenu;
import org.solovyev.android.menu.IdentifiableMenuItem; import org.solovyev.android.menu.IdentifiableMenuItem;
import org.solovyev.android.menu.ListActivityMenu; import org.solovyev.android.menu.ListActivityMenu;
@ -38,6 +21,7 @@ import org.solovyev.android.sherlock.menu.SherlockMenuHelper;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -59,8 +43,6 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
protected static final String TAG = "CalculatorPlotFragment"; protected static final String TAG = "CalculatorPlotFragment";
public static final String INPUT = "plot_input";
protected static final String PLOT_BOUNDARIES = "plot_boundaries"; protected static final String PLOT_BOUNDARIES = "plot_boundaries";
private static final int DEFAULT_MIN_NUMBER = -10; private static final int DEFAULT_MIN_NUMBER = -10;
@ -75,9 +57,6 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
********************************************************************** **********************************************************************
*/ */
@Nullable
private ParcelablePlotInput input;
private int bgColor; private int bgColor;
// thread for applying UI changes // thread for applying UI changes
@ -85,7 +64,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
private final Handler uiHandler = new Handler(); private final Handler uiHandler = new Handler();
@NotNull @NotNull
private PreparedInput preparedInput; private PlotData plotData = new PlotData(Collections.<PlotFunction>emptyList(), false);
@NotNull @NotNull
private ActivityMenu<Menu, MenuItem> fragmentMenu; private ActivityMenu<Menu, MenuItem> fragmentMenu;
@ -107,13 +86,9 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
final Bundle arguments = getArguments(); // todo serso: init variable properly
boolean paneFragment = true;
if (arguments != null) { if (paneFragment) {
input = (ParcelablePlotInput) arguments.getParcelable(INPUT);
}
if (input == null) {
this.bgColor = getResources().getColor(R.color.cpp_pane_background); this.bgColor = getResources().getColor(R.color.cpp_pane_background);
} else { } else {
this.bgColor = getResources().getColor(android.R.color.transparent); this.bgColor = getResources().getColor(android.R.color.transparent);
@ -126,11 +101,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
public void onViewCreated(View view, Bundle savedInstanceState) { public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
if (input == null) { this.plotData = Locator.getInstance().getPlotter().getPlotData();
this.preparedInput = prepareInputFromDisplay(Locator.getInstance().getDisplay().getViewState(), savedInstanceState);
} else {
this.preparedInput = prepareInput(input, true, savedInstanceState);
}
} }
@Override @Override
@ -150,42 +121,31 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
createChart(preparedInput); createChart(plotData);
createGraphicalView(getView(), preparedInput); createGraphicalView(getView(), plotData);
} }
@Override @Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable final Object data) { public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable final Object data) {
if (calculatorEventType.isOfType(CalculatorEventType.display_state_changed)) { if (calculatorEventType.isOfType(CalculatorEventType.plot_data_changed)) {
PreparedInput preparedInput = getPreparedInput();
if (!preparedInput.isFromInputArgs()) {
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); onNewPlotData((PlotData) data);
onNewPreparedInput(preparedInput);
}
} }
} }
} }
private void onNewPreparedInput(@NotNull PreparedInput preparedInput) { private void onNewPlotData(@NotNull final PlotData plotData) {
this.preparedInput = preparedInput; this.plotData = plotData;
final PreparedInput finalPreparedInput = preparedInput;
getUiHandler().post(new Runnable() { getUiHandler().post(new Runnable() {
@Override @Override
public void run() { public void run() {
createChart(plotData);
if (!finalPreparedInput.isError()) {
createChart(finalPreparedInput);
final View view = getView(); final View view = getView();
if (view != null) { if (view != null) {
createGraphicalView(view, finalPreparedInput); createGraphicalView(view, plotData);
}
} else {
onError();
} }
} }
}); });
@ -193,9 +153,9 @@ 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 PlotData plotData);
protected abstract void createChart(@NotNull PreparedInput preparedInput); protected abstract void createChart(@NotNull PlotData plotData);
protected double getMaxValue(@Nullable PlotBoundaries plotBoundaries) { protected double getMaxValue(@Nullable PlotBoundaries plotBoundaries) {
@ -219,11 +179,6 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
return uiHandler; return uiHandler;
} }
@NotNull
public PreparedInput getPreparedInput() {
return preparedInput;
}
public int getBgColor() { public int getBgColor() {
return bgColor; return bgColor;
} }
@ -257,7 +212,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
@Override @Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) { public void onClick(@NotNull MenuItem data, @NotNull Context context) {
onNewPreparedInput(PreparedInput.force3dInstance(preparedInput)); Locator.getInstance().getPlotter().setPlot3d(true);
} }
}); });
} }
@ -294,82 +249,6 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
********************************************************************** **********************************************************************
*/ */
@NotNull
protected static PreparedInput prepareInputFromDisplay(@NotNull CalculatorDisplayViewState displayState, @Nullable Bundle savedInstanceState) {
try {
if (displayState.isValid() && displayState.getResult() != null) {
final Generic expression = displayState.getResult();
if (CalculatorUtils.isPlotPossible(expression, displayState.getOperation())) {
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(expression));
final Constant xVariable;
if ( variables.size() > 0 ) {
xVariable = variables.get(0);
} else {
xVariable = null;
}
final Constant yVariable;
if ( variables.size() > 1 ) {
yVariable = variables.get(1);
} else {
yVariable = null;
}
final ParcelablePlotInput input = new ParcelablePlotInput(expression.toString(), xVariable == null ? null : xVariable.getName(), yVariable == null ? null : yVariable.getName());
return prepareInput(input, false, savedInstanceState);
}
}
} catch (RuntimeException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
return PreparedInput.newErrorInstance(false);
}
@NotNull
private static PreparedInput prepareInput(@NotNull ParcelablePlotInput input, boolean fromInputArgs, @Nullable Bundle savedInstanceState) {
PreparedInput result;
try {
final PreparedExpression preparedExpression = ToJsclTextProcessor.getInstance().process(input.getExpression());
final Generic expression = Expression.valueOf(preparedExpression.getExpression());
final Constant xVar;
if (input.getXVariableName() != null) {
xVar = new Constant(input.getXVariableName());
} else {
xVar = null;
}
final Constant yVar;
if (input.getYVariableName() != null) {
yVar = new Constant(input.getYVariableName());
} else {
yVar = null;
}
PlotBoundaries plotBoundaries = null;
if (savedInstanceState != null) {
plotBoundaries = (PlotBoundaries) savedInstanceState.getSerializable(PLOT_BOUNDARIES);
}
if ( plotBoundaries == null ) {
plotBoundaries = PlotBoundaries.newDefaultInstance();
}
result = PreparedInput.newInstance(input, expression, xVar, yVar, fromInputArgs, plotBoundaries);
} catch (ParseException e) {
result = PreparedInput.newErrorInstance(fromInputArgs);
Locator.getInstance().getNotifier().showMessage(e);
} catch (CalculatorParseException e) {
result = PreparedInput.newErrorInstance(fromInputArgs);
Locator.getInstance().getNotifier().showMessage(e);
}
return result;
}
private static enum PlotMenu implements IdentifiableMenuItem<MenuItem> { private static enum PlotMenu implements IdentifiableMenuItem<MenuItem> {
preferences(R.id.menu_plot_settings) { preferences(R.id.menu_plot_settings) {
@ -447,108 +326,19 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
} }
} }
public static class PreparedInput { public static void applyToPaint(@NotNull PlotFunctionLineDef plotFunctionLineDef, @NotNull Paint paint) {
paint.setColor(plotFunctionLineDef.getLineColor());
paint.setStyle(Paint.Style.STROKE);
@Nullable if ( plotFunctionLineDef.getLineWidth() == PlotFunctionLineDef.DEFAULT_LINE_WIDTH ) {
private ParcelablePlotInput input; paint.setStrokeWidth(0);
@Nullable
private Generic expression;
@Nullable
private Constant xVariable;
@Nullable
private Constant yVariable;
private boolean fromInputArgs;
private boolean force3d = false;
@NotNull
private PlotBoundaries plotBoundaries = PlotBoundaries.newDefaultInstance();
private PreparedInput() {
}
@NotNull
public static PreparedInput newInstance(@NotNull ParcelablePlotInput input,
@NotNull Generic expression,
@Nullable Constant xVariable,
@Nullable Constant yVariable,
boolean fromInputArgs,
@NotNull PlotBoundaries plotBoundaries) {
final PreparedInput result = new PreparedInput();
result.input = input;
result.expression = expression;
result.xVariable = xVariable;
result.yVariable = yVariable;
result.fromInputArgs = fromInputArgs;
result.plotBoundaries = plotBoundaries;
return result;
}
@NotNull
public static PreparedInput newErrorInstance(boolean fromInputArgs) {
final PreparedInput result = new PreparedInput();
result.input = null;
result.expression = null;
result.xVariable = null;
result.yVariable = null;
result.fromInputArgs = fromInputArgs;
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 { } else {
return that; paint.setStrokeWidth(plotFunctionLineDef.getLineWidth());
}
} }
public boolean isFromInputArgs() { final AndroidPlotLineStyle androidPlotLineStyle = AndroidPlotLineStyle.valueOf(plotFunctionLineDef.getLineStyle());
return fromInputArgs; if (androidPlotLineStyle != null) {
} androidPlotLineStyle.applyToPaint(paint);
@Nullable
public ParcelablePlotInput getInput() {
return input;
}
@Nullable
public Generic getExpression() {
return expression;
}
@NotNull
public PlotBoundaries getPlotBoundaries() {
return plotBoundaries;
}
@Nullable
public Constant getXVariable() {
return xVariable;
}
@Nullable
public Constant getYVariable() {
return yVariable;
}
public boolean isForce3d() {
return force3d;
}
public boolean isError() {
return input == null || expression == null;
} }
} }

View File

@ -8,25 +8,25 @@ import org.jetbrains.annotations.NotNull;
* Date: 1/5/13 * Date: 1/5/13
* Time: 7:35 PM * Time: 7:35 PM
*/ */
public class FunctionPlotDef { public class ArityPlotFunction {
@NotNull @NotNull
private Function function; private Function function;
@NotNull @NotNull
private FunctionLineDef lineDef; private PlotFunctionLineDef lineDef;
private FunctionPlotDef() { private ArityPlotFunction() {
} }
@NotNull @NotNull
public static FunctionPlotDef newInstance(@NotNull Function function) { public static ArityPlotFunction newInstance(@NotNull Function function) {
return newInstance(function, FunctionLineDef.newDefaultInstance()); return newInstance(function, PlotFunctionLineDef.newDefaultInstance());
} }
@NotNull @NotNull
public static FunctionPlotDef newInstance(@NotNull Function function, @NotNull FunctionLineDef lineDef) { public static ArityPlotFunction newInstance(@NotNull Function function, @NotNull PlotFunctionLineDef lineDef) {
final FunctionPlotDef result = new FunctionPlotDef(); final ArityPlotFunction result = new ArityPlotFunction();
result.function = function; result.function = function;
result.lineDef = lineDef; result.lineDef = lineDef;
@ -40,7 +40,7 @@ public class FunctionPlotDef {
} }
@NotNull @NotNull
public FunctionLineDef getLineDef() { public PlotFunctionLineDef getLineDef() {
return lineDef; return lineDef;
} }
} }

View File

@ -1,8 +1,6 @@
package org.solovyev.android.calculator.plot; package org.solovyev.android.calculator.plot;
import android.content.SharedPreferences;
import android.graphics.Color; import android.graphics.Color;
import android.preference.PreferenceManager;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import jscl.math.Generic; import jscl.math.Generic;
@ -10,7 +8,6 @@ import jscl.math.function.Constant;
import org.javia.arity.Function; import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorPreferences;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import java.util.ArrayList; import java.util.ArrayList;
@ -38,13 +35,7 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
} }
@Override @Override
protected void createGraphicalView(@NotNull View root, @NotNull PreparedInput preparedInput) { protected void createGraphicalView(@NotNull View root, @NotNull PlotData plotData) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final GraphLineColor realLineColor = CalculatorPreferences.Graph.lineColorReal.getPreference(preferences);
final GraphLineColor imagLineColor = CalculatorPreferences.Graph.lineColorImag.getPreference(preferences);
final boolean plotImag = CalculatorPreferences.Graph.plotImag.getPreference(preferences);
// remove old // remove old
final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout); final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
@ -53,47 +44,42 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
graphContainer.removeView((View) graphView); graphContainer.removeView((View) graphView);
} }
if (!preparedInput.isError()) { final List<ArityPlotFunction> arityFunctions = new ArrayList<ArityPlotFunction>();
final Generic expression = preparedInput.getExpression();
final Constant xVariable = preparedInput.getXVariable(); for (PlotFunction plotFunction : plotData.getFunctions()) {
final Constant yVariable = preparedInput.getYVariable();
final XyFunction xyFunction = plotFunction.getXyFunction();
final Generic expression = xyFunction.getExpression();
final Constant xVariable = xyFunction.getXVariable();
final Constant yVariable = xyFunction.getYVariable();
final int arity = xVariable == null ? 0 : (yVariable == null ? 1 : 2); final int arity = xVariable == null ? 0 : (yVariable == null ? 1 : 2);
final List<FunctionPlotDef> functions = new ArrayList<FunctionPlotDef>(); final Function arityFunction;
if (xyFunction.isImag()) {
functions.add(FunctionPlotDef.newInstance(new RealArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(realLineColor.getColor(), FunctionLineStyle.solid, 3f, FunctionLineColorType.color_map))); arityFunction = new ImaginaryArityFunction(arity, expression, xVariable, yVariable);
if (plotImag) { } else {
functions.add(FunctionPlotDef.newInstance(new ImaginaryArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(imagLineColor.getColor(), FunctionLineStyle.solid, 3f, FunctionLineColorType.color_map))); arityFunction = new RealArityFunction(arity, expression, xVariable, yVariable);
} }
switch (arity) { arityFunctions.add(ArityPlotFunction.newInstance(arityFunction, plotFunction.getPlotFunctionLineDef()));
case 0: }
case 1:
if (preparedInput.isForce3d()) { if ( plotData.isPlot3d() ) {
graphView = new Graph3dView(getActivity()); graphView = new Graph3dView(getActivity());
} else { } else {
graphView = new Graph2dView(getActivity()); graphView = new Graph2dView(getActivity());
} }
break;
case 2:
graphView = new Graph3dView(getActivity());
break;
default:
throw new IllegalArgumentException("Unsupported arity: " + arity);
}
graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor())); graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor()));
graphView.setFunctionPlotDefs(functions); graphView.setFunctionPlotDefs(arityFunctions);
graphContainer.addView((View) graphView); graphContainer.addView((View) graphView);
} else {
onError();
}
} }
@Override @Override
protected void createChart(@NotNull PreparedInput preparedInput) { protected void createChart(@NotNull PlotData plotData) {
} }
@Override @Override

View File

@ -35,6 +35,6 @@ public class CalculatorPlotActivity extends CalculatorFragmentActivity {
@NotNull @NotNull
public static CalculatorFragmentType getPlotterFragmentType() { public static CalculatorFragmentType getPlotterFragmentType() {
return CalculatorFragmentType.plotter_2; return CalculatorFragmentType.plotter;
} }
} }

View File

@ -1,235 +0,0 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.plot;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.view.View;
import android.view.ViewGroup;
import jscl.math.Generic;
import jscl.math.function.Constant;
import org.achartengine.chart.XYChart;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.tools.PanListener;
import org.achartengine.tools.ZoomEvent;
import org.achartengine.tools.ZoomListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorPreferences;
import org.solovyev.android.calculator.R;
import org.solovyev.common.MutableObject;
/**
* User: serso
* Date: 12/1/11
* Time: 12:40 AM
*/
public class CalculatorPlotFragment extends AbstractCalculatorPlotFragment {
public static final long EVAL_DELAY_MILLIS = 200;
@Nullable
private XYChart chart;
/**
* The encapsulated graphical view.
*/
@Nullable
private MyGraphicalView graphicalView;
protected void createChart(@NotNull PreparedInput preparedInput) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final Boolean interpolate = CalculatorPreferences.Graph.interpolate.getPreference(preferences);
final GraphLineColor realLineColor = CalculatorPreferences.Graph.lineColorReal.getPreference(preferences);
final GraphLineColor imagLineColor = CalculatorPreferences.Graph.lineColorImag.getPreference(preferences);
//noinspection ConstantConditions
try {
this.chart = PlotUtils.prepareChart(getMinValue(null), getMaxValue(null), preparedInput.getExpression(), preparedInput.getXVariable(), getBgColor(), interpolate, realLineColor.getColor(), imagLineColor.getColor());
} catch (ArithmeticException e) {
PlotUtils.handleArithmeticException(e, CalculatorPlotFragment.this);
}
}
@Override
protected boolean is3dPlotSupported() {
return false;
}
@Nullable
@Override
protected PlotBoundaries getPlotBoundaries() {
if (chart != null) {
return new PlotBoundaries(chart.getRenderer());
} else {
return null;
}
}
protected void createGraphicalView(@NotNull View root, @Nullable PreparedInput preparedInput) {
final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
if (graphicalView != null) {
graphContainer.removeView(graphicalView);
}
if (!getPreparedInput().isError()) {
final XYChart chart = this.chart;
assert chart != null;
final PlotBoundaries plotBoundaries = preparedInput.getPlotBoundaries();
double minValue = getMinValue(plotBoundaries);
double maxValue = getMaxValue(plotBoundaries);
// reverting boundaries (as in prepareChart() we add some cached values )
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double maxY = Double.MIN_VALUE;
for (XYSeries series : chart.getDataset().getSeries()) {
minX = Math.min(minX, series.getMinX());
minY = Math.min(minY, series.getMinY());
maxX = Math.max(maxX, series.getMaxX());
maxY = Math.max(maxY, series.getMaxY());
}
if (preparedInput == null) {
chart.getRenderer().setXAxisMin(Math.max(minX, minValue));
chart.getRenderer().setYAxisMin(Math.max(minY, minValue));
chart.getRenderer().setXAxisMax(Math.min(maxX, maxValue));
chart.getRenderer().setYAxisMax(Math.min(maxY, maxValue));
} else {
chart.getRenderer().setXAxisMin(plotBoundaries.getXMin());
chart.getRenderer().setYAxisMin(plotBoundaries.getYMin());
chart.getRenderer().setXAxisMax(plotBoundaries.getXMax());
chart.getRenderer().setYAxisMax(plotBoundaries.getYMax());
}
graphicalView = new MyGraphicalView(this.getActivity(), chart);
graphicalView.setBackgroundColor(this.getBgColor());
graphicalView.addZoomListener(new ZoomListener() {
@Override
public void zoomApplied(ZoomEvent e) {
updateDataSets(chart);
}
@Override
public void zoomReset() {
updateDataSets(chart);
}
}, true, true);
graphicalView.addPanListener(new PanListener() {
@Override
public void panApplied() {
updateDataSets(chart);
}
});
graphContainer.addView(graphicalView);
updateDataSets(chart, 50);
} else {
graphicalView = null;
}
}
private void updateDataSets(@NotNull final XYChart chart) {
updateDataSets(chart, EVAL_DELAY_MILLIS);
}
private void updateDataSets(@NotNull final XYChart chart, long millisToWait) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final GraphLineColor imagLineColor = CalculatorPreferences.Graph.lineColorImag.getPreference(preferences);
final PreparedInput preparedInput = getPreparedInput();
final Generic expression = preparedInput.getExpression();
final Constant variable = preparedInput.getXVariable();
final MyGraphicalView graphicalView = this.graphicalView;
if (expression != null && variable != null && graphicalView != null) {
pendingOperation.setObject(new Runnable() {
@Override
public void run() {
// allow only one runner at one time
synchronized (pendingOperation) {
//lock all operations with history
if (pendingOperation.getObject() == this) {
getPlotExecutor().execute(new Runnable() {
@Override
public void run() {
final XYMultipleSeriesRenderer dr = chart.getRenderer();
final XYMultipleSeriesDataset dataset = chart.getDataset();
if (dataset != null && dr != null) {
final MyXYSeries realSeries = (MyXYSeries) dataset.getSeriesAt(0);
if (realSeries != null) {
final MyXYSeries imagSeries;
if (dataset.getSeriesCount() > 1) {
imagSeries = (MyXYSeries) dataset.getSeriesAt(1);
} else {
imagSeries = new MyXYSeries(PlotUtils.getImagFunctionName(variable), PlotUtils.DEFAULT_NUMBER_OF_STEPS * 2);
}
try {
if (PlotUtils.addXY(dr.getXAxisMin(), dr.getXAxisMax(), expression, variable, realSeries, imagSeries, true, PlotUtils.DEFAULT_NUMBER_OF_STEPS)) {
if (dataset.getSeriesCount() <= 1) {
dataset.addSeries(imagSeries);
dr.addSeriesRenderer(PlotUtils.createImagRenderer(imagLineColor.getColor()));
}
}
} catch (ArithmeticException e) {
PlotUtils.handleArithmeticException(e, CalculatorPlotFragment.this);
}
getUiHandler().post(new Runnable() {
@Override
public void run() {
graphicalView.repaint();
}
});
}
}
}
});
}
}
}
});
}
getUiHandler().postDelayed(pendingOperation.getObject(), millisToWait);
}
@NotNull
private final MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
/* public void zoomInClickHandler(@NotNull View v) {
this.graphicalView.zoomIn();
}
public void zoomOutClickHandler(@NotNull View v) {
this.graphicalView.zoomOut();
}*/
public void onError() {
this.chart = null;
}
}

View File

@ -12,7 +12,7 @@ final class CalculatorPlotFunctionsController {
private static final CalculatorPlotFunctionsController instance = new CalculatorPlotFunctionsController(); private static final CalculatorPlotFunctionsController instance = new CalculatorPlotFunctionsController();
@NotNull @NotNull
private final List<ParcelablePlotInput> functions = new ArrayList<ParcelablePlotInput>(); private final List<XyFunction> functions = new ArrayList<XyFunction>();
private CalculatorPlotFunctionsController() { private CalculatorPlotFunctionsController() {
} }
@ -23,11 +23,11 @@ final class CalculatorPlotFunctionsController {
} }
@NotNull @NotNull
public List<ParcelablePlotInput> getFunctions() { public List<XyFunction> getFunctions() {
return Collections.unmodifiableList(functions); return Collections.unmodifiableList(functions);
} }
public boolean addFunction(@NotNull ParcelablePlotInput function) { public boolean addFunction(@NotNull XyFunction function) {
if (!functions.contains(function)) { if (!functions.contains(function)) {
return functions.add(function); return functions.add(function);
} else { } else {

View File

@ -23,9 +23,9 @@ public class CalculatorPlotFunctionsFragment extends CalculatorListFragment {
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
final List<ParcelablePlotInputListItem> items = Lists.transform(CalculatorPlotFunctionsController.getInstance().getFunctions(), new Function<ParcelablePlotInput, ParcelablePlotInputListItem>() { final List<ParcelablePlotInputListItem> items = Lists.transform(CalculatorPlotFunctionsController.getInstance().getFunctions(), new Function<XyFunction, ParcelablePlotInputListItem>() {
@Override @Override
public ParcelablePlotInputListItem apply(@Nullable ParcelablePlotInput input) { public ParcelablePlotInputListItem apply(@Nullable XyFunction input) {
return new ParcelablePlotInputListItem(input); return new ParcelablePlotInputListItem(input);
} }
}); });

View File

@ -86,12 +86,12 @@ public class Graph2dView extends View implements GraphView {
@Override @Override
public void init(@NotNull FunctionViewDef functionViewDef) { public void init(@NotNull FunctionViewDef functionViewDef) {
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<FunctionPlotDef>emptyList()); this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<ArityPlotFunction>emptyList());
} }
public void setFunctionPlotDefs(@NotNull List<FunctionPlotDef> functionPlotDefs) { public void setFunctionPlotDefs(@NotNull List<ArityPlotFunction> functionPlotDefs) {
for (FunctionPlotDef functionPlotDef: functionPlotDefs) { for (ArityPlotFunction functionPlotDef: functionPlotDefs) {
final int arity = functionPlotDef.getFunction().arity(); final int arity = functionPlotDef.getFunction().arity();
if (arity != 0 && arity != 1) { if (arity != 0 && arity != 1) {
throw new IllegalArgumentException("Function must have arity 0 or 1 for 2d plot!"); throw new IllegalArgumentException("Function must have arity 0 or 1 for 2d plot!");
@ -348,15 +348,16 @@ public class Graph2dView extends View implements GraphView {
private static StringBuilder b = new StringBuilder(); private static StringBuilder b = new StringBuilder();
private static char[] buf = new char[20]; private static char[] buf = new char[20];
private static StringBuilder format(float fv) { private static StringBuilder format(final float value) {
int pos = 0; int pos = 0;
boolean addDot = false; boolean addDot = false;
int v = Math.round(fv * 100);
boolean isNeg = v < 0; final boolean negative = value < 0;
v = isNeg ? -v : v;
int absValue = Math.round(Math.abs(value) * 100);
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
int digit = v % 10; int digit = absValue % 10;
v /= 10; absValue /= 10;
if (digit != 0 || addDot) { if (digit != 0 || addDot) {
buf[pos++] = (char) ('0' + digit); buf[pos++] = (char) ('0' + digit);
addDot = true; addDot = true;
@ -365,14 +366,14 @@ public class Graph2dView extends View implements GraphView {
if (addDot) { if (addDot) {
buf[pos++] = '.'; buf[pos++] = '.';
} }
if (v == 0) { if (absValue == 0) {
buf[pos++] = '0'; buf[pos++] = '0';
} }
while (v != 0) { while (absValue != 0) {
buf[pos++] = (char) ('0' + (v % 10)); buf[pos++] = (char) ('0' + (absValue % 10));
v /= 10; absValue /= 10;
} }
if (isNeg) { if (negative) {
buf[pos++] = '-'; buf[pos++] = '-';
} }
b.setLength(0); b.setLength(0);
@ -459,6 +460,7 @@ public class Graph2dView extends View implements GraphView {
paint.setPathEffect(null); paint.setPathEffect(null);
} }
{
// AXIS // AXIS
paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor()); paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor());
@ -466,6 +468,8 @@ public class Graph2dView extends View implements GraphView {
canvas.drawLine(x0, 0, x0, height, paint); canvas.drawLine(x0, 0, x0, height, paint);
} }
canvas.drawLine(0, y0, width, y0, paint); canvas.drawLine(0, y0, width, y0, paint);
}
matrix.reset(); matrix.reset();
matrix.preTranslate(-currentX, -currentY); matrix.preTranslate(-currentX, -currentY);
@ -474,23 +478,29 @@ public class Graph2dView extends View implements GraphView {
paint.setAntiAlias(false); paint.setAntiAlias(false);
final List<FunctionPlotDef> functionPlotDefs = graphViewHelper.getFunctionPlotDefs(); {
//GRAPH
final List<ArityPlotFunction> functionPlotDefs = graphViewHelper.getFunctionPlotDefs();
// create path once // create path once
final Path path = new Path(); final Path path = new Path();
for (int i = 0; i < functionPlotDefs.size(); i++) { for (int i = 0; i < functionPlotDefs.size(); i++) {
final FunctionPlotDef fpd = functionPlotDefs.get(i); final ArityPlotFunction fpd = functionPlotDefs.get(i);
computeGraph(fpd.getFunction(), minX, maxX, boundMinY, boundMaxY, graphs.get(i)); computeGraph(fpd.getFunction(), minX, maxX, boundMinY, boundMaxY, graphs.get(i));
graphToPath(graphs.get(i), path); graphToPath(graphs.get(i), path);
path.transform(matrix); path.transform(matrix);
fpd.getLineDef().applyToPaint(paint); AbstractCalculatorPlotFragment.applyToPaint(fpd.getLineDef(), paint);
canvas.drawPath(path, paint); canvas.drawPath(path, paint);
} }
}
lastMinX = minX; lastMinX = minX;
} }

View File

@ -0,0 +1,637 @@
// Copyright (C) 2009-2010 Mihai Preda
package org.solovyev.android.calculator.plot;
import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Scroller;
import android.widget.ZoomButtonsController;
import org.javia.arity.Function;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.AndroidUtils2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Graph2dViewNew extends View implements GraphView {
// view width and height
private int width;
private int height;
@NotNull
private final Matrix matrix = new Matrix();
// paints
@NotNull
private final Paint paint = new Paint();
@NotNull
private final Paint textPaint = new Paint();
@NotNull
private final Paint fillPaint = new Paint();
@NotNull
private GraphViewHelper graphViewHelper = GraphViewHelper.newDefaultInstance();
private final GraphData next = GraphData.newEmptyInstance();
private final GraphData endGraph = GraphData.newEmptyInstance();
@NotNull
private List<GraphData> graphs = new ArrayList<GraphData>(graphViewHelper.getFunctionPlotDefs().size());
private float x0;
private float y0;
private float graphWidth = 20;
private float lastXMin;
private float lastYMin;
private float lastYMax;
private float lastTouchX, lastTouchY;
@NotNull
private TouchHandler touchHandler;
@NotNull
protected ZoomButtonsController zoomController = new ZoomButtonsController(this);
@NotNull
private ZoomTracker zoomTracker = new ZoomTracker();
@NotNull
private Scroller scroller;
public Graph2dViewNew(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public Graph2dViewNew(Context context) {
super(context);
init(context);
}
private void init(Context context) {
touchHandler = new TouchHandler(this);
zoomController.setOnZoomListener(this);
scroller = new Scroller(context);
paint.setAntiAlias(false);
textPaint.setAntiAlias(true);
width = this.getWidth();
height = this.getHeight();
}
public String captureScreenshot() {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
onDraw(canvas);
return AndroidUtils2.saveBitmap(bitmap, GraphView.SCREENSHOT_DIR, "calculator");
}
private void clearAllGraphs() {
for (GraphData graph : graphs) {
graph.clear();
}
while ( graphViewHelper.getFunctionPlotDefs().size() > graphs.size() ) {
graphs.add(GraphData.newEmptyInstance());
}
}
@Override
public void init(@NotNull FunctionViewDef functionViewDef) {
this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<ArityPlotFunction>emptyList());
}
public void setFunctionPlotDefs(@NotNull List<ArityPlotFunction> functionPlotDefs) {
for (ArityPlotFunction functionPlotDef: functionPlotDefs) {
final int arity = functionPlotDef.getFunction().arity();
if (arity != 0 && arity != 1) {
throw new IllegalArgumentException("Function must have arity 0 or 1 for 2d plot!");
}
}
this.graphViewHelper = this.graphViewHelper.copy(functionPlotDefs);
clearAllGraphs();
invalidate();
}
public void onVisibilityChanged(boolean visible) {
}
public void onZoom(boolean zoomIn) {
if (zoomIn) {
if (canZoomIn()) {
graphWidth /= 2;
invalidateGraphs();
}
} else {
if (canZoomOut()) {
graphWidth *= 2;
invalidateGraphs();
}
}
zoomController.setZoomInEnabled(canZoomIn());
zoomController.setZoomOutEnabled(canZoomOut());
}
public void onResume() {
}
public void onPause() {
}
public void onDetachedFromWindow() {
zoomController.setVisible(false);
super.onDetachedFromWindow();
}
protected void onSizeChanged(int w, int h, int ow, int oh) {
width = w;
height = h;
clearAllGraphs();
// points = new float[w+w];
}
protected void onDraw(Canvas canvas) {
if (graphViewHelper.getFunctionPlotDefs().size() == 0) {
return;
}
if (scroller.computeScrollOffset()) {
final float scale = graphWidth / width;
x0 = scroller.getCurrX() * scale;
y0 = scroller.getCurrY() * scale;
if (!scroller.isFinished()) {
invalidate();
}
}
drawGraph(canvas);
}
private float eval(Function f, float x) {
float v = (float) f.eval(x);
// Calculator.log("eval " + x + "; " + v);
if (v < -10000f) {
return -10000f;
}
if (v > 10000f) {
return 10000f;
}
return v;
}
// distance from (x,y) to the line (x1,y1) to (x2,y2), squared, multiplied by 4
/*
private float distance(float x1, float y1, float x2, float y2, float x, float y) {
float dx = x2 - x1;
float dy = y2 - y1;
float mx = x - x1;
float my = y - y1;
float up = dx*my - dy*mx;
return up*up*4/(dx*dx + dy*dy);
}
*/
// distance as above when x==(x1+x2)/2.
private float distance2(float x1, float y1, float x2, float y2, float y) {
final float dx = x2 - x1;
final float dy = y2 - y1;
final float up = dx * (y1 + y2 - y - y);
return up * up / (dx * dx + dy * dy);
}
private void computeGraph(@NotNull Function function,
float xMin,
float xMax,
float yMin,
float yMax,
@NotNull GraphData graph) {
if (function.arity() == 0) {
float v = (float) function.eval();
if (v < -10000f) {
v = -10000f;
}
if (v > 10000f) {
v = 10000f;
}
graph.clear();
graph.push(xMin, v);
graph.push(xMax, v);
return;
}
// prepare graph
if (!graph.empty()) {
if (xMin >= lastXMin) {
graph.eraseBefore(xMin);
} else {
graph.eraseAfter(xMax);
xMax = Math.min(xMax, graph.firstX());
graph.swap(endGraph);
}
}
if (graph.empty()) {
graph.push(xMin, eval(function, xMin));
}
final float scale = width / graphWidth;
final float maxStep = 15.8976f / scale;
final float minStep = .05f / scale;
float ythresh = 1 / scale;
ythresh = ythresh * ythresh;
float leftX, leftY;
float rightX = graph.topX(), rightY = graph.topY();
int nEval = 1;
while (true) {
leftX = rightX;
leftY = rightY;
if (leftX > xMax) {
break;
}
if (next.empty()) {
float x = leftX + maxStep;
next.push(x, eval(function, x));
++nEval;
}
rightX = next.topX();
rightY = next.topY();
next.pop();
if (leftY != leftY && rightY != rightY) { // NaN
continue;
}
float dx = rightX - leftX;
float middleX = (leftX + rightX) / 2;
float middleY = eval(function, middleX);
++nEval;
boolean middleIsOutside = (middleY < leftY && middleY < rightY) || (leftY < middleY && rightY < middleY);
if (dx < minStep) {
// Calculator.log("minStep");
if (middleIsOutside) {
graph.push(rightX, Float.NaN);
}
graph.push(rightX, rightY);
continue;
}
if (middleIsOutside && ((leftY < yMin && rightY > yMax) || (leftY > yMax && rightY < yMin))) {
graph.push(rightX, Float.NaN);
graph.push(rightX, rightY);
// Calculator.log("+-inf");
continue;
}
if (!middleIsOutside) {
/*
float diff = leftY + rightY - middleY - middleY;
float dy = rightY - leftY;
float dx2 = dx*dx;
float distance = dx2*diff*diff/(dx2+dy*dy);
*/
// Calculator.log("" + dx + ' ' + leftY + ' ' + middleY + ' ' + rightY + ' ' + distance + ' ' + ythresh);
if (distance2(leftX, leftY, rightX, rightY, middleY) < ythresh) {
graph.push(rightX, rightY);
continue;
}
}
next.push(rightX, rightY);
next.push(middleX, middleY);
rightX = leftX;
rightY = leftY;
}
if (!endGraph.empty()) {
graph.append(endGraph);
}
long t2 = System.currentTimeMillis();
// Calculator.log("graph points " + graph.size + " evals " + nEval + " time " + (t2-t1));
next.clear();
endGraph.clear();
}
private static void graphToPath(@NotNull GraphData graph, @NotNull Path path) {
final int size = graph.getSize();
final float[] xs = graph.getXs();
final float[] ys = graph.getYs();
path.rewind();
boolean newCurve = true;
for (int i = 0; i < size; i++) {
final float y = ys[i];
final float x = xs[i];
if (y != y) {
newCurve = true;
} else { // !NaN
if (newCurve) {
path.moveTo(x, y);
newCurve = false;
} else {
path.lineTo(x, y);
}
}
}
}
private static final float NTICKS = 15;
private static float stepFactor(float w) {
float f = 1;
while (w / f > NTICKS) {
f *= 10;
}
while (w / f < NTICKS / 10) {
f /= 10;
}
float r = w / f;
if (r < NTICKS / 5) {
return f / 5;
} else if (r < NTICKS / 2) {
return f / 2;
} else {
return f;
}
}
private static StringBuilder b = new StringBuilder();
private static char[] buf = new char[20];
private static StringBuilder format(final float value) {
int pos = 0;
boolean addDot = false;
final boolean negative = value < 0;
int absValue = Math.round(Math.abs(value) * 100);
for (int i = 0; i < 2; ++i) {
int digit = absValue % 10;
absValue /= 10;
if (digit != 0 || addDot) {
buf[pos++] = (char) ('0' + digit);
addDot = true;
}
}
if (addDot) {
buf[pos++] = '.';
}
if (absValue == 0) {
buf[pos++] = '0';
}
while (absValue != 0) {
buf[pos++] = (char) ('0' + (absValue % 10));
absValue /= 10;
}
if (negative) {
buf[pos++] = '-';
}
b.setLength(0);
b.append(buf, 0, pos);
b.reverse();
return b;
}
private void drawGraph(Canvas canvas) {
final float xMin = getXMin();
final float xMax = getXMax(xMin);
float graphHeight = graphWidth * height / width;
float yMin = y0 - graphHeight / 2;
float yMax = yMin + graphHeight;
if (yMin < lastYMin || yMax > lastYMax) {
float halfGraphHeight = graphHeight / 2;
lastYMin = yMin - halfGraphHeight;
lastYMax = yMax + halfGraphHeight;
clearAllGraphs();
}
// set background
canvas.drawColor(graphViewHelper.getFunctionViewDef().getBackgroundColor());
// prepare paint
paint.setStrokeWidth(0);
paint.setAntiAlias(false);
paint.setStyle(Paint.Style.STROKE);
final float scale = width / graphWidth;
float x0 = -xMin * scale;
boolean drawYAxis = true;
if (x0 < 25) {
x0 = 25;
// drawYAxis = false;
} else if (x0 > width - 3) {
x0 = width - 3;
// drawYAxis = false;
}
float y0 = yMax * scale;
if (y0 < 3) {
y0 = 3;
} else if (y0 > height - 15) {
y0 = height - 15;
}
final float tickSize = 3;
final float y2 = y0 + tickSize;
{
// GRID
paint.setPathEffect(new DashPathEffect(new float[]{5, 10}, 0));
paint.setColor(graphViewHelper.getFunctionViewDef().getGridColor());
float step = stepFactor(graphWidth);
// Calculator.log("width " + gwidth + " step " + step);
float v = ((int) (xMin / step)) * step;
textPaint.setColor(graphViewHelper.getFunctionViewDef().getAxisLabelsColor());
textPaint.setTextSize(12);
textPaint.setTextAlign(Paint.Align.CENTER);
float stepScale = step * scale;
for (float x = (v - xMin) * scale; x <= width; x += stepScale, v += step) {
canvas.drawLine(x, 0, x, height, paint);
if (!(-.001f < v && v < .001f)) {
StringBuilder b = format(v);
canvas.drawText(b, 0, b.length(), x, y2 + 10, textPaint);
}
}
final float x1 = x0 - tickSize;
v = ((int) (yMin / step)) * step;
textPaint.setTextAlign(Paint.Align.RIGHT);
for (float y = height - (v - yMin) * scale; y >= 0; y -= stepScale, v += step) {
canvas.drawLine(0, y, width, y, paint);
if (!(-.001f < v && v < .001f)) {
StringBuilder b = format(v);
canvas.drawText(b, 0, b.length(), x1, y + 4, textPaint);
}
}
paint.setPathEffect(null);
}
{
// AXIS
paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor());
if (drawYAxis) {
canvas.drawLine(x0, 0, x0, height, paint);
}
canvas.drawLine(0, y0, width, y0, paint);
}
matrix.reset();
matrix.preTranslate(-this.x0, -this.y0);
matrix.postScale(scale, -scale);
matrix.postTranslate(width / 2, height / 2);
paint.setAntiAlias(false);
{
//GRAPH
final List<ArityPlotFunction> functionPlotDefs = graphViewHelper.getFunctionPlotDefs();
// create path once
final Path path = new Path();
for (int i = 0; i < functionPlotDefs.size(); i++) {
final ArityPlotFunction fpd = functionPlotDefs.get(i);
computeGraph(fpd.getFunction(), xMin, xMax, lastYMin, lastYMax, graphs.get(i));
graphToPath(graphs.get(i), path);
path.transform(matrix);
AbstractCalculatorPlotFragment.applyToPaint(fpd.getLineDef(), paint);
canvas.drawPath(path, paint);
}
}
lastXMin = xMin;
}
private float getXMax(float minX) {
return minX + graphWidth;
}
private float getXMax() {
return getXMax(getXMin());
}
private float getXMin() {
return x0 - graphWidth / 2;
}
private boolean canZoomIn() {
return true;
}
private boolean canZoomOut() {
return true;
}
private void invalidateGraphs() {
clearAllGraphs();
lastYMin = lastYMax = 0;
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return touchHandler != null ? touchHandler.onTouchEvent(event) : super.onTouchEvent(event);
}
public void onTouchDown(float x, float y) {
zoomController.setVisible(true);
if (!scroller.isFinished()) {
scroller.abortAnimation();
}
lastTouchX = x;
lastTouchY = y;
}
public void onTouchMove(float x, float y) {
float deltaX = x - lastTouchX;
float deltaY = y - lastTouchY;
if (deltaX < -1 || deltaX > 1 || deltaY < -1 || deltaY > 1) {
scroll(-deltaX, deltaY);
lastTouchX = x;
lastTouchY = y;
invalidate();
}
}
public void onTouchUp(float x, float y) {
final float scale = width / graphWidth;
float sx = -touchHandler.velocityTracker.getXVelocity();
float sy = touchHandler.velocityTracker.getYVelocity();
final float asx = Math.abs(sx);
final float asy = Math.abs(sy);
if (asx < asy / 3) {
sx = 0;
} else if (asy < asx / 3) {
sy = 0;
}
scroller.fling(Math.round(x0 * scale),
Math.round(y0 * scale),
Math.round(sx), Math.round(sy), -10000, 10000, -10000, 10000);
invalidate();
}
public void onTouchZoomDown(float x1, float y1, float x2, float y2) {
zoomTracker.start(graphWidth, x1, y1, x2, y2);
}
public void onTouchZoomMove(float x1, float y1, float x2, float y2) {
if (!zoomTracker.update(x1, y1, x2, y2)) {
return;
}
float targetGwidth = zoomTracker.value;
if (targetGwidth > .25f && targetGwidth < 200) {
graphWidth = targetGwidth;
}
// scroll(-zoomTracker.moveX, zoomTracker.moveY);
invalidateGraphs();
// Calculator.log("zoom redraw");
}
private void scroll(float deltaX, float deltaY) {
final float scale = graphWidth / width;
float dx = deltaX * scale;
float dy = deltaY * scale;
final float adx = Math.abs(dx);
final float ady = Math.abs(dy);
if (adx < ady / 3) {
dx = 0;
} else if (ady < adx / 3) {
dy = 0;
}
x0 += dx;
y0 += dy;
}
}

View File

@ -92,9 +92,9 @@ class Graph3d {
return bb; return bb;
} }
public void update(@NotNull GL11 gl, @NotNull FunctionPlotDef fpd, float zoom) { public void update(@NotNull GL11 gl, @NotNull ArityPlotFunction fpd, float zoom) {
final Function function = fpd.getFunction(); final Function function = fpd.getFunction();
final FunctionLineDef lineDef = fpd.getLineDef(); final PlotFunctionLineDef 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;
@ -285,7 +285,7 @@ class Graph3d {
return maxAbsZ; return maxAbsZ;
} }
private byte[] prepareFunctionPolygonColors(FunctionLineDef lineDef, float[] vertices, float maxAbsZ) { private byte[] prepareFunctionPolygonColors(PlotFunctionLineDef lineDef, float[] vertices, float maxAbsZ) {
// 4 color components per polygon (color[i] = red, color[i+1] = green, color[i+2] = blue, color[i+3] = alpha ) // 4 color components per polygon (color[i] = red, color[i+1] = green, color[i+2] = blue, color[i+3] = alpha )
final byte colors[] = new byte[polygonsⁿ * COLOR_COMPONENTS_COUNT]; final byte colors[] = new byte[polygonsⁿ * COLOR_COMPONENTS_COUNT];
@ -295,7 +295,7 @@ class Graph3d {
final float z = vertices[j]; final float z = vertices[j];
if (!Float.isNaN(z)) { if (!Float.isNaN(z)) {
if (lineDef.getLineColorType() == FunctionLineColorType.color_map) { if (lineDef.getLineColorType() == PlotFunctionLineColorType.color_map) {
final float color = z / maxAbsZ; final float color = z / maxAbsZ;
final float abs = Math.abs(color); final float abs = Math.abs(color);
colors[i] = floatToByte(color); colors[i] = floatToByte(color);

View File

@ -174,11 +174,11 @@ 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()); this.graphViewHelper = GraphViewHelper.newInstance(functionViewDef, Collections.<ArityPlotFunction>emptyList());
} }
public void setFunctionPlotDefs(@NotNull List<FunctionPlotDef> functionPlotDefs) { public void setFunctionPlotDefs(@NotNull List<ArityPlotFunction> functionPlotDefs) {
for (FunctionPlotDef functionPlotDef: functionPlotDefs) { for (ArityPlotFunction functionPlotDef: functionPlotDefs) {
final int arity = functionPlotDef.getFunction().arity(); final int arity = functionPlotDef.getFunction().arity();
if (arity != 0 && arity != 1 && arity != 2) { if (arity != 0 && arity != 1 && arity != 2) {
throw new IllegalArgumentException("Function must have arity 0 or 1 or 2 for 3d plot!"); throw new IllegalArgumentException("Function must have arity 0 or 1 or 2 for 3d plot!");

View File

@ -13,7 +13,7 @@ public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHa
public void init(@NotNull FunctionViewDef functionViewDef); public void init(@NotNull FunctionViewDef functionViewDef);
public void setFunctionPlotDefs(@NotNull List<FunctionPlotDef> functionPlotDefs); public void setFunctionPlotDefs(@NotNull List<ArityPlotFunction> functionPlotDefs);
public void onPause(); public void onPause();
public void onResume(); public void onResume();

View File

@ -16,7 +16,7 @@ public class GraphViewHelper {
private FunctionViewDef functionViewDef = FunctionViewDef.newDefaultInstance(); private FunctionViewDef functionViewDef = FunctionViewDef.newDefaultInstance();
@NotNull @NotNull
private List<FunctionPlotDef> functionPlotDefs = Collections.emptyList(); private List<ArityPlotFunction> functionPlotDefs = Collections.emptyList();
private GraphViewHelper() { private GraphViewHelper() {
} }
@ -28,7 +28,7 @@ public class GraphViewHelper {
@NotNull @NotNull
public static GraphViewHelper newInstance(@NotNull FunctionViewDef functionViewDef, public static GraphViewHelper newInstance(@NotNull FunctionViewDef functionViewDef,
@NotNull List<FunctionPlotDef> functionPlotDefs) { @NotNull List<ArityPlotFunction> functionPlotDefs) {
final GraphViewHelper result = new GraphViewHelper(); final GraphViewHelper result = new GraphViewHelper();
result.functionViewDef = functionViewDef; result.functionViewDef = functionViewDef;
@ -38,7 +38,7 @@ public class GraphViewHelper {
} }
@NotNull @NotNull
public GraphViewHelper copy(@NotNull List<FunctionPlotDef> newFunctionPlotDefs) { public GraphViewHelper copy(@NotNull List<ArityPlotFunction> newFunctionPlotDefs) {
final GraphViewHelper result = new GraphViewHelper(); final GraphViewHelper result = new GraphViewHelper();
result.functionViewDef = functionViewDef; result.functionViewDef = functionViewDef;
@ -48,7 +48,7 @@ public class GraphViewHelper {
} }
@NotNull @NotNull
public List<FunctionPlotDef> getFunctionPlotDefs() { public List<ArityPlotFunction> getFunctionPlotDefs() {
return functionPlotDefs; return functionPlotDefs;
} }

View File

@ -36,7 +36,7 @@ class ZoomTracker {
return true; return true;
} }
private float distance(float x1, float y1, float x2, float y2) { private static float distance(float x1, float y1, float x2, float y2) {
final float dx = x1-x2; final float dx = x1-x2;
final float dy = y1-y2; final float dy = y1-y2;
// return (float) Math.sqrt(dx*dx+dy*dy); // return (float) Math.sqrt(dx*dx+dy*dy);

View File

@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer; import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
import org.solovyev.android.calculator.history.CalculatorHistory; import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
/** /**
* User: serso * User: serso
@ -16,7 +17,7 @@ import org.solovyev.android.calculator.history.CalculatorHistory;
public class CalculatorTestUtils { public class CalculatorTestUtils {
public static void staticSetUp(@Nullable Context context) throws Exception { public static void staticSetUp(@Nullable Context context) throws Exception {
Locator.getInstance().init(new CalculatorImpl(), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), Mockito.mock(CalculatorHistory.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(CalculatorKeyboard.class), Mockito.mock(CalculatorExternalListenersContainer.class)); Locator.getInstance().init(new CalculatorImpl(), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), Mockito.mock(CalculatorHistory.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(CalculatorKeyboard.class), Mockito.mock(CalculatorExternalListenersContainer.class), Mockito.mock(CalculatorPlotter.class));
Locator.getInstance().getEngine().init(); Locator.getInstance().getEngine().init();
if ( context != null ) { if ( context != null ) {

View File

@ -159,11 +159,8 @@ public enum CalculatorEventType {
show_create_matrix_dialog, show_create_matrix_dialog,
show_create_function_dialog, show_create_function_dialog,
//org.solovyev.android.calculator.plot.PlotInput
plot_graph, plot_graph,
plot_data_changed,
plot_graph_3d,
//String //String
show_evaluation_error; show_evaluation_error;

View File

@ -3,6 +3,7 @@ package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer; import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
import org.solovyev.android.calculator.history.CalculatorHistory; import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
/** /**
* User: Solovyev_S * User: Solovyev_S
@ -19,7 +20,8 @@ public interface CalculatorLocator {
@NotNull CalculatorLogger logger, @NotNull CalculatorLogger logger,
@NotNull CalculatorPreferenceService preferenceService, @NotNull CalculatorPreferenceService preferenceService,
@NotNull CalculatorKeyboard keyboard, @NotNull CalculatorKeyboard keyboard,
@NotNull CalculatorExternalListenersContainer externalListenersContainer); @NotNull CalculatorExternalListenersContainer externalListenersContainer,
@NotNull CalculatorPlotter plotter);
@NotNull @NotNull
Calculator getCalculator(); Calculator getCalculator();
@ -48,6 +50,9 @@ public interface CalculatorLocator {
@NotNull @NotNull
CalculatorLogger getLogger(); CalculatorLogger getLogger();
@NotNull
CalculatorPlotter getPlotter();
@NotNull @NotNull
CalculatorPreferenceService getPreferenceService(); CalculatorPreferenceService getPreferenceService();

View File

@ -4,7 +4,6 @@ import jscl.math.Generic;
import jscl.math.function.Constant; import jscl.math.function.Constant;
import jscl.math.function.IConstant; import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -41,16 +40,4 @@ public final class CalculatorUtils {
return notSystemConstants; return notSystemConstants;
} }
public static boolean isPlotPossible(@NotNull Generic expression, @NotNull JsclOperation operation) {
boolean result = false;
if (operation == JsclOperation.simplify || operation == JsclOperation.numeric) {
int size = getNotSystemConstants(expression).size();
if (size == 0 || size == 1 || size == 2) {
result = true;
}
}
return result;
}
} }

View File

@ -3,6 +3,7 @@ package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer; import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
import org.solovyev.android.calculator.history.CalculatorHistory; import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
/** /**
* User: Solovyev_S * User: Solovyev_S
@ -47,6 +48,9 @@ public class Locator implements CalculatorLocator {
@NotNull @NotNull
private CalculatorExternalListenersContainer calculatorExternalListenersContainer; private CalculatorExternalListenersContainer calculatorExternalListenersContainer;
@NotNull
private CalculatorPlotter calculatorPlotter;
public Locator() { public Locator() {
} }
@ -59,7 +63,8 @@ public class Locator implements CalculatorLocator {
@NotNull CalculatorLogger logger, @NotNull CalculatorLogger logger,
@NotNull CalculatorPreferenceService preferenceService, @NotNull CalculatorPreferenceService preferenceService,
@NotNull CalculatorKeyboard keyboard, @NotNull CalculatorKeyboard keyboard,
@NotNull CalculatorExternalListenersContainer externalListenersContainer) { @NotNull CalculatorExternalListenersContainer externalListenersContainer,
@NotNull CalculatorPlotter plotter) {
this.calculator = calculator; this.calculator = calculator;
this.calculatorEngine = engine; this.calculatorEngine = engine;
@ -69,6 +74,7 @@ public class Locator implements CalculatorLocator {
this.calculatorLogger = logger; this.calculatorLogger = logger;
this.calculatorPreferenceService = preferenceService; this.calculatorPreferenceService = preferenceService;
this.calculatorExternalListenersContainer = externalListenersContainer; this.calculatorExternalListenersContainer = externalListenersContainer;
this.calculatorPlotter = plotter;
calculatorEditor = new CalculatorEditorImpl(this.calculator); calculatorEditor = new CalculatorEditorImpl(this.calculator);
calculatorDisplay = new CalculatorDisplayImpl(this.calculator); calculatorDisplay = new CalculatorDisplayImpl(this.calculator);
@ -134,6 +140,12 @@ public class Locator implements CalculatorLocator {
return calculatorLogger; return calculatorLogger;
} }
@NotNull
@Override
public CalculatorPlotter getPlotter() {
return calculatorPlotter;
}
@NotNull @NotNull
@Override @Override
public CalculatorPreferenceService getPreferenceService() { public CalculatorPreferenceService getPreferenceService() {

View File

@ -0,0 +1,56 @@
package org.solovyev.android.calculator.plot;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* User: serso
* Date: 1/12/13
* Time: 8:23 PM
*/
public interface CalculatorPlotter {
@NotNull
PlotData getPlotData();
boolean addFunction(@NotNull Generic expression);
boolean addFunction(@NotNull PlotFunction plotFunction);
boolean addFunction(@NotNull XyFunction xyFunction);
boolean addFunction(@NotNull XyFunction xyFunction, @NotNull PlotFunctionLineDef functionLineDef);
boolean updateFunction(@NotNull PlotFunction newFunction);
boolean updateFunction(@NotNull XyFunction xyFunction, @NotNull PlotFunctionLineDef functionLineDef);
boolean removeFunction(@NotNull PlotFunction plotFunction);
boolean removeFunction(@NotNull XyFunction xyFunction);
void pin(@NotNull PlotFunction plotFunction);
void unpin(@NotNull PlotFunction plotFunction);
void show(@NotNull PlotFunction plotFunction);
void hide(@NotNull PlotFunction plotFunction);
void clearAllFunctions();
@NotNull
List<PlotFunction> getFunctions();
@NotNull
List<PlotFunction> getVisibleFunctions();
void plot();
boolean isPlotPossible(@NotNull Generic expression);
void setPlot3d(boolean plot3d);
void removeAllUnpinned();
void setPlotImag(boolean plotImag);
void setRealLineColor(@NotNull GraphLineColor realLineColor);
void setImagLineColor(@NotNull GraphLineColor imagLineColor);
}

View File

@ -0,0 +1,294 @@
package org.solovyev.android.calculator.plot;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import jscl.math.Generic;
import jscl.math.function.Constant;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.Calculator;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.CalculatorUtils;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 1/12/13
* Time: 8:42 PM
*/
public class CalculatorPlotterImpl implements CalculatorPlotter {
@NotNull
private final List<PlotFunction> functions = new ArrayList<PlotFunction>();
@NotNull
private final Calculator calculator;
private boolean plot3d = false;
private boolean plotImag = false;
@NotNull
private GraphLineColor realLineColor;
@NotNull
private GraphLineColor imagLineColor;
public CalculatorPlotterImpl(@NotNull Calculator calculator) {
this.calculator = calculator;
}
@NotNull
@Override
public PlotData getPlotData() {
return new PlotData(getVisibleFunctions(), plot3d);
}
@Override
public boolean addFunction(@NotNull Generic expression) {
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(expression));
assert variables.size() <= 2;
final Constant xVariable;
if (variables.size() > 0) {
xVariable = variables.get(0);
} else {
xVariable = null;
}
final Constant yVariable;
if (variables.size() > 1) {
yVariable = variables.get(1);
} else {
yVariable = null;
}
boolean realAdded = addFunction(new XyFunction(expression, xVariable, yVariable, false));
final PlotFunction imagPlotFunction = new PlotFunction(new XyFunction(expression, xVariable, yVariable, true));
final boolean imagAdded = addFunction(plotImag ? imagPlotFunction : PlotFunction.invisible(imagPlotFunction));
return imagAdded || realAdded;
}
@Override
public boolean addFunction(@NotNull PlotFunction plotFunction) {
synchronized (functions) {
if (!functions.contains(plotFunction)) {
functions.add(plotFunction);
onFunctionsChanged();
return true;
} else {
return false;
}
}
}
@Override
public void removeAllUnpinned() {
synchronized (functions) {
boolean changed = Iterables.removeIf(functions, new Predicate<PlotFunction>() {
@Override
public boolean apply(@Nullable PlotFunction function) {
return function != null && !function.isPinned();
}
});
if (changed) {
onFunctionsChanged();
}
}
}
@Override
public boolean removeFunction(@NotNull PlotFunction plotFunction) {
synchronized (functions) {
boolean changed = functions.remove(plotFunction);
if (changed) {
onFunctionsChanged();
}
return changed;
}
}
@Override
public boolean addFunction(@NotNull XyFunction xyFunction) {
return addFunction(new PlotFunction(xyFunction));
}
@Override
public boolean addFunction(@NotNull XyFunction xyFunction, @NotNull PlotFunctionLineDef functionLineDef) {
return addFunction(new PlotFunction(xyFunction, functionLineDef));
}
@Override
public boolean updateFunction(@NotNull XyFunction xyFunction, @NotNull PlotFunctionLineDef functionLineDef) {
final PlotFunction newFunction = new PlotFunction(xyFunction, functionLineDef);
return updateFunction(newFunction);
}
@Override
public boolean updateFunction(@NotNull PlotFunction newFunction) {
boolean changed = false;
synchronized (functions) {
for (int i = 0; i < functions.size(); i++) {
final PlotFunction plotFunction = functions.get(i);
if (plotFunction.equals(newFunction)) {
// update old function
functions.set(i, newFunction);
changed = true;
break;
}
}
}
return changed;
}
@Override
public boolean removeFunction(@NotNull XyFunction xyFunction) {
return removeFunction(new PlotFunction(xyFunction));
}
@Override
public void pin(@NotNull PlotFunction plotFunction) {
updateFunction(PlotFunction.pin(plotFunction));
}
@Override
public void unpin(@NotNull PlotFunction plotFunction) {
updateFunction(PlotFunction.unpin(plotFunction));
}
@Override
public void show(@NotNull PlotFunction plotFunction) {
updateFunction(PlotFunction.visible(plotFunction));
firePlotDataChangedEvent();
}
@Override
public void hide(@NotNull PlotFunction plotFunction) {
updateFunction(PlotFunction.invisible(plotFunction));
firePlotDataChangedEvent();
}
@Override
public void clearAllFunctions() {
synchronized (functions) {
functions.clear();
onFunctionsChanged();
}
}
// NOTE: this method must be called from synchronized block
private void onFunctionsChanged() {
assert Thread.holdsLock(functions);
int maxArity = 0;
for (PlotFunction function : functions) {
final XyFunction xyFunction = function.getXyFunction();
maxArity = Math.max(maxArity, xyFunction.getArity());
}
if (maxArity > 1) {
plot3d = true;
} else {
plot3d = false;
}
firePlotDataChangedEvent();
}
@NotNull
@Override
public List<PlotFunction> getFunctions() {
synchronized (functions) {
return new ArrayList<PlotFunction>(functions);
}
}
@NotNull
@Override
public List<PlotFunction> getVisibleFunctions() {
synchronized (functions) {
return Lists.newArrayList(Iterables.filter(functions, new Predicate<PlotFunction>() {
@Override
public boolean apply(@Nullable PlotFunction function) {
return function != null && function.isVisible();
}
}));
}
}
@Override
public void plot() {
calculator.fireCalculatorEvent(CalculatorEventType.plot_graph, null);
}
@Override
public boolean isPlotPossible(@NotNull Generic expression) {
boolean result = false;
int size = CalculatorUtils.getNotSystemConstants(expression).size();
if (size == 0 || size == 1 || size == 2) {
result = true;
}
return result;
}
@Override
public void setPlot3d(boolean plot3d) {
if (this.plot3d != plot3d) {
this.plot3d = plot3d;
firePlotDataChangedEvent();
}
}
private void firePlotDataChangedEvent() {
calculator.fireCalculatorEvent(CalculatorEventType.plot_data_changed, getPlotData());
}
@Override
public void setPlotImag(boolean plotImag) {
if (this.plotImag != plotImag) {
this.plotImag = plotImag;
if (toggleImagFunctions(this.plotImag)) {
firePlotDataChangedEvent();
}
}
}
@Override
public void setRealLineColor(@NotNull GraphLineColor realLineColor) {
this.realLineColor = realLineColor;
}
@Override
public void setImagLineColor(@NotNull GraphLineColor imagLineColor) {
this.imagLineColor = imagLineColor;
}
private boolean toggleImagFunctions(boolean show) {
boolean changed = false;
synchronized (functions) {
for (int i = 0; i < functions.size(); i++) {
final PlotFunction plotFunction = functions.get(i);
if (plotFunction.getXyFunction().isImag()) {
functions.set(i, show ? PlotFunction.visible(plotFunction) : PlotFunction.invisible(plotFunction));
changed = true;
}
}
}
return changed;
}
}

View File

@ -1,7 +1,5 @@
package org.solovyev.android.calculator.plot; package org.solovyev.android.calculator.plot;
import android.graphics.Color;
/** /**
* User: serso * User: serso
* Date: 10/4/12 * Date: 10/4/12
@ -9,11 +7,19 @@ import android.graphics.Color;
*/ */
public enum GraphLineColor { public enum GraphLineColor {
white(Color.WHITE), // Color.WHITE
grey(Color.GRAY), white(0xFFFFFFFF),
red(Color.RED),
blue(Color.rgb(16, 100, 140)), // Color.GRAY
green(Color.GREEN); grey(0xFF888888),
// Color.RED
red(0xFFFF0000),
blue(0xFF10648C),
// Color.GREEN
green(0xFF00FF00);
private final int color; private final int color;

View File

@ -0,0 +1,28 @@
package org.solovyev.android.calculator.plot;
import java.util.List;
/**
* User: serso
* Date: 1/12/13
* Time: 10:01 PM
*/
public class PlotData {
private List<PlotFunction> functions;
private boolean plot3d;
public PlotData(List<PlotFunction> functions, boolean plot3d) {
this.functions = functions;
this.plot3d = plot3d;
}
public List<PlotFunction> getFunctions() {
return functions;
}
public boolean isPlot3d() {
return plot3d;
}
}

View File

@ -0,0 +1,102 @@
package org.solovyev.android.calculator.plot;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 1/12/13
* Time: 8:45 PM
*/
public class PlotFunction {
@NotNull
private XyFunction xyFunction;
@NotNull
private PlotFunctionLineDef plotFunctionLineDef;
private boolean pinned = false;
private boolean visible = true;
public PlotFunction(@NotNull XyFunction xyFunction) {
this.xyFunction = xyFunction;
this.plotFunctionLineDef = PlotFunctionLineDef.newDefaultInstance();
}
public PlotFunction(@NotNull XyFunction xyFunction,
@NotNull PlotFunctionLineDef plotFunctionLineDef) {
this.xyFunction = xyFunction;
this.plotFunctionLineDef = plotFunctionLineDef;
}
@NotNull
private PlotFunction copy() {
final PlotFunction copy = new PlotFunction(this.xyFunction);
copy.plotFunctionLineDef = this.plotFunctionLineDef;
copy.pinned = this.pinned;
copy.visible = this.visible;
return copy;
}
public static PlotFunction pin(@NotNull PlotFunction that) {
final PlotFunction copy = that.copy();
copy.pinned = true;
return copy;
}
public static PlotFunction unpin(@NotNull PlotFunction that) {
final PlotFunction copy = that.copy();
copy.pinned = false;
return copy;
}
public static PlotFunction visible(@NotNull PlotFunction that) {
final PlotFunction copy = that.copy();
copy.visible = true;
return copy;
}
public static PlotFunction invisible(@NotNull PlotFunction that) {
final PlotFunction copy = that.copy();
copy.visible = false;
return copy;
}
@NotNull
public XyFunction getXyFunction() {
return xyFunction;
}
@NotNull
public PlotFunctionLineDef getPlotFunctionLineDef() {
return plotFunctionLineDef;
}
public boolean isPinned() {
return pinned;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PlotFunction)) return false;
PlotFunction that = (PlotFunction) o;
if (!xyFunction.equals(that.xyFunction)) return false;
return true;
}
@Override
public int hashCode() {
return xyFunction.hashCode();
}
public boolean isVisible() {
return visible;
}
}

View File

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

View File

@ -0,0 +1,96 @@
package org.solovyev.android.calculator.plot;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 1/5/13
* Time: 7:41 PM
*/
public class PlotFunctionLineDef {
/*
**********************************************************************
*
* CONSTANTS
*
**********************************************************************
*/
@NotNull
public static final Float DEFAULT_LINE_WIDTH = -1f;
private static final int WHITE = 0xFFFFFFFF;
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@NotNull
private PlotFunctionLineColorType lineColorType = PlotFunctionLineColorType.solid;
private int lineColor = WHITE;
@NotNull
private PlotLineStyle lineStyle = PlotLineStyle.solid;
private float lineWidth = -DEFAULT_LINE_WIDTH;
private PlotFunctionLineDef() {
}
@NotNull
public static PlotFunctionLineDef newInstance(int lineColor, @NotNull PlotLineStyle lineStyle) {
final PlotFunctionLineDef result = new PlotFunctionLineDef();
result.lineColor = lineColor;
result.lineStyle = lineStyle;
return result;
}
@NotNull
public static PlotFunctionLineDef newInstance(int lineColor, @NotNull PlotLineStyle lineStyle, float lineWidth) {
final PlotFunctionLineDef result = new PlotFunctionLineDef();
result.lineColor = lineColor;
result.lineStyle = lineStyle;
result.lineWidth = lineWidth;
return result;
}
@NotNull
public static PlotFunctionLineDef newInstance(int lineColor, @NotNull PlotLineStyle lineStyle, float lineWidth, @NotNull PlotFunctionLineColorType lineColorType) {
final PlotFunctionLineDef result = new PlotFunctionLineDef();
result.lineColor = lineColor;
result.lineColorType = lineColorType;
result.lineStyle = lineStyle;
result.lineWidth = lineWidth;
return result;
}
@NotNull
public static PlotFunctionLineDef newDefaultInstance() {
return new PlotFunctionLineDef();
}
public int getLineColor() {
return lineColor;
}
@NotNull
public PlotLineStyle getLineStyle() {
return lineStyle;
}
public float getLineWidth() {
return lineWidth;
}
@NotNull
public PlotFunctionLineColorType getLineColorType() {
return lineColorType;
}
}

View File

@ -0,0 +1,16 @@
package org.solovyev.android.calculator.plot;
/**
* User: serso
* Date: 1/5/13
* Time: 7:37 PM
*/
public enum PlotLineStyle {
solid,
dashed,
dotted,
dash_dotted;
}

View File

@ -0,0 +1,128 @@
package org.solovyev.android.calculator.plot;
import jscl.math.Generic;
import jscl.math.function.Constant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class XyFunction {
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@NotNull
private final Generic expression;
@NotNull
private String expressionString;
@Nullable
private final Constant xVariable;
@Nullable
private String xVariableName;
@Nullable
private final Constant yVariable;
private final boolean imag;
@Nullable
private String yVariableName;
private int arity;
public XyFunction(@NotNull Generic expression,
@Nullable Constant xVariable,
@Nullable Constant yVariable,
boolean imag) {
this.expression = expression;
this.xVariable = xVariable;
this.yVariable = yVariable;
this.imag = imag;
if (imag) {
this.expressionString = "Im(" + expression.toString() + ")";
} else {
this.expressionString = expression.toString();
}
this.xVariableName = xVariable == null ? null : xVariable.getName();
this.yVariableName = yVariable == null ? null : yVariable.getName();
this.arity = 2;
if ( this.yVariableName == null ) {
this.arity--;
}
if ( this.xVariableName == null ) {
this.arity--;
}
}
public boolean isImag() {
return imag;
}
public int getArity() {
return arity;
}
@NotNull
public Generic getExpression() {
return expression;
}
@Nullable
public Constant getXVariable() {
return xVariable;
}
@Nullable
public Constant getYVariable() {
return yVariable;
}
@NotNull
public String getExpressionString() {
return expressionString;
}
@Nullable
public String getXVariableName() {
return xVariableName;
}
@Nullable
public String getYVariableName() {
return yVariableName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof XyFunction)) return false;
final XyFunction that = (XyFunction) o;
if (!expressionString.equals(that.expressionString)) return false;
if (xVariableName != null ? !xVariableName.equals(that.xVariableName) : that.xVariableName != null)
return false;
if (yVariableName != null ? !yVariableName.equals(that.yVariableName) : that.yVariableName != null)
return false;
return true;
}
@Override
public int hashCode() {
int result = expressionString.hashCode();
result = 31 * result + (xVariableName != null ? xVariableName.hashCode() : 0);
result = 31 * result + (yVariableName != null ? yVariableName.hashCode() : 0);
return result;
}
}

View File

@ -3,6 +3,7 @@ package org.solovyev.android.calculator;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer; import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
import org.solovyev.android.calculator.history.CalculatorHistory; import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
/** /**
* User: serso * User: serso
@ -12,7 +13,7 @@ import org.solovyev.android.calculator.history.CalculatorHistory;
public class AbstractCalculatorTest { public class AbstractCalculatorTest {
protected void setUp() throws Exception { protected void setUp() throws Exception {
Locator.getInstance().init(new CalculatorImpl(), CalculatorTestUtils.newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), Mockito.mock(CalculatorHistory.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(CalculatorKeyboard.class), Mockito.mock(CalculatorExternalListenersContainer.class)); Locator.getInstance().init(new CalculatorImpl(), CalculatorTestUtils.newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), Mockito.mock(CalculatorHistory.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(CalculatorKeyboard.class), Mockito.mock(CalculatorExternalListenersContainer.class), Mockito.mock(CalculatorPlotter.class));
Locator.getInstance().getEngine().init(); Locator.getInstance().getEngine().init();
} }

View File

@ -8,6 +8,7 @@ import org.mockito.Mockito;
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer; import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
import org.solovyev.android.calculator.history.CalculatorHistory; import org.solovyev.android.calculator.history.CalculatorHistory;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
import java.io.*; import java.io.*;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -24,7 +25,7 @@ public class CalculatorTestUtils {
public static final int TIMEOUT = 3; public static final int TIMEOUT = 3;
public static void staticSetUp() throws Exception { public static void staticSetUp() throws Exception {
Locator.getInstance().init(new CalculatorImpl(), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), Mockito.mock(CalculatorHistory.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(CalculatorKeyboard.class), Mockito.mock(CalculatorExternalListenersContainer.class)); Locator.getInstance().init(new CalculatorImpl(), newCalculatorEngine(), Mockito.mock(CalculatorClipboard.class), Mockito.mock(CalculatorNotifier.class), Mockito.mock(CalculatorHistory.class), new SystemOutCalculatorLogger(), Mockito.mock(CalculatorPreferenceService.class), Mockito.mock(CalculatorKeyboard.class), Mockito.mock(CalculatorExternalListenersContainer.class), Mockito.mock(CalculatorPlotter.class));
Locator.getInstance().getEngine().init(); Locator.getInstance().getEngine().init();
} }