Compare commits
3 Commits
1.0.0+4
...
c9190d05a1
Author | SHA1 | Date | |
---|---|---|---|
c9190d05a1
|
|||
40dc6f8511
|
|||
2a56a83898
|
@@ -7,6 +7,9 @@ abstract class Expr {
|
|||||||
/// 新增:对表达式进行“求值/数值化”——尽可能把可算的部分算出来
|
/// 新增:对表达式进行“求值/数值化”——尽可能把可算的部分算出来
|
||||||
Expr evaluate();
|
Expr evaluate();
|
||||||
|
|
||||||
|
/// Substitute variable with value
|
||||||
|
Expr substitute(String varName, Expr value);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString();
|
String toString();
|
||||||
|
|
||||||
@@ -27,6 +30,9 @@ class IntExpr extends Expr {
|
|||||||
@override
|
@override
|
||||||
Expr evaluate() => this;
|
Expr evaluate() => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) => this;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => value.toString();
|
String toString() => value.toString();
|
||||||
}
|
}
|
||||||
@@ -42,10 +48,31 @@ class DoubleExpr extends Expr {
|
|||||||
@override
|
@override
|
||||||
Expr evaluate() => this;
|
Expr evaluate() => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) => this;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => value.toString();
|
String toString() => value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === VarExpr ===
|
||||||
|
class VarExpr extends Expr {
|
||||||
|
final String name;
|
||||||
|
VarExpr(this.name);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr simplify() => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr evaluate() => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) => name == varName ? value : this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => name;
|
||||||
|
}
|
||||||
|
|
||||||
// === FractionExpr.evaluate ===
|
// === FractionExpr.evaluate ===
|
||||||
class FractionExpr extends Expr {
|
class FractionExpr extends Expr {
|
||||||
final int numerator;
|
final int numerator;
|
||||||
@@ -74,6 +101,9 @@ class FractionExpr extends Expr {
|
|||||||
@override
|
@override
|
||||||
Expr evaluate() => simplify();
|
Expr evaluate() => simplify();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) => this;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "$numerator/$denominator";
|
String toString() => "$numerator/$denominator";
|
||||||
}
|
}
|
||||||
@@ -150,6 +180,12 @@ class AddExpr extends Expr {
|
|||||||
return AddExpr(l, r);
|
return AddExpr(l, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) => AddExpr(
|
||||||
|
left.substitute(varName, value),
|
||||||
|
right.substitute(varName, value),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "($left + $right)";
|
String toString() => "($left + $right)";
|
||||||
}
|
}
|
||||||
@@ -213,6 +249,12 @@ class SubExpr extends Expr {
|
|||||||
return SubExpr(l, r);
|
return SubExpr(l, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) => SubExpr(
|
||||||
|
left.substitute(varName, value),
|
||||||
|
right.substitute(varName, value),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "($left - $right)";
|
String toString() => "($left - $right)";
|
||||||
}
|
}
|
||||||
@@ -296,6 +338,12 @@ class MulExpr extends Expr {
|
|||||||
return MulExpr(l, r);
|
return MulExpr(l, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) => MulExpr(
|
||||||
|
left.substitute(varName, value),
|
||||||
|
right.substitute(varName, value),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "($left * $right)";
|
String toString() => "($left * $right)";
|
||||||
}
|
}
|
||||||
@@ -378,6 +426,12 @@ class DivExpr extends Expr {
|
|||||||
return DivExpr(l, r);
|
return DivExpr(l, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) => DivExpr(
|
||||||
|
left.substitute(varName, value),
|
||||||
|
right.substitute(varName, value),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "($left / $right)";
|
String toString() => "($left / $right)";
|
||||||
}
|
}
|
||||||
@@ -429,6 +483,10 @@ class SqrtExpr extends Expr {
|
|||||||
return SqrtExpr(i);
|
return SqrtExpr(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) =>
|
||||||
|
SqrtExpr(inner.substitute(varName, value));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "sqrt($inner)";
|
String toString() => "sqrt($inner)";
|
||||||
}
|
}
|
||||||
@@ -456,6 +514,10 @@ class CosExpr extends Expr {
|
|||||||
return CosExpr(i);
|
return CosExpr(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) =>
|
||||||
|
CosExpr(inner.substitute(varName, value));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "cos($inner)";
|
String toString() => "cos($inner)";
|
||||||
}
|
}
|
||||||
@@ -483,6 +545,10 @@ class SinExpr extends Expr {
|
|||||||
return SinExpr(i);
|
return SinExpr(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) =>
|
||||||
|
SinExpr(inner.substitute(varName, value));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "sin($inner)";
|
String toString() => "sin($inner)";
|
||||||
}
|
}
|
||||||
@@ -510,6 +576,10 @@ class TanExpr extends Expr {
|
|||||||
return TanExpr(i);
|
return TanExpr(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Expr substitute(String varName, Expr value) =>
|
||||||
|
TanExpr(inner.substitute(varName, value));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => "tan($inner)";
|
String toString() => "tan($inner)";
|
||||||
}
|
}
|
||||||
|
@@ -99,6 +99,13 @@ class Parser {
|
|||||||
return TanExpr(inner);
|
return TanExpr(inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 解析变量 (单个字母)
|
||||||
|
if (RegExp(r'[a-zA-Z]').hasMatch(current)) {
|
||||||
|
var varName = current;
|
||||||
|
eat();
|
||||||
|
return VarExpr(varName);
|
||||||
|
}
|
||||||
|
|
||||||
// 解析整数
|
// 解析整数
|
||||||
var buf = '';
|
var buf = '';
|
||||||
while (!isEnd && RegExp(r'\d').hasMatch(current)) {
|
while (!isEnd && RegExp(r'\d').hasMatch(current)) {
|
||||||
|
@@ -3,7 +3,8 @@ import 'package:latext/latext.dart';
|
|||||||
import 'package:simple_math_calc/models/calculation_step.dart';
|
import 'package:simple_math_calc/models/calculation_step.dart';
|
||||||
import 'package:simple_math_calc/solver.dart';
|
import 'package:simple_math_calc/solver.dart';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:math_expressions/math_expressions.dart' as math_expressions;
|
import 'package:simple_math_calc/calculator.dart';
|
||||||
|
import 'package:simple_math_calc/parser.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
class CalculatorHomePage extends StatefulWidget {
|
class CalculatorHomePage extends StatefulWidget {
|
||||||
@@ -70,11 +71,8 @@ class _CalculatorHomePageState extends State<CalculatorHomePage> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 解析表达式
|
// 解析表达式
|
||||||
final parser = math_expressions.ShuntingYardParser();
|
final parser = Parser(functionExpr);
|
||||||
final expr = parser.parse(functionExpr);
|
final expr = parser.parse();
|
||||||
|
|
||||||
// 创建变量 x
|
|
||||||
final x = math_expressions.Variable('x');
|
|
||||||
|
|
||||||
// 根据缩放因子动态调整范围和步长
|
// 根据缩放因子动态调整范围和步长
|
||||||
final range = 10.0 * zoomFactor;
|
final range = 10.0 * zoomFactor;
|
||||||
@@ -84,13 +82,15 @@ class _CalculatorHomePageState extends State<CalculatorHomePage> {
|
|||||||
List<FlSpot> points = [];
|
List<FlSpot> points = [];
|
||||||
for (double i = -range; i <= range; i += step) {
|
for (double i = -range; i <= range; i += step) {
|
||||||
try {
|
try {
|
||||||
final context = math_expressions.ContextModel()
|
// 替换变量 x 为当前值
|
||||||
..bindVariable(x, math_expressions.Number(i));
|
final substituted = expr.substitute('x', DoubleExpr(i));
|
||||||
final evaluator = math_expressions.RealEvaluator(context);
|
final evaluated = substituted.evaluate();
|
||||||
final y = evaluator.evaluate(expr);
|
|
||||||
|
|
||||||
if (y.isFinite && !y.isNaN) {
|
if (evaluated is DoubleExpr) {
|
||||||
points.add(FlSpot(i, y.toDouble()));
|
final y = evaluated.value;
|
||||||
|
if (y.isFinite && !y.isNaN) {
|
||||||
|
points.add(FlSpot(i, y));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 跳过无法计算的点
|
// 跳过无法计算的点
|
||||||
@@ -410,12 +410,22 @@ class _CalculatorHomePageState extends State<CalculatorHomePage> {
|
|||||||
sideTitles: SideTitles(
|
sideTitles: SideTitles(
|
||||||
showTitles: true,
|
showTitles: true,
|
||||||
reservedSize: 40,
|
reservedSize: 40,
|
||||||
|
getTitlesWidget: (value, meta) =>
|
||||||
|
SideTitleWidget(
|
||||||
|
axisSide: meta.axisSide,
|
||||||
|
child: Text(value.toStringAsFixed(2)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
bottomTitles: AxisTitles(
|
bottomTitles: AxisTitles(
|
||||||
sideTitles: SideTitles(
|
sideTitles: SideTitles(
|
||||||
showTitles: true,
|
showTitles: true,
|
||||||
reservedSize: 30,
|
reservedSize: 30,
|
||||||
|
getTitlesWidget: (value, meta) =>
|
||||||
|
SideTitleWidget(
|
||||||
|
axisSide: meta.axisSide,
|
||||||
|
child: Text(value.toStringAsFixed(2)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
topTitles: AxisTitles(
|
topTitles: AxisTitles(
|
||||||
@@ -431,6 +441,19 @@ class _CalculatorHomePageState extends State<CalculatorHomePage> {
|
|||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
lineTouchData: LineTouchData(
|
||||||
|
enabled: true,
|
||||||
|
touchTooltipData: LineTouchTooltipData(
|
||||||
|
getTooltipItems: (touchedSpots) {
|
||||||
|
return touchedSpots.map((spot) {
|
||||||
|
return LineTooltipItem(
|
||||||
|
'x = ${spot.x.toStringAsFixed(2)}',
|
||||||
|
const TextStyle(color: Colors.white),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
lineBarsData: [
|
lineBarsData: [
|
||||||
LineChartBarData(
|
LineChartBarData(
|
||||||
spots: points,
|
spots: points,
|
||||||
|
@@ -400,14 +400,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.11.1"
|
||||||
math_expressions:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: math_expressions
|
|
||||||
sha256: "2e1ceb974c2b1893c809a68c7005f1b63f7324db0add800a0e792b1ac8ff9f03"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.0"
|
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@@ -34,7 +34,6 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
math_expressions: ^3.1.0
|
|
||||||
latext: ^0.5.1
|
latext: ^0.5.1
|
||||||
google_fonts: ^6.3.1
|
google_fonts: ^6.3.1
|
||||||
go_router: ^16.2.1
|
go_router: ^16.2.1
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||||
<meta name="description" content="A new Flutter project.">
|
<meta name="description" content="A simple math calculator.">
|
||||||
|
|
||||||
<!-- iOS meta tags & icons -->
|
<!-- iOS meta tags & icons -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
Reference in New Issue
Block a user