| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- // tools/circle_equation.dart
- import 'package:flutter/material.dart';
- import 'tool_kit.dart';
- /// 圆的方程 —— 过三点的圆。
- ///
- /// 设圆为 x² + y² + Dx + Ey + F = 0,把三个点代进去解 D、E、F。
- /// 未知量在 Symja 侧用 dd/ee/ff 命名(D、E 是 Symja 的内置名)。
- /// 三点共线时 Symja 的 Solve 返回空集,我们的表达式直接回一句中文说明。
- class CircleEquationPage extends StatefulWidget {
- const CircleEquationPage({super.key});
- @override
- State<CircleEquationPage> createState() => _CircleEquationPageState();
- }
- class _CircleEquationPageState extends State<CircleEquationPage> {
- final _x1 = ToolField('点1 x', hint: '分数如 1/2,或小数');
- // 点的横纵坐标是一对,并排一行
- final _y1 = ToolField('点1 y', hint: '分数如 1/2,或小数', rowWithPrevious: true);
- final _x2 = ToolField('点2 x', hint: '分数如 1/2,或小数');
- // 点的横纵坐标是一对,并排一行
- final _y2 = ToolField('点2 y', hint: '分数如 1/2,或小数', rowWithPrevious: true);
- final _x3 = ToolField('点3 x', hint: '分数如 1/2,或小数');
- // 点的横纵坐标是一对,并排一行
- final _y3 = ToolField('点3 y', hint: '分数如 1/2,或小数', rowWithPrevious: true);
- @override
- Widget build(BuildContext context) => ToolScaffold(
- title: '圆的方程',
- fields: [_x1, _y1, _x2, _y2, _x3, _y3],
- compute: (session) async {
- final bind =
- 'x1=${_need(_x1)},y1=${_need(_y1)},'
- 'x2=${_need(_x2)},y2=${_need(_y2)},'
- 'x3=${_need(_x3)},y3=${_need(_y3)}';
- final eqs =
- '{x1^2+y1^2+dd*x1+ee*y1+ff==0, '
- 'x2^2+y2^2+dd*x2+ee*y2+ff==0, '
- 'x3^2+y3^2+dd*x3+ee*y3+ff==0}';
- const noCircle = '"三点共线(没有外接圆)"';
- // 三个系数
- final defExpr =
- 'With[{$bind}, Module[{s}, s=Solve[$eqs,{dd,ee,ff}]; '
- 'If[s==={}, $noCircle, {dd,ee,ff} /. First[s]]]]';
- final defs = parseSymjaList((await session.compute(defExpr)).text);
- if (defs.length != 3) {
- // 共线:Symja 返回的就是那句中文,直接显示。
- return [ToolText('结果', '${defs.isEmpty ? '' : defs.first}')];
- }
- // 圆心 (-dd/2, -ee/2) 与半径 √(dd²+ee²-4ff)/2
- final centerExpr =
- 'With[{$bind}, Module[{s}, s=Solve[$eqs,{dd,ee,ff}]; '
- 'If[s==={}, $noCircle, '
- '{-dd/2, -ee/2, Sqrt[dd^2+ee^2-4*ff]/2} /. First[s]]]]';
- // 精确值 + 近似值都要:系数和圆心半径都是分数,小数形式有时更直观
- final defsN = parseSymjaList(
- (await session.compute(
- approxExpr('Rationalize[$defExpr]', session.approximateDigits),
- )).text,
- );
- final center = parseSymjaList((await session.compute(centerExpr)).text);
- final centerN = parseSymjaList(
- (await session.compute(
- approxExpr('Rationalize[$centerExpr]', session.approximateDigits),
- )).text,
- );
- String at(List<Object> l, int i) => i < l.length ? '${l[i]}' : '';
- // 一般方程 x² + y² + Dx + Ey + F = 0(系数代进去,渲染成数学排版)。
- // 放在最前面 —— 它才是"结果",D/E/F 是它的系数。
- final general = polyEqualsZero([
- ('1', 'x^{2}', 'x^2'),
- ('1', 'y^{2}', 'y^2'),
- ('${defs[0]}', 'x', 'x'),
- ('${defs[1]}', 'y', 'y'),
- ('${defs[2]}', '', ''),
- ]);
- final out = <ToolOutput>[
- ToolText('圆的一般方程', general.plain, tex: general.tex),
- ToolText('D', withApprox('${defs[0]}', at(defsN, 0))),
- ToolText('E', withApprox('${defs[1]}', at(defsN, 1))),
- ToolText('F', withApprox('${defs[2]}', at(defsN, 2))),
- ];
- if (center.length == 3) {
- // 圆心是坐标,两个分量各自配自己的近似值
- out.add(
- ToolText(
- '圆心',
- '(${withApprox('${center[0]}', at(centerN, 0))}, '
- '${withApprox('${center[1]}', at(centerN, 1))})',
- ),
- );
- out.add(ToolText('半径', withApprox('${center[2]}', at(centerN, 2))));
- }
- return out;
- },
- );
- /// 取一个标量输入框:空着或填了多个值都直接报中文错。
- String _need(ToolField f) {
- if (f.count == 0) throw Exception('请填写「${f.label}」');
- if (f.count > 1) throw Exception('「${f.label}」只能填一个数');
- return f.text;
- }
- }
|