| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- // tools/vector_normal.dart
- import 'package:flutter/material.dart';
- import 'package:rational/rational.dart';
- import '../utils/rationalsqrt2.dart';
- class VectorNormalPage extends StatefulWidget {
- @override
- _VectorNormalPageState createState() => _VectorNormalPageState();
- }
- class _VectorNormalPageState extends State<VectorNormalPage> {
- final List<TextEditingController> _controllers = List.generate(9, (_) => TextEditingController());
- final List<FocusNode> _focusNodes = List.generate(9, (_) => FocusNode());
- TextEditingController? _currentFocusedController;
- FocusNode? _currentFocusedNode;
- String _result = '';
- bool _isCalculating = false;
- @override
- void initState() {
- super.initState();
- // 为每个焦点节点添加监听器
- for (int i = 0; i < _focusNodes.length; i++) {
- _focusNodes[i].addListener(() {
- if (_focusNodes[i].hasFocus) {
- _setCurrentFocusedController(_controllers[i], _focusNodes[i]);
- }
- });
- }
- }
- @override
- void dispose() {
- for (var controller in _controllers) {
- controller.dispose();
- }
- for (var focusNode in _focusNodes) {
- focusNode.dispose();
- }
- super.dispose();
- }
- // 设置当前聚焦的控制器和焦点节点
- void _setCurrentFocusedController(TextEditingController controller, FocusNode focusNode) {
- setState(() {
- _currentFocusedController = controller;
- _currentFocusedNode = focusNode;
- });
- }
- // 插入根号到当前光标位置
- void _insertSqrt() {
- if (_currentFocusedController == null || _currentFocusedNode == null) return;
-
- final controller = _currentFocusedController!;
- final focusNode = _currentFocusedNode!;
- final text = controller.text;
- final selection = controller.selection;
- final newText = text.substring(0, selection.start) + '√' + text.substring(selection.end);
- final newCursorPosition = selection.start + 1;
-
- controller.value = TextEditingValue(
- text: newText,
- selection: TextSelection.collapsed(offset: newCursorPosition),
- );
-
- // 确保焦点保持在当前输入框
- if (!focusNode.hasFocus) {
- focusNode.requestFocus();
- }
-
- // 延迟设置光标位置,避免全选
- Future.delayed(Duration(milliseconds: 10), () {
- controller.selection = TextSelection.collapsed(offset: newCursorPosition);
- });
- }
- // 计算法向量
- void _calculateNormal() {
- setState(() {
- _isCalculating = true;
- _result = '';
- });
- try {
- // 解析三个点的坐标
- List<Vector3> points = [];
- for (int i = 0; i < 3; i++) {
- final x = RationalExtend.parse(_controllers[i * 3].text.isEmpty ? '0' : _controllers[i * 3].text);
- final y = RationalExtend.parse(_controllers[i * 3 + 1].text.isEmpty ? '0' : _controllers[i * 3 + 1].text);
- final z = RationalExtend.parse(_controllers[i * 3 + 2].text.isEmpty ? '0' : _controllers[i * 3 + 2].text);
- points.add(Vector3(x, y, z));
- }
- // 计算两个向量
- final vector1 = points[1] - points[0];
- final vector2 = points[2] - points[0];
- // 计算叉积(法向量)
- final normal = vector1.cross(vector2);
- setState(() {
- _result = '法向量:(${normal.x}, ${normal.y}, ${normal.z})';
- });
- } catch (e) {
- setState(() {
- _result = '计算错误:$e';
- });
- } finally {
- setState(() {
- _isCalculating = false;
- });
- }
- }
- // 清除所有输入
- void _clearAll() {
- for (var controller in _controllers) {
- controller.clear();
- }
- setState(() {
- _result = '';
- _currentFocusedController = null;
- _currentFocusedNode = null;
- });
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('三点法向量计算'),
- backgroundColor: Colors.white,
- elevation: 1,
- ),
- body: SingleChildScrollView(
- padding: EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- _buildInputSection('点A', 0),
- SizedBox(height: 16),
- _buildInputSection('点B', 3),
- SizedBox(height: 16),
- _buildInputSection('点C', 6),
- SizedBox(height: 24),
- _buildControlButtons(),
- SizedBox(height: 24),
- _buildResultSection(),
- SizedBox(height: 24),
- _buildInstructions(),
- ],
- ),
- ),
- );
- }
- Widget _buildInputSection(String title, int startIndex) {
- return Card(
- elevation: 2,
- child: Padding(
- padding: EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- title,
- style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
- ),
- SizedBox(height: 12),
- Row(
- children: [
- _buildCoordinateInput('x', _controllers[startIndex], _focusNodes[startIndex]),
- SizedBox(width: 12),
- _buildCoordinateInput('y', _controllers[startIndex + 1], _focusNodes[startIndex + 1]),
- SizedBox(width: 12),
- _buildCoordinateInput('z', _controllers[startIndex + 2], _focusNodes[startIndex + 2]),
- ],
- ),
- ],
- ),
- ),
- );
- }
- Widget _buildCoordinateInput(String label, TextEditingController controller, FocusNode focusNode) {
- return Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- label,
- style: TextStyle(fontSize: 14, color: Colors.grey[600]),
- ),
- SizedBox(height: 4),
- TextField(
- controller: controller,
- focusNode: focusNode,
- decoration: InputDecoration(
- hintText: '0',
- border: OutlineInputBorder(),
- contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
- ),
- keyboardType: TextInputType.number,
- ),
- ],
- ),
- );
- }
- Widget _buildControlButtons() {
- return Row(
- children: [
- Expanded(
- flex: 3,
- child: ElevatedButton(
- onPressed: _isCalculating ? null : _calculateNormal,
- style: ElevatedButton.styleFrom(
- backgroundColor: Colors.blue,
- padding: EdgeInsets.symmetric(vertical: 12),
- ),
- child: _isCalculating
- ? SizedBox(
- width: 20,
- height: 20,
- child: CircularProgressIndicator(
- strokeWidth: 2,
- valueColor: AlwaysStoppedAnimation(Colors.white),
- ),
- )
- : Text(
- '计算法向量',
- style: TextStyle(fontSize: 16, color: Colors.white),
- ),
- ),
- ),
- SizedBox(width: 12),
- Expanded(
- flex: 2,
- child: OutlinedButton(
- onPressed: _clearAll,
- style: OutlinedButton.styleFrom(
- padding: EdgeInsets.symmetric(vertical: 12),
- ),
- child: Text('清除', style: TextStyle(fontSize: 16)),
- ),
- ),
- SizedBox(width: 12),
- // 使用 GestureDetector 而不是 ElevatedButton 来避免焦点转移
- GestureDetector(
- onTap: _insertSqrt,
- child: Container(
- width: 60,
- padding: EdgeInsets.symmetric(vertical: 12),
- decoration: BoxDecoration(
- color: Colors.blue,
- borderRadius: BorderRadius.circular(4),
- ),
- child: Text(
- '√',
- style: TextStyle(fontSize: 18, color: Colors.white),
- textAlign: TextAlign.center,
- ),
- ),
- ),
- ],
- );
- }
- Widget _buildResultSection() {
- if (_result.isEmpty) return SizedBox();
- return Card(
- color: Colors.grey[50],
- elevation: 2,
- child: Padding(
- padding: EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- '计算结果',
- style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
- ),
- SizedBox(height: 12),
- Text(
- _result,
- style: TextStyle(fontSize: 16, color: Colors.blue[700]),
- ),
- SizedBox(height: 8),
- Text(
- '说明:法向量是通过向量AB和向量AC的叉积计算得到',
- style: TextStyle(fontSize: 12, color: Colors.grey[600]),
- ),
- ],
- ),
- ),
- );
- }
- Widget _buildInstructions() {
- return Card(
- color: Colors.grey[50],
- elevation: 2,
- child: Padding(
- padding: EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- '使用说明',
- style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
- ),
- SizedBox(height: 12),
- Text(
- '√2/2请输入为1/2√2\n'
- '如果已知两向量坐标,将一个点设为原点即可',
- style: TextStyle(fontSize: 14, color: Colors.grey[700]),
- ),
- ],
- ),
- ),
- );
- }
- }
- // 三维向量类,使用RationalExtend进行计算
- class Vector3 {
- final RationalExtend x, y, z;
- Vector3(this.x, this.y, this.z);
- // 向量减法
- Vector3 operator -(Vector3 other) {
- return Vector3(x - other.x, y - other.y, z - other.z);
- }
- // 向量叉积
- Vector3 cross(Vector3 other) {
- final newX = y * other.z - z * other.y;
- final newY = z * other.x - x * other.z;
- final newZ = x * other.y - y * other.x;
- return Vector3(newX, newY, newZ);
- }
- @override
- String toString() {
- return '($x, $y, $z)';
- }
- }
|