new plotter
This commit is contained in:
parent
cbfdf237f0
commit
3f09528f45
@ -2,17 +2,12 @@ package org.solovyev.android.calculator;
|
||||
|
||||
import android.content.Context;
|
||||
import jscl.math.Generic;
|
||||
import jscl.math.function.Constant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.solovyev.android.calculator.core.R;
|
||||
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.menu.LabeledMenuItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
* Date: 21.09.12
|
||||
@ -81,31 +76,17 @@ public enum CalculatorDisplayMenuItem implements LabeledMenuItem<CalculatorDispl
|
||||
plot(R.string.c_plot) {
|
||||
@Override
|
||||
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
|
||||
final Generic generic = data.getResult();
|
||||
assert generic != null;
|
||||
final Generic expression = data.getResult();
|
||||
assert expression != null;
|
||||
|
||||
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(generic));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.plot_graph, PlotInput.newInstance(generic, xVariable, yVariable), context);
|
||||
Locator.getInstance().getPlotter().removeAllUnpinned();
|
||||
Locator.getInstance().getPlotter().addFunction(expression);
|
||||
Locator.getInstance().getPlotter().plot();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
|
||||
return CalculatorUtils.isPlotPossible(generic, operation);
|
||||
return Locator.getInstance().getPlotter().isPlotPossible(generic);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -8,7 +8,6 @@ import org.solovyev.android.AndroidUtils;
|
||||
import org.solovyev.android.calculator.math.MathType;
|
||||
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
|
||||
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.IntegerPreference;
|
||||
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.StringPreference;
|
||||
import org.solovyev.android.view.VibratorContainer;
|
||||
import org.solovyev.common.ListMapper;
|
||||
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -13,12 +13,12 @@ import org.solovyev.android.view.UpdatableViewBuilder;
|
||||
public class ParcelablePlotInputListItem implements ListItem {
|
||||
|
||||
@NotNull
|
||||
private ParcelablePlotInput plotInput;
|
||||
private XyFunction plotInput;
|
||||
|
||||
@NotNull
|
||||
private UpdatableViewBuilder<TextView> viewBuilder;
|
||||
|
||||
public ParcelablePlotInputListItem(@NotNull ParcelablePlotInput plotInput) {
|
||||
public ParcelablePlotInputListItem(@NotNull XyFunction plotInput) {
|
||||
this.plotInput = plotInput;
|
||||
// todo serso: use correct tag
|
||||
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) {
|
||||
textView.setText(plotInput.getExpression());
|
||||
textView.setText(plotInput.getExpressionString());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -11,8 +11,6 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import com.actionbarsherlock.app.SherlockFragmentActivity;
|
||||
import jscl.math.Generic;
|
||||
import jscl.math.function.Constant;
|
||||
import org.achartengine.ChartFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.help.CalculatorHelpActivity;
|
||||
import org.solovyev.android.calculator.history.CalculatorHistoryActivity;
|
||||
import org.solovyev.android.calculator.math.edit.CalculatorFunctionsActivity;
|
||||
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.math.edit.*;
|
||||
import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity;
|
||||
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.MessageType;
|
||||
import org.solovyev.common.text.StringUtils;
|
||||
@ -104,14 +95,9 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void plotGraph(@NotNull final Context context,
|
||||
@NotNull Generic generic,
|
||||
@Nullable Constant xVariable,
|
||||
@Nullable Constant yVariable){
|
||||
public static void plotGraph(@NotNull final Context context){
|
||||
final Intent intent = new Intent();
|
||||
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);
|
||||
AndroidUtils2.addFlags(intent, false, context);
|
||||
context.startActivity(intent);
|
||||
@ -218,12 +204,10 @@ public final class CalculatorActivityLauncher implements CalculatorEventListener
|
||||
});
|
||||
break;
|
||||
case plot_graph:
|
||||
final PlotInput plotInput = (PlotInput) data;
|
||||
assert plotInput != null;
|
||||
App.getInstance().getUiThreadExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
plotGraph(context, plotInput.getFunction(), plotInput.getXVariable(), plotInput.getYVariable());
|
||||
plotGraph(context);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
@ -19,6 +19,8 @@ import org.solovyev.android.calculator.external.AndroidExternalListenersContaine
|
||||
import org.solovyev.android.calculator.history.AndroidCalculatorHistory;
|
||||
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
|
||||
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 java.util.ArrayList;
|
||||
@ -118,7 +120,8 @@ public class CalculatorApplication extends android.app.Application implements Sh
|
||||
new AndroidCalculatorLogger(),
|
||||
new AndroidCalculatorPreferenceService(this),
|
||||
new AndroidCalculatorKeyboard(this, new CalculatorKeyboardImpl(calculator)),
|
||||
new AndroidExternalListenersContainer(calculator));
|
||||
new AndroidExternalListenersContainer(calculator),
|
||||
new AndroidCalculatorPlotter(this, new CalculatorPlotterImpl(calculator)));
|
||||
|
||||
Locator.getInstance().getCalculator().init();
|
||||
|
||||
|
@ -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.matrix.CalculatorMatrixEditFragment;
|
||||
import org.solovyev.android.calculator.plot.CalculatorArityPlotFragment;
|
||||
import org.solovyev.android.calculator.plot.CalculatorPlotFragment;
|
||||
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),
|
||||
functions(CalculatorFunctionsFragment.class, R.layout.math_entities_fragment, R.string.c_functions),
|
||||
operators(CalculatorOperatorsFragment.class, R.layout.math_entities_fragment, R.string.c_operators),
|
||||
plotter(CalculatorPlotFragment.class, R.layout.plot_fragment, R.string.c_graph),
|
||||
plotter_2(CalculatorArityPlotFragment.class, R.layout.plot_fragment, R.string.c_graph),
|
||||
plotter(CalculatorArityPlotFragment.class, R.layout.plot_fragment, R.string.c_graph),
|
||||
|
||||
// todo serso: strings
|
||||
plotter_functions(CalculatorPlotFunctionsFragment.class, R.layout.plot_functions_fragment, R.string.c_graph),
|
||||
|
@ -2,35 +2,18 @@ package org.solovyev.android.calculator.plot;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import com.actionbarsherlock.view.Menu;
|
||||
import com.actionbarsherlock.view.MenuInflater;
|
||||
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.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.android.calculator.CalculatorApplication;
|
||||
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.calculator.*;
|
||||
import org.solovyev.android.menu.ActivityMenu;
|
||||
import org.solovyev.android.menu.IdentifiableMenuItem;
|
||||
import org.solovyev.android.menu.ListActivityMenu;
|
||||
@ -38,6 +21,7 @@ import org.solovyev.android.sherlock.menu.SherlockMenuHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -59,8 +43,6 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
|
||||
|
||||
protected static final String TAG = "CalculatorPlotFragment";
|
||||
|
||||
public static final String INPUT = "plot_input";
|
||||
|
||||
protected static final String PLOT_BOUNDARIES = "plot_boundaries";
|
||||
|
||||
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;
|
||||
|
||||
// thread for applying UI changes
|
||||
@ -85,7 +64,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
|
||||
private final Handler uiHandler = new Handler();
|
||||
|
||||
@NotNull
|
||||
private PreparedInput preparedInput;
|
||||
private PlotData plotData = new PlotData(Collections.<PlotFunction>emptyList(), false);
|
||||
|
||||
@NotNull
|
||||
private ActivityMenu<Menu, MenuItem> fragmentMenu;
|
||||
@ -107,13 +86,9 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final Bundle arguments = getArguments();
|
||||
|
||||
if (arguments != null) {
|
||||
input = (ParcelablePlotInput) arguments.getParcelable(INPUT);
|
||||
}
|
||||
|
||||
if (input == null) {
|
||||
// todo serso: init variable properly
|
||||
boolean paneFragment = true;
|
||||
if (paneFragment) {
|
||||
this.bgColor = getResources().getColor(R.color.cpp_pane_background);
|
||||
} else {
|
||||
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) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
if (input == null) {
|
||||
this.preparedInput = prepareInputFromDisplay(Locator.getInstance().getDisplay().getViewState(), savedInstanceState);
|
||||
} else {
|
||||
this.preparedInput = prepareInput(input, true, savedInstanceState);
|
||||
}
|
||||
this.plotData = Locator.getInstance().getPlotter().getPlotData();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -150,42 +121,31 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
createChart(preparedInput);
|
||||
createGraphicalView(getView(), preparedInput);
|
||||
createChart(plotData);
|
||||
createGraphicalView(getView(), plotData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable final Object data) {
|
||||
if (calculatorEventType.isOfType(CalculatorEventType.display_state_changed)) {
|
||||
PreparedInput preparedInput = getPreparedInput();
|
||||
if (!preparedInput.isFromInputArgs()) {
|
||||
|
||||
final CalculatorEventHolder.Result result = this.lastEventHolder.apply(calculatorEventData);
|
||||
if (result.isNewAfter()) {
|
||||
preparedInput = prepareInputFromDisplay(((CalculatorDisplayChangeEventData) data).getNewValue(), null);
|
||||
onNewPreparedInput(preparedInput);
|
||||
}
|
||||
if (calculatorEventType.isOfType(CalculatorEventType.plot_data_changed)) {
|
||||
final CalculatorEventHolder.Result result = this.lastEventHolder.apply(calculatorEventData);
|
||||
if (result.isNewAfter()) {
|
||||
onNewPlotData((PlotData) data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onNewPreparedInput(@NotNull PreparedInput preparedInput) {
|
||||
this.preparedInput = preparedInput;
|
||||
private void onNewPlotData(@NotNull final PlotData plotData) {
|
||||
this.plotData = plotData;
|
||||
|
||||
final PreparedInput finalPreparedInput = preparedInput;
|
||||
getUiHandler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
createChart(plotData);
|
||||
|
||||
if (!finalPreparedInput.isError()) {
|
||||
createChart(finalPreparedInput);
|
||||
|
||||
final View view = getView();
|
||||
if (view != null) {
|
||||
createGraphicalView(view, finalPreparedInput);
|
||||
}
|
||||
} else {
|
||||
onError();
|
||||
final View view = getView();
|
||||
if (view != null) {
|
||||
createGraphicalView(view, plotData);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -193,9 +153,9 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
|
||||
|
||||
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) {
|
||||
@ -219,11 +179,6 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
|
||||
return uiHandler;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PreparedInput getPreparedInput() {
|
||||
return preparedInput;
|
||||
}
|
||||
|
||||
public int getBgColor() {
|
||||
return bgColor;
|
||||
}
|
||||
@ -257,7 +212,7 @@ public abstract class AbstractCalculatorPlotFragment extends CalculatorFragment
|
||||
|
||||
@Override
|
||||
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> {
|
||||
|
||||
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
|
||||
private ParcelablePlotInput input;
|
||||
|
||||
@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() {
|
||||
if ( plotFunctionLineDef.getLineWidth() == PlotFunctionLineDef.DEFAULT_LINE_WIDTH ) {
|
||||
paint.setStrokeWidth(0);
|
||||
} else {
|
||||
paint.setStrokeWidth(plotFunctionLineDef.getLineWidth());
|
||||
}
|
||||
|
||||
@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 {
|
||||
return that;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFromInputArgs() {
|
||||
return fromInputArgs;
|
||||
}
|
||||
|
||||
@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;
|
||||
final AndroidPlotLineStyle androidPlotLineStyle = AndroidPlotLineStyle.valueOf(plotFunctionLineDef.getLineStyle());
|
||||
if (androidPlotLineStyle != null) {
|
||||
androidPlotLineStyle.applyToPaint(paint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,25 +8,25 @@ import org.jetbrains.annotations.NotNull;
|
||||
* Date: 1/5/13
|
||||
* Time: 7:35 PM
|
||||
*/
|
||||
public class FunctionPlotDef {
|
||||
public class ArityPlotFunction {
|
||||
|
||||
@NotNull
|
||||
private Function function;
|
||||
|
||||
@NotNull
|
||||
private FunctionLineDef lineDef;
|
||||
private PlotFunctionLineDef lineDef;
|
||||
|
||||
private FunctionPlotDef() {
|
||||
private ArityPlotFunction() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FunctionPlotDef newInstance(@NotNull Function function) {
|
||||
return newInstance(function, FunctionLineDef.newDefaultInstance());
|
||||
public static ArityPlotFunction newInstance(@NotNull Function function) {
|
||||
return newInstance(function, PlotFunctionLineDef.newDefaultInstance());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FunctionPlotDef newInstance(@NotNull Function function, @NotNull FunctionLineDef lineDef) {
|
||||
final FunctionPlotDef result = new FunctionPlotDef();
|
||||
public static ArityPlotFunction newInstance(@NotNull Function function, @NotNull PlotFunctionLineDef lineDef) {
|
||||
final ArityPlotFunction result = new ArityPlotFunction();
|
||||
|
||||
result.function = function;
|
||||
result.lineDef = lineDef;
|
||||
@ -40,7 +40,7 @@ public class FunctionPlotDef {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public FunctionLineDef getLineDef() {
|
||||
public PlotFunctionLineDef getLineDef() {
|
||||
return lineDef;
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package org.solovyev.android.calculator.plot;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import jscl.math.Generic;
|
||||
@ -10,7 +8,6 @@ import jscl.math.function.Constant;
|
||||
import org.javia.arity.Function;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.solovyev.android.calculator.CalculatorPreferences;
|
||||
import org.solovyev.android.calculator.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -38,13 +35,7 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createGraphicalView(@NotNull View root, @NotNull PreparedInput preparedInput) {
|
||||
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
|
||||
|
||||
final GraphLineColor realLineColor = CalculatorPreferences.Graph.lineColorReal.getPreference(preferences);
|
||||
final GraphLineColor imagLineColor = CalculatorPreferences.Graph.lineColorImag.getPreference(preferences);
|
||||
final boolean plotImag = CalculatorPreferences.Graph.plotImag.getPreference(preferences);
|
||||
protected void createGraphicalView(@NotNull View root, @NotNull PlotData plotData) {
|
||||
|
||||
// remove old
|
||||
final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
|
||||
@ -53,47 +44,42 @@ public class CalculatorArityPlotFragment extends AbstractCalculatorPlotFragment
|
||||
graphContainer.removeView((View) graphView);
|
||||
}
|
||||
|
||||
if (!preparedInput.isError()) {
|
||||
final Generic expression = preparedInput.getExpression();
|
||||
final Constant xVariable = preparedInput.getXVariable();
|
||||
final Constant yVariable = preparedInput.getYVariable();
|
||||
final List<ArityPlotFunction> arityFunctions = new ArrayList<ArityPlotFunction>();
|
||||
|
||||
for (PlotFunction plotFunction : plotData.getFunctions()) {
|
||||
|
||||
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 List<FunctionPlotDef> functions = new ArrayList<FunctionPlotDef>();
|
||||
|
||||
functions.add(FunctionPlotDef.newInstance(new RealArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(realLineColor.getColor(), FunctionLineStyle.solid, 3f, FunctionLineColorType.color_map)));
|
||||
if (plotImag) {
|
||||
functions.add(FunctionPlotDef.newInstance(new ImaginaryArityFunction(arity, expression, xVariable, yVariable), FunctionLineDef.newInstance(imagLineColor.getColor(), FunctionLineStyle.solid, 3f, FunctionLineColorType.color_map)));
|
||||
final Function arityFunction;
|
||||
if (xyFunction.isImag()) {
|
||||
arityFunction = new ImaginaryArityFunction(arity, expression, xVariable, yVariable);
|
||||
} else {
|
||||
arityFunction = new RealArityFunction(arity, expression, xVariable, yVariable);
|
||||
}
|
||||
|
||||
switch (arity) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (preparedInput.isForce3d()) {
|
||||
graphView = new Graph3dView(getActivity());
|
||||
} else {
|
||||
graphView = new Graph2dView(getActivity());
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
graphView = new Graph3dView(getActivity());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported arity: " + arity);
|
||||
}
|
||||
|
||||
graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor()));
|
||||
graphView.setFunctionPlotDefs(functions);
|
||||
|
||||
graphContainer.addView((View) graphView);
|
||||
} else {
|
||||
onError();
|
||||
arityFunctions.add(ArityPlotFunction.newInstance(arityFunction, plotFunction.getPlotFunctionLineDef()));
|
||||
}
|
||||
|
||||
if ( plotData.isPlot3d() ) {
|
||||
graphView = new Graph3dView(getActivity());
|
||||
} else {
|
||||
graphView = new Graph2dView(getActivity());
|
||||
}
|
||||
|
||||
graphView.init(FunctionViewDef.newInstance(Color.WHITE, Color.WHITE, Color.DKGRAY, getBgColor()));
|
||||
graphView.setFunctionPlotDefs(arityFunctions);
|
||||
|
||||
graphContainer.addView((View) graphView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createChart(@NotNull PreparedInput preparedInput) {
|
||||
protected void createChart(@NotNull PlotData plotData) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,6 +35,6 @@ public class CalculatorPlotActivity extends CalculatorFragmentActivity {
|
||||
|
||||
@NotNull
|
||||
public static CalculatorFragmentType getPlotterFragmentType() {
|
||||
return CalculatorFragmentType.plotter_2;
|
||||
return CalculatorFragmentType.plotter;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ final class CalculatorPlotFunctionsController {
|
||||
private static final CalculatorPlotFunctionsController instance = new CalculatorPlotFunctionsController();
|
||||
|
||||
@NotNull
|
||||
private final List<ParcelablePlotInput> functions = new ArrayList<ParcelablePlotInput>();
|
||||
private final List<XyFunction> functions = new ArrayList<XyFunction>();
|
||||
|
||||
private CalculatorPlotFunctionsController() {
|
||||
}
|
||||
@ -23,11 +23,11 @@ final class CalculatorPlotFunctionsController {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<ParcelablePlotInput> getFunctions() {
|
||||
public List<XyFunction> getFunctions() {
|
||||
return Collections.unmodifiableList(functions);
|
||||
}
|
||||
|
||||
public boolean addFunction(@NotNull ParcelablePlotInput function) {
|
||||
public boolean addFunction(@NotNull XyFunction function) {
|
||||
if (!functions.contains(function)) {
|
||||
return functions.add(function);
|
||||
} else {
|
||||
|
@ -23,9 +23,9 @@ public class CalculatorPlotFunctionsFragment extends CalculatorListFragment {
|
||||
public void 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
|
||||
public ParcelablePlotInputListItem apply(@Nullable ParcelablePlotInput input) {
|
||||
public ParcelablePlotInputListItem apply(@Nullable XyFunction input) {
|
||||
return new ParcelablePlotInputListItem(input);
|
||||
}
|
||||
});
|
||||
|
@ -86,12 +86,12 @@ public class Graph2dView extends View implements GraphView {
|
||||
|
||||
@Override
|
||||
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();
|
||||
if (arity != 0 && arity != 1) {
|
||||
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 char[] buf = new char[20];
|
||||
|
||||
private static StringBuilder format(float fv) {
|
||||
private static StringBuilder format(final float value) {
|
||||
int pos = 0;
|
||||
boolean addDot = false;
|
||||
int v = Math.round(fv * 100);
|
||||
boolean isNeg = v < 0;
|
||||
v = isNeg ? -v : v;
|
||||
|
||||
final boolean negative = value < 0;
|
||||
|
||||
int absValue = Math.round(Math.abs(value) * 100);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int digit = v % 10;
|
||||
v /= 10;
|
||||
int digit = absValue % 10;
|
||||
absValue /= 10;
|
||||
if (digit != 0 || addDot) {
|
||||
buf[pos++] = (char) ('0' + digit);
|
||||
addDot = true;
|
||||
@ -365,14 +366,14 @@ public class Graph2dView extends View implements GraphView {
|
||||
if (addDot) {
|
||||
buf[pos++] = '.';
|
||||
}
|
||||
if (v == 0) {
|
||||
if (absValue == 0) {
|
||||
buf[pos++] = '0';
|
||||
}
|
||||
while (v != 0) {
|
||||
buf[pos++] = (char) ('0' + (v % 10));
|
||||
v /= 10;
|
||||
while (absValue != 0) {
|
||||
buf[pos++] = (char) ('0' + (absValue % 10));
|
||||
absValue /= 10;
|
||||
}
|
||||
if (isNeg) {
|
||||
if (negative) {
|
||||
buf[pos++] = '-';
|
||||
}
|
||||
b.setLength(0);
|
||||
@ -459,13 +460,16 @@ public class Graph2dView extends View implements GraphView {
|
||||
paint.setPathEffect(null);
|
||||
}
|
||||
|
||||
// AXIS
|
||||
{
|
||||
// AXIS
|
||||
|
||||
paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor());
|
||||
if (drawYAxis) {
|
||||
canvas.drawLine(x0, 0, x0, height, paint);
|
||||
paint.setColor(graphViewHelper.getFunctionViewDef().getAxisColor());
|
||||
if (drawYAxis) {
|
||||
canvas.drawLine(x0, 0, x0, height, paint);
|
||||
}
|
||||
canvas.drawLine(0, y0, width, y0, paint);
|
||||
}
|
||||
canvas.drawLine(0, y0, width, y0, paint);
|
||||
|
||||
|
||||
matrix.reset();
|
||||
matrix.preTranslate(-currentX, -currentY);
|
||||
@ -474,23 +478,29 @@ public class Graph2dView extends View implements GraphView {
|
||||
|
||||
paint.setAntiAlias(false);
|
||||
|
||||
final List<FunctionPlotDef> functionPlotDefs = graphViewHelper.getFunctionPlotDefs();
|
||||
{
|
||||
//GRAPH
|
||||
|
||||
// create path once
|
||||
final Path path = new Path();
|
||||
final List<ArityPlotFunction> functionPlotDefs = graphViewHelper.getFunctionPlotDefs();
|
||||
|
||||
for (int i = 0; i < functionPlotDefs.size(); i++) {
|
||||
final FunctionPlotDef fpd = functionPlotDefs.get(i);
|
||||
computeGraph(fpd.getFunction(), minX, maxX, boundMinY, boundMaxY, graphs.get(i));
|
||||
// create path once
|
||||
final Path path = new Path();
|
||||
|
||||
graphToPath(graphs.get(i), path);
|
||||
for (int i = 0; i < functionPlotDefs.size(); i++) {
|
||||
final ArityPlotFunction fpd = functionPlotDefs.get(i);
|
||||
computeGraph(fpd.getFunction(), minX, maxX, boundMinY, boundMaxY, graphs.get(i));
|
||||
|
||||
path.transform(matrix);
|
||||
graphToPath(graphs.get(i), path);
|
||||
|
||||
fpd.getLineDef().applyToPaint(paint);
|
||||
path.transform(matrix);
|
||||
|
||||
canvas.drawPath(path, paint);
|
||||
AbstractCalculatorPlotFragment.applyToPaint(fpd.getLineDef(), paint);
|
||||
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lastMinX = minX;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -92,9 +92,9 @@ class Graph3d {
|
||||
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 FunctionLineDef lineDef = fpd.getLineDef();
|
||||
final PlotFunctionLineDef lineDef = fpd.getLineDef();
|
||||
final int NTICK = useHighQuality3d ? 5 : 0;
|
||||
|
||||
final float size = 4 * zoom;
|
||||
@ -285,7 +285,7 @@ class Graph3d {
|
||||
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 )
|
||||
final byte colors[] = new byte[polygonsⁿ * COLOR_COMPONENTS_COUNT];
|
||||
|
||||
@ -295,7 +295,7 @@ class Graph3d {
|
||||
final float z = vertices[j];
|
||||
|
||||
if (!Float.isNaN(z)) {
|
||||
if (lineDef.getLineColorType() == FunctionLineColorType.color_map) {
|
||||
if (lineDef.getLineColorType() == PlotFunctionLineColorType.color_map) {
|
||||
final float color = z / maxAbsZ;
|
||||
final float abs = Math.abs(color);
|
||||
colors[i] = floatToByte(color);
|
||||
|
@ -174,11 +174,11 @@ public class Graph3dView extends GLView implements GraphView {
|
||||
|
||||
@Override
|
||||
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) {
|
||||
for (FunctionPlotDef functionPlotDef: functionPlotDefs) {
|
||||
public void setFunctionPlotDefs(@NotNull List<ArityPlotFunction> functionPlotDefs) {
|
||||
for (ArityPlotFunction functionPlotDef: functionPlotDefs) {
|
||||
final int arity = functionPlotDef.getFunction().arity();
|
||||
if (arity != 0 && arity != 1 && arity != 2) {
|
||||
throw new IllegalArgumentException("Function must have arity 0 or 1 or 2 for 3d plot!");
|
||||
|
@ -13,7 +13,7 @@ public interface GraphView extends ZoomButtonsController.OnZoomListener, TouchHa
|
||||
|
||||
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 onResume();
|
||||
|
@ -16,7 +16,7 @@ public class GraphViewHelper {
|
||||
private FunctionViewDef functionViewDef = FunctionViewDef.newDefaultInstance();
|
||||
|
||||
@NotNull
|
||||
private List<FunctionPlotDef> functionPlotDefs = Collections.emptyList();
|
||||
private List<ArityPlotFunction> functionPlotDefs = Collections.emptyList();
|
||||
|
||||
private GraphViewHelper() {
|
||||
}
|
||||
@ -28,7 +28,7 @@ public class GraphViewHelper {
|
||||
|
||||
@NotNull
|
||||
public static GraphViewHelper newInstance(@NotNull FunctionViewDef functionViewDef,
|
||||
@NotNull List<FunctionPlotDef> functionPlotDefs) {
|
||||
@NotNull List<ArityPlotFunction> functionPlotDefs) {
|
||||
final GraphViewHelper result = new GraphViewHelper();
|
||||
|
||||
result.functionViewDef = functionViewDef;
|
||||
@ -38,7 +38,7 @@ public class GraphViewHelper {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GraphViewHelper copy(@NotNull List<FunctionPlotDef> newFunctionPlotDefs) {
|
||||
public GraphViewHelper copy(@NotNull List<ArityPlotFunction> newFunctionPlotDefs) {
|
||||
final GraphViewHelper result = new GraphViewHelper();
|
||||
|
||||
result.functionViewDef = functionViewDef;
|
||||
@ -48,7 +48,7 @@ public class GraphViewHelper {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<FunctionPlotDef> getFunctionPlotDefs() {
|
||||
public List<ArityPlotFunction> getFunctionPlotDefs() {
|
||||
return functionPlotDefs;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ class ZoomTracker {
|
||||
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 dy = y1-y2;
|
||||
// return (float) Math.sqrt(dx*dx+dy*dy);
|
||||
|
@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.mockito.Mockito;
|
||||
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
|
||||
import org.solovyev.android.calculator.history.CalculatorHistory;
|
||||
import org.solovyev.android.calculator.plot.CalculatorPlotter;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
@ -16,7 +17,7 @@ import org.solovyev.android.calculator.history.CalculatorHistory;
|
||||
public class CalculatorTestUtils {
|
||||
|
||||
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();
|
||||
|
||||
if ( context != null ) {
|
||||
|
@ -159,11 +159,8 @@ public enum CalculatorEventType {
|
||||
show_create_matrix_dialog,
|
||||
show_create_function_dialog,
|
||||
|
||||
//org.solovyev.android.calculator.plot.PlotInput
|
||||
plot_graph,
|
||||
|
||||
|
||||
plot_graph_3d,
|
||||
plot_data_changed,
|
||||
|
||||
//String
|
||||
show_evaluation_error;
|
||||
|
@ -3,6 +3,7 @@ package org.solovyev.android.calculator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
|
||||
import org.solovyev.android.calculator.history.CalculatorHistory;
|
||||
import org.solovyev.android.calculator.plot.CalculatorPlotter;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
@ -19,7 +20,8 @@ public interface CalculatorLocator {
|
||||
@NotNull CalculatorLogger logger,
|
||||
@NotNull CalculatorPreferenceService preferenceService,
|
||||
@NotNull CalculatorKeyboard keyboard,
|
||||
@NotNull CalculatorExternalListenersContainer externalListenersContainer);
|
||||
@NotNull CalculatorExternalListenersContainer externalListenersContainer,
|
||||
@NotNull CalculatorPlotter plotter);
|
||||
|
||||
@NotNull
|
||||
Calculator getCalculator();
|
||||
@ -48,6 +50,9 @@ public interface CalculatorLocator {
|
||||
@NotNull
|
||||
CalculatorLogger getLogger();
|
||||
|
||||
@NotNull
|
||||
CalculatorPlotter getPlotter();
|
||||
|
||||
@NotNull
|
||||
CalculatorPreferenceService getPreferenceService();
|
||||
|
||||
|
@ -4,7 +4,6 @@ import jscl.math.Generic;
|
||||
import jscl.math.function.Constant;
|
||||
import jscl.math.function.IConstant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@ -41,16 +40,4 @@ public final class CalculatorUtils {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package org.solovyev.android.calculator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
|
||||
import org.solovyev.android.calculator.history.CalculatorHistory;
|
||||
import org.solovyev.android.calculator.plot.CalculatorPlotter;
|
||||
|
||||
/**
|
||||
* User: Solovyev_S
|
||||
@ -47,7 +48,10 @@ public class Locator implements CalculatorLocator {
|
||||
@NotNull
|
||||
private CalculatorExternalListenersContainer calculatorExternalListenersContainer;
|
||||
|
||||
public Locator() {
|
||||
@NotNull
|
||||
private CalculatorPlotter calculatorPlotter;
|
||||
|
||||
public Locator() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -59,7 +63,8 @@ public class Locator implements CalculatorLocator {
|
||||
@NotNull CalculatorLogger logger,
|
||||
@NotNull CalculatorPreferenceService preferenceService,
|
||||
@NotNull CalculatorKeyboard keyboard,
|
||||
@NotNull CalculatorExternalListenersContainer externalListenersContainer) {
|
||||
@NotNull CalculatorExternalListenersContainer externalListenersContainer,
|
||||
@NotNull CalculatorPlotter plotter) {
|
||||
|
||||
this.calculator = calculator;
|
||||
this.calculatorEngine = engine;
|
||||
@ -69,6 +74,7 @@ public class Locator implements CalculatorLocator {
|
||||
this.calculatorLogger = logger;
|
||||
this.calculatorPreferenceService = preferenceService;
|
||||
this.calculatorExternalListenersContainer = externalListenersContainer;
|
||||
this.calculatorPlotter = plotter;
|
||||
|
||||
calculatorEditor = new CalculatorEditorImpl(this.calculator);
|
||||
calculatorDisplay = new CalculatorDisplayImpl(this.calculator);
|
||||
@ -134,6 +140,12 @@ public class Locator implements CalculatorLocator {
|
||||
return calculatorLogger;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CalculatorPlotter getPlotter() {
|
||||
return calculatorPlotter;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CalculatorPreferenceService getPreferenceService() {
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package org.solovyev.android.calculator.plot;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
* Date: 10/4/12
|
||||
@ -9,11 +7,19 @@ import android.graphics.Color;
|
||||
*/
|
||||
public enum GraphLineColor {
|
||||
|
||||
white(Color.WHITE),
|
||||
grey(Color.GRAY),
|
||||
red(Color.RED),
|
||||
blue(Color.rgb(16, 100, 140)),
|
||||
green(Color.GREEN);
|
||||
// Color.WHITE
|
||||
white(0xFFFFFFFF),
|
||||
|
||||
// Color.GRAY
|
||||
grey(0xFF888888),
|
||||
|
||||
// Color.RED
|
||||
red(0xFFFF0000),
|
||||
|
||||
blue(0xFF10648C),
|
||||
|
||||
// Color.GREEN
|
||||
green(0xFF00FF00);
|
||||
|
||||
private final int color;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ package org.solovyev.android.calculator.plot;
|
||||
* Date: 1/5/13
|
||||
* Time: 10:45 PM
|
||||
*/
|
||||
public enum FunctionLineColorType {
|
||||
public enum PlotFunctionLineColorType {
|
||||
|
||||
color_map,
|
||||
solid;
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package org.solovyev.android.calculator;
|
||||
import org.mockito.Mockito;
|
||||
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
|
||||
import org.solovyev.android.calculator.history.CalculatorHistory;
|
||||
import org.solovyev.android.calculator.plot.CalculatorPlotter;
|
||||
|
||||
/**
|
||||
* User: serso
|
||||
@ -12,7 +13,7 @@ import org.solovyev.android.calculator.history.CalculatorHistory;
|
||||
public class AbstractCalculatorTest {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import org.mockito.Mockito;
|
||||
import org.solovyev.android.calculator.external.CalculatorExternalListenersContainer;
|
||||
import org.solovyev.android.calculator.history.CalculatorHistory;
|
||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||
import org.solovyev.android.calculator.plot.CalculatorPlotter;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@ -24,7 +25,7 @@ public class CalculatorTestUtils {
|
||||
public static final int TIMEOUT = 3;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user