This commit is contained in:
serso 2016-02-28 13:08:10 +01:00
parent 4070de7693
commit f7b66673a5
16 changed files with 131 additions and 748 deletions

View File

@ -28,7 +28,11 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils;
import dagger.Lazy;
import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.math.function.CustomFunction;
import org.solovyev.android.Activities; import org.solovyev.android.Activities;
import org.solovyev.android.Check; import org.solovyev.android.Check;
import org.solovyev.android.calculator.about.AboutActivity; import org.solovyev.android.calculator.about.AboutActivity;
@ -38,31 +42,40 @@ import org.solovyev.android.calculator.functions.FunctionsActivity;
import org.solovyev.android.calculator.history.HistoryActivity; import org.solovyev.android.calculator.history.HistoryActivity;
import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity; import org.solovyev.android.calculator.matrix.CalculatorMatrixActivity;
import org.solovyev.android.calculator.operators.OperatorsActivity; import org.solovyev.android.calculator.operators.OperatorsActivity;
import org.solovyev.android.calculator.plot.CalculatorPlotter; import org.solovyev.android.calculator.plot.ExpressionFunction;
import org.solovyev.android.calculator.plot.PlotActivity; import org.solovyev.android.calculator.plot.PlotActivity;
import org.solovyev.android.calculator.preferences.PreferencesActivity; import org.solovyev.android.calculator.preferences.PreferencesActivity;
import org.solovyev.android.calculator.variables.CppVariable; import org.solovyev.android.calculator.variables.CppVariable;
import org.solovyev.android.calculator.variables.EditVariableFragment; import org.solovyev.android.calculator.variables.EditVariableFragment;
import org.solovyev.android.calculator.variables.VariablesActivity; import org.solovyev.android.calculator.variables.VariablesActivity;
import org.solovyev.android.calculator.variables.VariablesFragment; import org.solovyev.android.calculator.variables.VariablesFragment;
import org.solovyev.android.plotter.Plotter;
import org.solovyev.common.msg.MessageType; import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.Strings; import org.solovyev.common.text.Strings;
import jscl.math.Generic;
import jscl.math.function.Constant;
import java.util.Set;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@Singleton @Singleton
public final class ActivityLauncher implements CalculatorEventListener { public final class ActivityLauncher implements CalculatorEventListener {
@Inject @Inject
Application application; Application application;
@Inject
Lazy<Plotter> plotter;
@Inject
Lazy<ErrorReporter> errorReporter;
@Inject
Lazy<Display> display;
@Inject
Lazy<VariablesRegistry> variablesRegistry;
@Inject
Notifier notifier;
@Nullable @Nullable
private CalculatorActivity activity; private CalculatorActivity activity;
@ -109,7 +122,7 @@ public final class ActivityLauncher implements CalculatorEventListener {
final CppFunction.Builder builder = CppFunction.builder("", functionBody); final CppFunction.Builder builder = CppFunction.builder("", functionBody);
final Generic generic = viewState.getResult(); final Generic generic = viewState.getResult();
if (generic != null) { if (generic != null) {
final Set<Constant> constants = CalculatorUtils.getNotSystemConstants(generic); final Set<Constant> constants = generic.getUndefinedConstants(null);
for (Constant constant : constants) { for (Constant constant : constants) {
builder.withParameter(constant.getName()); builder.withParameter(constant.getName());
} }
@ -128,26 +141,13 @@ public final class ActivityLauncher implements CalculatorEventListener {
return ((CalculatorApplication) App.getApplication()).notifier; return ((CalculatorApplication) App.getApplication()).notifier;
} }
public static void tryPlot() { public void plotDisplayedExpression() {
final CalculatorPlotter plotter = Locator.getInstance().getPlotter(); final DisplayState state = display.get().getState();
final Display display = App.getDisplay(); if (!state.valid) {
final DisplayState viewState = display.getState();
if (viewState.valid) {
final String functionValue = viewState.text;
final Generic expression = viewState.getResult();
if (!Strings.isEmpty(functionValue) && expression != null) {
if (plotter.isPlotPossibleFor(expression)) {
plotter.plot(expression);
} else {
getNotifier().showMessage(R.string.cpp_plot_too_many_variables, MessageType.error);
}
} else {
getNotifier().showMessage(R.string.cpp_plot_empty_function_error, MessageType.error);
}
} else {
getNotifier().showMessage(R.string.not_valid_result, MessageType.error); getNotifier().showMessage(R.string.not_valid_result, MessageType.error);
return;
} }
plot(state.getResult());
} }
public void showHistory() { public void showHistory() {
@ -258,4 +258,44 @@ public final class ActivityLauncher implements CalculatorEventListener {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent); context.startActivity(intent);
} }
void plot(@Nullable Generic expression) {
if (expression == null) {
notifier.showMessage(R.string.cpp_plot_empty_function_error);
return;
}
final String content = expression.toString();
if (TextUtils.isEmpty(content)) {
notifier.showMessage(R.string.cpp_plot_empty_function_error);
return;
}
final List<String> parameters = new ArrayList<>();
for (Constant parameter : expression.getUndefinedConstants(variablesRegistry.get())) {
parameters.add(parameter.getName());
}
if (parameters.size() > 2) {
notifier.showMessage(R.string.cpp_plot_too_many_variables);
return;
}
try {
final CustomFunction f = new CustomFunction.Builder().setName("").setParameterNames(parameters).setContent(content).create();
final ExpressionFunction ef = new ExpressionFunction(f, false);
plotter.get().add(ef);
showPlotter();
} catch (RuntimeException e) {
errorReporter.get().onException(e);
notifier.showMessage(e.getLocalizedMessage());
}
}
public boolean canPlot(@Nullable Generic expression) {
if (expression == null || TextUtils.isEmpty(expression.toString())) {
return false;
}
if (expression.getUndefinedConstants(variablesRegistry.get()).size() > 2) {
return false;
}
return true;
}
} }

View File

@ -27,9 +27,8 @@ import android.os.Handler;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log; import android.util.Log;
import android.util.TimingLogger; import android.util.TimingLogger;
import com.squareup.otto.Bus; import com.squareup.otto.Bus;
import jscl.MathEngine;
import org.acra.ACRA; import org.acra.ACRA;
import org.acra.ACRAConfiguration; import org.acra.ACRAConfiguration;
import org.acra.sender.HttpSender; import org.acra.sender.HttpSender;
@ -38,18 +37,13 @@ import org.solovyev.android.calculator.floating.FloatingCalculatorActivity;
import org.solovyev.android.calculator.history.History; import org.solovyev.android.calculator.history.History;
import org.solovyev.android.calculator.language.Language; import org.solovyev.android.calculator.language.Language;
import org.solovyev.android.calculator.language.Languages; import org.solovyev.android.calculator.language.Languages;
import org.solovyev.android.calculator.plot.AndroidCalculatorPlotter;
import org.solovyev.android.calculator.plot.CalculatorPlotterImpl;
import org.solovyev.common.msg.MessageType; import org.solovyev.common.msg.MessageType;
import jscl.MathEngine;
import java.util.Locale;
import java.util.concurrent.Executor;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import java.util.Locale;
import java.util.concurrent.Executor;
public class CalculatorApplication extends android.app.Application implements SharedPreferences.OnSharedPreferenceChangeListener { public class CalculatorApplication extends android.app.Application implements SharedPreferences.OnSharedPreferenceChangeListener {
@ -143,11 +137,7 @@ public class CalculatorApplication extends android.app.Application implements Sh
languages.updateContextLocale(this, true); languages.updateContextLocale(this, true);
App.getGa().reportInitially(preferences); App.getGa().reportInitially(preferences);
Locator.getInstance().init(calculator, Locator.getInstance().init(calculator, engine, keyboard);
engine,
keyboard,
new AndroidCalculatorPlotter(this, new CalculatorPlotterImpl(calculator))
);
calculator.init(initThread); calculator.init(initThread);

View File

@ -22,16 +22,13 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public interface CalculatorLocator { public interface CalculatorLocator {
void init(@Nonnull Calculator calculator, void init(@Nonnull Calculator calculator,
@Nonnull Engine engine, @Nonnull Engine engine,
@Nonnull Keyboard keyboard, @Nonnull Keyboard keyboard);
@Nonnull CalculatorPlotter plotter);
@Nonnull @Nonnull
Calculator getCalculator(); Calculator getCalculator();
@ -42,6 +39,4 @@ public interface CalculatorLocator {
@Nonnull @Nonnull
Keyboard getKeyboard(); Keyboard getKeyboard();
@Nonnull
CalculatorPlotter getPlotter();
} }

View File

@ -22,15 +22,6 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.math.function.IConstant;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nonnull;
/** /**
* User: serso * User: serso
* Date: 9/22/12 * Date: 9/22/12
@ -42,18 +33,4 @@ public final class CalculatorUtils {
throw new AssertionError(); throw new AssertionError();
} }
@Nonnull
public static Set<Constant> getNotSystemConstants(@Nonnull Generic expression) {
final Set<Constant> notSystemConstants = new HashSet<Constant>();
for (Constant constant : expression.getConstants()) {
IConstant var = Locator.getInstance().getEngine().getVariablesRegistry().get(constant.getName());
if (var != null && !var.isSystem() && !var.isDefined()) {
notSystemConstants.add(constant);
}
}
return notSystemConstants;
}
} }

View File

@ -57,8 +57,6 @@ public class Display {
Lazy<Notifier> notifier; Lazy<Notifier> notifier;
@Inject @Inject
Lazy<PreferredPreferences> preferredPreferences; Lazy<PreferredPreferences> preferredPreferences;
@Inject
ActivityLauncher launcher;
@Nullable @Nullable
private DisplayView view; private DisplayView view;
@Nonnull @Nonnull

View File

@ -37,17 +37,12 @@ import butterknife.ButterKnife;
import com.squareup.otto.Bus; import com.squareup.otto.Bus;
import jscl.NumeralBase; import jscl.NumeralBase;
import jscl.math.Generic; import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.math.function.CustomFunction;
import org.solovyev.android.calculator.converter.ConverterFragment; import org.solovyev.android.calculator.converter.ConverterFragment;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.plot.ExpressionFunction;
import org.solovyev.android.plotter.Plotter; import org.solovyev.android.plotter.Plotter;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
public class DisplayFragment extends BaseFragment implements View.OnClickListener, public class DisplayFragment extends BaseFragment implements View.OnClickListener,
MenuItem.OnMenuItemClickListener { MenuItem.OnMenuItemClickListener {
@ -146,8 +141,7 @@ public class DisplayFragment extends BaseFragment implements View.OnClickListene
addMenu(menu, R.string.c_convert, this); addMenu(menu, R.string.c_convert, this);
} }
} }
final int parameters = CalculatorUtils.getNotSystemConstants(result).size(); if (launcher.canPlot(result)) {
if (parameters >= 0 && parameters <= 2) {
addMenu(menu, R.string.c_plot, this); addMenu(menu, R.string.c_plot, this);
} }
} }
@ -216,21 +210,7 @@ public class DisplayFragment extends BaseFragment implements View.OnClickListene
ConverterFragment.show(getActivity(), getValue(result)); ConverterFragment.show(getActivity(), getValue(result));
return true; return true;
case R.string.c_plot: case R.string.c_plot:
if (result != null) { launcher.plot(result);
try {
final List<String> parameters = new ArrayList<>();
for (Constant parameter : CalculatorUtils.getNotSystemConstants(result)) {
parameters.add(parameter.getName());
}
new CustomFunction.Builder().setParameterNames(parameters).setContent(state.text).create();
final CustomFunction f = new CustomFunction.Builder().setName("").setParameterNames(parameters).setContent(result.toString()).create();
final ExpressionFunction ef = new ExpressionFunction(f, false);
plotter.add(ef);
launcher.showPlotter();
} catch (RuntimeException e) {
errorReporter.onException(e);
}
}
return true; return true;
default: default:
return false; return false;

View File

@ -22,8 +22,6 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public class Locator implements CalculatorLocator { public class Locator implements CalculatorLocator {
@ -36,8 +34,6 @@ public class Locator implements CalculatorLocator {
private Calculator calculator; private Calculator calculator;
@Nonnull @Nonnull
private Keyboard keyboard; private Keyboard keyboard;
@Nonnull
private CalculatorPlotter calculatorPlotter;
public Locator() { public Locator() {
} }
@ -50,13 +46,10 @@ public class Locator implements CalculatorLocator {
@Override @Override
public void init(@Nonnull Calculator calculator, public void init(@Nonnull Calculator calculator,
@Nonnull Engine engine, @Nonnull Engine engine,
@Nonnull Keyboard keyboard, @Nonnull Keyboard keyboard) {
@Nonnull CalculatorPlotter plotter) {
this.calculator = calculator; this.calculator = calculator;
this.engine = engine; this.engine = engine;
this.calculatorPlotter = plotter;
this.keyboard = keyboard; this.keyboard = keyboard;
} }
@ -78,9 +71,4 @@ public class Locator implements CalculatorLocator {
return keyboard; return keyboard;
} }
@Nonnull
@Override
public CalculatorPlotter getPlotter() {
return calculatorPlotter;
}
} }

View File

@ -24,17 +24,14 @@ package org.solovyev.android.calculator;
import android.app.Application; import android.app.Application;
import android.os.Handler; import android.os.Handler;
import android.support.annotation.StringRes;
import android.widget.Toast; import android.widget.Toast;
import org.solovyev.android.Threads; import org.solovyev.android.Threads;
import org.solovyev.android.msg.AndroidMessage;
import org.solovyev.common.msg.Message; import org.solovyev.common.msg.Message;
import org.solovyev.common.msg.MessageType;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.List;
@Singleton @Singleton
public class Notifier { public class Notifier {
@ -48,21 +45,22 @@ public class Notifier {
} }
public void showMessage(@Nonnull Message message) { public void showMessage(@Nonnull Message message) {
showMessageInUiThread(message.getLocalizedMessage()); showMessage(message.getLocalizedMessage());
} }
public void showMessage(@Nonnull Integer messageCode, @Nonnull MessageType messageType, @Nonnull List<Object> parameters) { public void showMessage(@StringRes int message, Object... parameters) {
showMessage(new AndroidMessage(messageCode, messageType, application, parameters)); showMessage(application.getString(message, parameters));
} }
public void showMessage(@Nonnull Integer messageCode, @Nonnull MessageType messageType, @Nullable Object... parameters) { public void showMessage(@StringRes int message) {
showMessage(new AndroidMessage(messageCode, messageType, application, parameters)); showMessage(application.getString(message));
} }
private void showMessageInUiThread(@Nonnull final String message) { public void showMessage(@Nonnull final String message) {
if (Threads.isUiThread()) { if (Threads.isUiThread()) {
Toast.makeText(application, message, Toast.LENGTH_SHORT).show(); Toast.makeText(application, message, Toast.LENGTH_SHORT).show();
} else { return;
}
handler.post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -71,4 +69,3 @@ public class Notifier {
}); });
} }
} }
}

View File

@ -1,13 +1,5 @@
package org.solovyev.android.calculator.keyboard; package org.solovyev.android.calculator.keyboard;
import static android.view.HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING;
import static android.view.HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING;
import static android.view.HapticFeedbackConstants.KEYBOARD_TAP;
import static org.solovyev.android.calculator.App.cast;
import static org.solovyev.android.calculator.App.getScreenMetrics;
import static org.solovyev.android.calculator.Preferences.Gui.Layout.simple;
import static org.solovyev.android.calculator.Preferences.Gui.Layout.simple_mobile;
import android.app.Activity; import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
@ -20,15 +12,8 @@ import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.solovyev.android.Views; import org.solovyev.android.Views;
import org.solovyev.android.calculator.ActivityUi; import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.Calculator;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.Keyboard;
import org.solovyev.android.calculator.Preferences;
import org.solovyev.android.calculator.PreferredPreferences;
import org.solovyev.android.calculator.buttons.CppSpecialButton; import org.solovyev.android.calculator.buttons.CppSpecialButton;
import org.solovyev.android.calculator.view.ScreenMetrics; import org.solovyev.android.calculator.view.ScreenMetrics;
import org.solovyev.android.views.Adjuster; import org.solovyev.android.views.Adjuster;
@ -37,11 +22,16 @@ import org.solovyev.android.views.dragbutton.DragButton;
import org.solovyev.android.views.dragbutton.DragDirection; import org.solovyev.android.views.dragbutton.DragDirection;
import org.solovyev.android.views.dragbutton.SimpleDragListener; import org.solovyev.android.views.dragbutton.SimpleDragListener;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.annotation.Nonnull; import static android.view.HapticFeedbackConstants.*;
import javax.inject.Inject; import static org.solovyev.android.calculator.App.cast;
import static org.solovyev.android.calculator.App.getScreenMetrics;
import static org.solovyev.android.calculator.Preferences.Gui.Layout.simple;
import static org.solovyev.android.calculator.Preferences.Gui.Layout.simple_mobile;
public abstract class BaseKeyboardUi implements SharedPreferences.OnSharedPreferenceChangeListener, SimpleDragListener.DragProcessor, View.OnClickListener { public abstract class BaseKeyboardUi implements SharedPreferences.OnSharedPreferenceChangeListener, SimpleDragListener.DragProcessor, View.OnClickListener {
@ -67,6 +57,8 @@ public abstract class BaseKeyboardUi implements SharedPreferences.OnSharedPrefer
@Inject @Inject
Calculator calculator; Calculator calculator;
@Inject @Inject
ActivityLauncher launcher;
@Inject
PreferredPreferences preferredPreferences; PreferredPreferences preferredPreferences;
protected int orientation = Configuration.ORIENTATION_PORTRAIT; protected int orientation = Configuration.ORIENTATION_PORTRAIT;
private int textSize; private int textSize;

View File

@ -17,7 +17,6 @@ import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import jscl.NumeralBase; import jscl.NumeralBase;
import org.solovyev.android.Check; import org.solovyev.android.Check;
import org.solovyev.android.calculator.ActivityLauncher;
import org.solovyev.android.calculator.Engine; import org.solovyev.android.calculator.Engine;
import org.solovyev.android.calculator.Preferences; import org.solovyev.android.calculator.Preferences;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
@ -121,7 +120,7 @@ public class PartialKeyboardUi extends BaseKeyboardUi {
return true; return true;
case R.id.cpp_button_equals: case R.id.cpp_button_equals:
if (direction == down) { if (direction == down) {
ActivityLauncher.tryPlot(); launcher.plotDisplayedExpression();
return true; return true;
} else if (direction == up) { } else if (direction == up) {
calculator.simplify(); calculator.simplify();

View File

@ -1,442 +0,0 @@
/*
* Copyright 2013 serso aka se.solovyev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Contact details
*
* Email: se.solovyev@gmail.com
* Site: http://se.solovyev.org
*/
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.solovyev.android.calculator.Calculator;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.CalculatorUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CalculatorPlotterImpl implements CalculatorPlotter {
@Nonnull
private final List<PlotFunction> functions = new ArrayList<PlotFunction>();
@Nonnull
private final Calculator calculator;
private final PlotResourceManager resourceManager = new MapPlotResourceManager();
private boolean plot3d = false;
private boolean adjustYAxis = true;
private boolean plotImag = false;
private int arity = 0;
@Nonnull
private PlotBoundaries plotBoundaries = PlotBoundaries.newDefaultInstance();
@Nonnull
private PlotData plotData = new PlotData(Collections.<PlotFunction>emptyList(), plot3d, true, plotBoundaries);
public CalculatorPlotterImpl(@Nonnull Calculator calculator) {
this.calculator = calculator;
}
@Nonnull
@Override
public PlotData getPlotData() {
return plotData;
}
@Override
public boolean addFunction(@Nonnull Generic expression) {
final List<Constant> variables = new ArrayList<Constant>(CalculatorUtils.getNotSystemConstants(expression));
if (variables.size() > 2) throw new AssertionError();
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 XyFunction realXyFunction = new XyFunction(expression, xVariable, yVariable, false);
final XyFunction imagXyFunction = new XyFunction(expression, xVariable, yVariable, true);
// first create plot functions with default line definitions
PlotFunction realPlotFunction = new PlotFunction(realXyFunction);
PlotFunction imagPlotFunction = new PlotFunction(imagXyFunction);
// then remove all unpinned graphs and free their line definitions
removeAllUnpinnedExcept(realPlotFunction, imagPlotFunction);
// create plot functions with freed line definitions
realPlotFunction = newPlotFunction(realXyFunction);
imagPlotFunction = newPlotFunction(imagXyFunction);
final boolean realAdded = addFunction(realPlotFunction);
final boolean imagAdded = addFunction(plotImag ? imagPlotFunction : PlotFunction.invisible(imagPlotFunction));
return imagAdded || realAdded;
}
@Nonnull
private PlotFunction newPlotFunction(@Nonnull XyFunction xyFunction) {
return new PlotFunction(xyFunction, resourceManager.generateAndRegister());
}
@Override
public boolean addFunction(@Nonnull PlotFunction plotFunction) {
synchronized (functions) {
if (!functions.contains(plotFunction)) {
resourceManager.register(plotFunction.getPlotLineDef());
functions.add(plotFunction);
onFunctionsChanged();
return true;
} else {
return false;
}
}
}
private boolean removeAllUnpinnedExcept(@Nonnull final PlotFunction... exceptFunctions) {
synchronized (functions) {
boolean changed = Iterables.removeIf(functions, new Predicate<PlotFunction>() {
@Override
public boolean apply(@Nullable PlotFunction function) {
if (function != null && !function.isPinned()) {
for (PlotFunction exceptFunction : exceptFunctions) {
if (exceptFunction.equals(function)) {
return false;
}
}
resourceManager.unregister(function.getPlotLineDef());
return true;
} else {
return false;
}
}
});
if (changed) {
onFunctionsChanged();
}
return changed;
}
}
@Override
public void removeAllUnpinned() {
synchronized (functions) {
boolean changed = Iterables.removeIf(functions, new Predicate<PlotFunction>() {
@Override
public boolean apply(@Nullable PlotFunction function) {
boolean removed = function != null && !function.isPinned();
if (removed) {
resourceManager.unregister(function.getPlotLineDef());
}
return removed;
}
});
if (changed) {
onFunctionsChanged();
}
}
}
@Override
public boolean removeFunction(@Nonnull PlotFunction plotFunction) {
synchronized (functions) {
boolean changed = functions.remove(plotFunction);
if (changed) {
resourceManager.unregister(plotFunction.getPlotLineDef());
onFunctionsChanged();
}
return changed;
}
}
@Override
public boolean addFunction(@Nonnull XyFunction xyFunction) {
return addFunction(newPlotFunction(xyFunction));
}
@Override
public boolean addFunction(@Nonnull XyFunction xyFunction, @Nonnull PlotLineDef functionLineDef) {
return addFunction(new PlotFunction(xyFunction, functionLineDef));
}
@Override
public boolean updateFunction(@Nonnull XyFunction xyFunction, @Nonnull PlotLineDef functionLineDef) {
final PlotFunction newFunction = new PlotFunction(xyFunction, functionLineDef);
return updateFunction(newFunction);
}
@Override
public boolean updateFunction(@Nonnull PlotFunction newFunction) {
boolean changed = updateFunction0(newFunction);
if (changed) {
firePlotDataChangedEvent();
}
return changed;
}
public boolean updateFunction0(@Nonnull PlotFunction newFunction) {
boolean changed = false;
synchronized (functions) {
for (int i = 0; i < functions.size(); i++) {
final PlotFunction oldFunction = functions.get(i);
if (oldFunction.equals(newFunction)) {
resourceManager.unregister(oldFunction.getPlotLineDef());
resourceManager.register(newFunction.getPlotLineDef());
// update old function
functions.set(i, newFunction);
changed = true;
break;
}
}
}
return changed;
}
@Override
public boolean removeFunction(@Nonnull XyFunction xyFunction) {
return removeFunction(new PlotFunction(xyFunction));
}
@Nonnull
@Override
public PlotFunction pin(@Nonnull PlotFunction plotFunction) {
final PlotFunction newFunction = PlotFunction.pin(plotFunction);
updateFunction0(newFunction);
return newFunction;
}
@Nonnull
@Override
public PlotFunction unpin(@Nonnull PlotFunction plotFunction) {
final PlotFunction newFunction = PlotFunction.unpin(plotFunction);
updateFunction0(newFunction);
return newFunction;
}
@Nonnull
@Override
public PlotFunction show(@Nonnull PlotFunction plotFunction) {
final PlotFunction newFunction = PlotFunction.visible(plotFunction);
updateFunction(newFunction);
return newFunction;
}
@Nonnull
@Override
public PlotFunction hide(@Nonnull PlotFunction plotFunction) {
final PlotFunction newFunction = PlotFunction.invisible(plotFunction);
updateFunction(newFunction);
return newFunction;
}
@Override
public void clearAllFunctions() {
synchronized (functions) {
resourceManager.unregisterAll();
functions.clear();
onFunctionsChanged();
}
}
@Nullable
@Override
public PlotFunction getFunctionById(@Nonnull final String functionId) {
synchronized (functions) {
return Iterables.find(functions, new Predicate<PlotFunction>() {
@Override
public boolean apply(@Nullable PlotFunction function) {
return function != null && function.getXyFunction().getId().equals(functionId);
}
}, null);
}
}
// NOTE: this method must be called from synchronized block
private void onFunctionsChanged() {
if (!Thread.holdsLock(functions)) throw new AssertionError();
int maxArity = 0;
for (PlotFunction function : functions) {
final XyFunction xyFunction = function.getXyFunction();
maxArity = Math.max(maxArity, xyFunction.getArity());
}
plot3d = maxArity > 1;
if (functions.isEmpty()) {
// no functions => new plot => default boundaries
this.plotBoundaries = PlotBoundaries.newDefaultInstance();
this.adjustYAxis = true;
}
arity = maxArity;
firePlotDataChangedEvent();
}
@Nonnull
@Override
public List<PlotFunction> getFunctions() {
synchronized (functions) {
return new ArrayList<PlotFunction>(functions);
}
}
@Nonnull
@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 void plot(@Nonnull Generic expression) {
addFunction(expression);
plot();
}
@Override
public boolean is2dPlotPossible() {
return arity < 2;
}
@Override
public boolean isPlotPossibleFor(@Nonnull 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() {
updatePlotData();
calculator.fireCalculatorEvent(CalculatorEventType.plot_data_changed, plotData);
}
private void updatePlotData() {
plotData = new PlotData(getVisibleFunctions(), plot3d, adjustYAxis, plotBoundaries);
}
@Override
public void setPlotImag(boolean plotImag) {
if (this.plotImag != plotImag) {
this.plotImag = plotImag;
if (toggleImagFunctions(this.plotImag)) {
firePlotDataChangedEvent();
}
}
}
@Override
public void savePlotBoundaries(@Nonnull PlotBoundaries plotBoundaries) {
if (!this.plotBoundaries.equals(plotBoundaries)) {
this.plotBoundaries = plotBoundaries;
this.adjustYAxis = false;
updatePlotData();
}
}
@Override
public void setPlotBoundaries(@Nonnull PlotBoundaries plotBoundaries) {
if (!this.plotBoundaries.equals(plotBoundaries)) {
this.plotBoundaries = plotBoundaries;
this.adjustYAxis = false;
firePlotDataChangedEvent();
}
}
private boolean toggleImagFunctions(boolean show) {
boolean changed = false;
synchronized (functions) {
for (int i = 0; i < functions.size(); i++) {
final PlotFunction plotFunction = functions.get(i);
if (plotFunction.getXyFunction().isImag()) {
functions.set(i, show ? PlotFunction.visible(plotFunction) : PlotFunction.invisible(plotFunction));
changed = true;
}
}
}
return changed;
}
}

View File

@ -1,152 +0,0 @@
/*
* Copyright 2013 serso aka se.solovyev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Contact details
*
* Email: se.solovyev@gmail.com
* Site: http://se.solovyev.org
*/
package org.solovyev.android.calculator.plot;
import android.content.Context;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.TextView;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.android.list.ListItem;
import org.solovyev.android.view.ViewBuilder;
import org.solovyev.android.view.ViewFromLayoutBuilder;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PlotFunctionListItem implements ListItem {
private static final String PREFIX = "plot_function_";
@Nonnull
private PlotFunction plotFunction;
@Nonnull
private ViewBuilder<View> viewBuilder;
@Nonnull
private String tag;
public PlotFunctionListItem(@Nonnull PlotFunction plotFunction) {
this.plotFunction = plotFunction;
this.viewBuilder = ViewFromLayoutBuilder.newInstance(R.layout.cpp_plot_function_list_item);
this.tag = PREFIX + plotFunction.getXyFunction().getExpressionString();
}
@Nullable
@Override
public OnClickAction getOnClickAction() {
return null;
}
@Nullable
@Override
public OnClickAction getOnLongClickAction() {
return null;
}
@Nonnull
@Override
public View updateView(@Nonnull Context context, @Nonnull View view) {
final Object viewTag = view.getTag();
if (viewTag instanceof String) {
if (this.tag.equals(viewTag)) {
return view;
} else if (((String) viewTag).startsWith(PREFIX)) {
fillView(view, context);
return view;
} else {
return build(context);
}
}
return build(context);
}
@Nonnull
@Override
public View build(@Nonnull Context context) {
final View root = buildView(context);
fillView(root, context);
return root;
}
private View buildView(@Nonnull Context context) {
return viewBuilder.build(context);
}
private void fillView(@Nonnull View root, @Nonnull final Context context) {
root.setTag(tag);
final CalculatorPlotter plotter = Locator.getInstance().getPlotter();
final TextView expressionTextView = (TextView) root.findViewById(R.id.cpp_plot_function_expression_textview);
expressionTextView.setText(plotFunction.getXyFunction().getExpressionString());
final CheckBox pinnedCheckBox = (CheckBox) root.findViewById(R.id.cpp_plot_function_pinned_checkbox);
pinnedCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean pin) {
if (pin) {
if (!plotFunction.isPinned()) {
plotFunction = plotter.pin(plotFunction);
}
} else {
if (plotFunction.isPinned()) {
plotFunction = plotter.unpin(plotFunction);
}
}
}
});
pinnedCheckBox.setChecked(plotFunction.isPinned());
final CheckBox visibleCheckBox = (CheckBox) root.findViewById(R.id.cpp_plot_function_visible_checkbox);
visibleCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean show) {
if (show) {
if (!plotFunction.isVisible()) {
plotFunction = plotter.show(plotFunction);
}
} else {
if (plotFunction.isVisible()) {
plotFunction = plotter.hide(plotFunction);
}
}
}
});
visibleCheckBox.setChecked(plotFunction.isVisible());
final ImageButton settingsButton = (ImageButton) root.findViewById(R.id.cpp_plot_function_settings_button);
settingsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// FIXME: 2016-02-01
}
});
}
}

View File

@ -24,7 +24,6 @@ package org.solovyev.android.calculator;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import com.squareup.otto.Bus; import com.squareup.otto.Bus;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -38,7 +37,7 @@ import static org.mockito.Mockito.mock;
public class AbstractCalculatorTest { public class AbstractCalculatorTest {
protected void setUp() throws Exception { protected void setUp() throws Exception {
Locator.getInstance().init(new Calculator(mock(SharedPreferences.class), mock(Bus.class), mock(Executor.class), mock(Executor.class)), CalculatorTestUtils.newCalculatorEngine(), mock(Keyboard.class), mock(CalculatorPlotter.class)); Locator.getInstance().init(new Calculator(mock(SharedPreferences.class), mock(Bus.class), mock(Executor.class), mock(Executor.class)), CalculatorTestUtils.newCalculatorEngine(), mock(Keyboard.class));
Locator.getInstance().getEngine().init(new Executor() { Locator.getInstance().getEngine().init(new Executor() {
@Override @Override
public void execute(Runnable command) { public void execute(Runnable command) {

View File

@ -36,7 +36,6 @@ import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.language.Languages; import org.solovyev.android.calculator.language.Languages;
import org.solovyev.android.calculator.operators.OperatorsRegistry; import org.solovyev.android.calculator.operators.OperatorsRegistry;
import org.solovyev.android.calculator.operators.PostfixFunctionsRegistry; import org.solovyev.android.calculator.operators.PostfixFunctionsRegistry;
import org.solovyev.android.calculator.plot.CalculatorPlotter;
import jscl.JsclMathEngine; import jscl.JsclMathEngine;
@ -68,7 +67,7 @@ public class CalculatorTestUtils {
public static void staticSetUp() throws Exception { public static void staticSetUp() throws Exception {
App.init(new CalculatorApplication(), new Languages(new RoboSharedPreferences(new HashMap<String, Map<String, Object>>(), "test", 0))); App.init(new CalculatorApplication(), new Languages(new RoboSharedPreferences(new HashMap<String, Map<String, Object>>(), "test", 0)));
Locator.getInstance().init(new Calculator(mock(SharedPreferences.class), mock(Bus.class), mock(Executor.class), mock(Executor.class)), newCalculatorEngine(), mock(Keyboard.class), mock(CalculatorPlotter.class)); Locator.getInstance().init(new Calculator(mock(SharedPreferences.class), mock(Bus.class), mock(Executor.class), mock(Executor.class)), newCalculatorEngine(), mock(Keyboard.class));
Locator.getInstance().getEngine().init(new Executor() { Locator.getInstance().getEngine().init(new Executor() {
@Override @Override
public void execute(Runnable command) { public void execute(Runnable command) {
@ -83,7 +82,7 @@ public class CalculatorTestUtils {
} }
public static void staticSetUp(@Nullable Context context) throws Exception { public static void staticSetUp(@Nullable Context context) throws Exception {
Locator.getInstance().init(new Calculator(mock(SharedPreferences.class), mock(Bus.class), mock(Executor.class), mock(Executor.class)), newCalculatorEngine(), mock(Keyboard.class), mock(CalculatorPlotter.class)); Locator.getInstance().init(new Calculator(mock(SharedPreferences.class), mock(Bus.class), mock(Executor.class), mock(Executor.class)), newCalculatorEngine(), mock(Keyboard.class));
Locator.getInstance().getEngine().init(new Executor() { Locator.getInstance().getEngine().init(new Executor() {
@Override @Override
public void execute(Runnable command) { public void execute(Runnable command) {

View File

@ -1,17 +1,40 @@
package jscl.math; package jscl.math;
import jscl.math.function.Constant; import jscl.math.function.Constant;
import jscl.math.function.IConstant;
import jscl.mathml.MathML; import jscl.mathml.MathML;
import jscl.text.ParserUtils; import jscl.text.ParserUtils;
import org.solovyev.common.math.MathRegistry;
import java.math.BigInteger;
import java.util.Set;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;
public abstract class Generic implements Arithmetic<Generic>, Comparable { public abstract class Generic implements Arithmetic<Generic>, Comparable {
@Nonnull
public Set<Constant> getUndefinedConstants(@Nonnull MathRegistry<IConstant> constantsRegistry) {
final Set<Constant> result = new HashSet<>();
for (Constant expressionConstant : getConstants()) {
final IConstant registryConstant = constantsRegistry.get(expressionConstant.getName());
if (registryConstant == null) {
continue;
}
if (registryConstant.isSystem()) {
continue;
}
if(registryConstant.isDefined()) {
continue;
}
result.add(expressionConstant);
}
return result;
}
public BigInteger toBigInteger() { public BigInteger toBigInteger() {
return null; return null;
} }

View File

@ -43,7 +43,7 @@ public class CustomFunction extends Function implements IFunction {
private CustomFunction(@Nonnull String name, private CustomFunction(@Nonnull String name,
@Nonnull List<String> parameterNames, @Nonnull List<String> parameterNames,
@Nonnull String content, @Nonnull String content,
@Nullable String description) { @Nullable String description) throws CustomFunctionCalculationException {
super(name, new Generic[parameterNames.size()]); super(name, new Generic[parameterNames.size()]);
this.parameterNames = parameterNames; this.parameterNames = parameterNames;
try { try {
@ -321,7 +321,7 @@ public class CustomFunction extends Function implements IFunction {
} }
@Nonnull @Nonnull
public CustomFunction create() { public CustomFunction create() throws CustomFunctionCalculationException {
final CustomFunction customFunction = new CustomFunction(name, parameterNames, prepareContent(content), description); final CustomFunction customFunction = new CustomFunction(name, parameterNames, prepareContent(content), description);
customFunction.setSystem(system); customFunction.setSystem(system);
if (id != null) { if (id != null) {