欢迎光临
我们一直在努力

Flutter for Harmony 跨平台开发实战:反应扩散系统——图灵斑图的动态生成

在这里插入图片描述

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net


🎨 一、反应扩散系统:数学之美

📚 1.1 图灵斑图的发现

1952年,数学家艾伦·图灵发表了一篇开创性论文《形态发生的化学基础》,提出了反应扩散系统的概念,解释了自然界中斑纹图案的形成机制。

图灵的核心洞察:

两种化学物质(形态因子)在组织中扩散并相互作用:
– 一种促进某种结构形成(激活子 Activator)
– 一种抑制该结构的扩展(抑制子 Inhibitor)

当扩散速率不同时,均匀分布会变得不稳定
最终形成稳定的空间图案——图灵斑图

历史意义:

1952年:图灵发表论文,奠定理论基础
1972年:Gierer 和 Meinhardt 提出具体模型
1980年代:Gray-Scott 模型成为经典
1990年代:计算机模拟验证理论
2012年:实验首次在化学反应中观察到图灵斑图

图灵斑图示意:

均匀初始状态 扰动后 最终稳定图案
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ · · · · · · │ │ · · ◉ · · · │ │ ○ ○ ● ● ○ ○ │
│ · · · · · · │ │ · · · · ◉ · │ │ ● ● ● ● ● ● │
│ · · · · · · │ → │ · ◉ · · · · │ → │ ○ ● ● ● ● ○ │
│ · · · · · · │ │ · · · · · · │ │ ○ ○ ● ● ○ ○ │
│ · · · · · · │ │ · · · ◉ · · │ │ ○ ○ ○ ○ ○ ○ │
└─────────────┘ └─────────────┘ └─────────────┘


📐 1.2 反应扩散方程

反应扩散系统的数学模型由偏微分方程描述:

Gray-Scott 模型:

∂u/∂t = Du∇²u – uv² + f(1 – u)
∂v/∂t = Dv∇²v + uv² – (f + k)v

其中:
– u:激活子浓度
– v:抑制子浓度
– Du、Dv:扩散系数
– f:供给速率(Feed rate)
– k:衰减速率(Kill rate)
– ∇²:拉普拉斯算子(扩散项)

参数含义详解:

参数作用典型值范围影响
Du 激活子扩散率 0.14 – 0.20 控制激活子扩散速度
Dv 抑制子扩散率 0.05 – 0.08 控制抑制子扩散速度
f 供给率 0.01 – 0.07 新激活子的补充速度
k 衰减率 0.03 – 0.07 抑制子的消耗速度

拉普拉斯算子离散化:

二维网格上的五点差分格式:

∇²u ≈ (u[i+1,j] + u[i-1,j] + u[i,j+1] + u[i,j-1] – 4u[i,j]) / h²

u[i-1,j]

u[i,j-1] ← u[i,j] → u[i,j+1]

u[i+1,j]

h 为网格间距,通常取 1


🔬 1.3 斑图类型与参数空间

不同的参数组合会产生不同类型的斑图:

斑图类型分类:

斑图类型参数特征自然界示例视觉特点
🐆斑点图案 f 较大,k 较小 豹纹、长颈鹿 离散圆形斑点
🦓条纹图案 f 中等,k 中等 斑马、虎纹 平行条纹
🌊迷宫图案 f 较小,k 较大 珊瑚、脑珊瑚 连通迷宫状
🌿分叉图案 f 很小 树枝分叉 分形分支
🎯点阵图案 特定参数组合 热带鱼 规则点阵
🌀波纹图案 f 很小,k 特定 化学波 脉冲波纹

斑图相图示意:

k (衰减率)

0.07 │ 迷宫 │ 条纹 │ 斑点
│ 🌀 │ ═══ │ ●●●
0.05 │────────┼────────┼────────
│ 分叉 │ 混合 │ 规则
│ ⑂ │ ●═● │ ●●●
0.03 │────────┼────────┼────────
│ 死亡 │ 波纹 │ 脉冲
│ ○ │ 〰 │ ◉◉◉
└────────┴────────┴────────→ f (供给率)
0.02 0.04 0.06

"死亡"区域:图案消失,趋于均匀
"脉冲"区域:动态振荡图案


🎯 1.4 自然界中的图灵斑图

生物斑图形成机制:

生物斑图类型形成机制发育阶段
🐆 豹 斑点 激活子扩散慢,抑制子扩散快 胚胎期皮肤
🦓 斑马 条纹 扩散各向异性 胚胎期皮肤
🐠 热带鱼 点阵 多种形态因子相互作用 幼鱼期
🦎 蜥蜴 格子 皮肤拉伸影响 发育过程
🐚 贝壳 条纹/斑点 色素细胞分布 生长边缘

斑马条纹形成示意:

胚胎期(均匀) 发育中(条纹出现) 成熟期(清晰条纹)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ │ │ ═════════ │ │ ═════════ │
│ │ │ ═════════ │ │ ═════════ │
│ │ → │ ═════════ │ → │ ═════════ │
│ │ │ ═════════ │ │ ═════════ │
│ │ │ ═════════ │ │ ═════════ │
└─────────────┘ └─────────────┘ └─────────────┘

条纹宽度由胚胎大小决定
条纹方向由扩散各向异性决定

其他自然现象:

沙丘波纹:风沙运动的反应扩散
珊瑚纹理:钙化过程的形态发生
蝴蝶翅膀:色素细胞的空间分布
哺乳动物毛色:毛囊中的色素合成


🔧 二、反应扩散系统的 Dart 实现

🧮 2.1 核心数据结构

import 'dart:math';
import 'dart:typed_data';

/// 反应扩散网格
class ReactionDiffusionGrid {
final int width;
final int height;

late Float32List _u;
late Float32List _v;
late Float32List _uNext;
late Float32List _vNext;

double du = 0.16;
double dv = 0.08;
double feed = 0.055;
double kill = 0.062;
double dt = 1.0;

ReactionDiffusionGrid({
required this.width,
required this.height,
}) {
_u = Float32List(width * height);
_v = Float32List(width * height);
_uNext = Float32List(width * height);
_vNext = Float32List(width * height);

initialize();
}

/// 初始化网格
void initialize() {
for (int i = 0; i < _u.length; i++) {
_u[i] = 1.0;
_v[i] = 0.0;
}
}

/// 添加扰动(种子)
void addSeed(int cx, int cy, int radius) {
for (int y = radius; y <= radius; y++) {
for (int x = radius; x <= radius; x++) {
if (x * x + y * y <= radius * radius) {
final px = (cx + x + width) % width;
final py = (cy + y + height) % height;
final idx = py * width + px;

_u[idx] = 0.5;
_v[idx] = 0.25;
}
}
}
}

/// 添加随机种子
void addRandomSeeds(int count, int radius) {
final random = Random();
for (int i = 0; i < count; i++) {
addSeed(random.nextInt(width), random.nextInt(height), radius);
}
}

/// 添加线性种子(用于条纹)
void addLinearSeeds() {
final random = Random();
for (int i = 0; i < 5; i++) {
final y = random.nextInt(height);
for (int x = 0; x < width; x++) {
if (random.nextDouble() < 0.3) {
final idx = y * width + x;
_u[idx] = 0.5;
_v[idx] = 0.25;
}
}
}
}

/// 获取值
double getU(int x, int y) => _u[(y % height) * width + (x % width)];
double getV(int x, int y) => _v[(y % height) * width + (x % width)];

/// 设置值
void setU(int x, int y, double value) {
_u[(y % height) * width + (x % width)] = value.clamp(0.0, 1.0);
}

void setV(int x, int y, double value) {
_v[(y % height) * width + (x % width)] = value.clamp(0.0, 1.0);
}

/// 计算拉普拉斯算子(周期边界)
double _laplacian(Float32List grid, int x, int y) {
final left = grid[y * width + ((x 1 + width) % width)];
final right = grid[y * width + ((x + 1) % width)];
final up = grid[((y 1 + height) % height) * width + x];
final down = grid[((y + 1) % height) * width + x];

return left + right + up + down 4 * grid[y * width + x];
}

/// 单步更新(Gray-Scott 模型)
void step() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
final idx = y * width + x;
final u = _u[idx];
final v = _v[idx];

final lapU = _laplacian(_u, x, y);
final lapV = _laplacian(_v, x, y);

final uvv = u * v * v;

_uNext[idx] = u + dt * (du * lapU uvv + feed * (1 u));
_vNext[idx] = v + dt * (dv * lapV + uvv (feed + kill) * v);

_uNext[idx] = _uNext[idx].clamp(0.0, 1.0);
_vNext[idx] = _vNext[idx].clamp(0.0, 1.0);
}
}

// 交换缓冲区
final tempU = _u;
final tempV = _v;
_u = _uNext;
_v = _vNext;
_uNext = tempU;
_vNext = tempV;
}

/// 多步更新
void update(int steps) {
for (int i = 0; i < steps; i++) {
step();
}
}

/// 获取浓度数据
Float32List get u => _u;
Float32List get v => _v;

/// 重置
void reset() {
initialize();
}

/// 设置参数预设
void setPreset(ReactionDiffusionPreset preset) {
du = preset.du;
dv = preset.dv;
feed = preset.feed;
kill = preset.kill;
}
}

/// 反应扩散参数预设
class ReactionDiffusionPreset {
final String name;
final double du;
final double dv;
final double feed;
final double kill;

const ReactionDiffusionPreset({
required this.name,
required this.du,
required this.dv,
required this.feed,
required this.kill,
});

static const List<ReactionDiffusionPreset> presets = [
ReactionDiffusionPreset(
name: '斑点',
du: 0.16,
dv: 0.08,
feed: 0.035,
kill: 0.065,
),
ReactionDiffusionPreset(
name: '条纹',
du: 0.16,
dv: 0.08,
feed: 0.035,
kill: 0.060,
),
ReactionDiffusionPreset(
name: '迷宫',
du: 0.16,
dv: 0.08,
feed: 0.029,
kill: 0.057,
),
ReactionDiffusionPreset(
name: '珊瑚',
du: 0.16,
dv: 0.08,
feed: 0.0545,
kill: 0.062,
),
ReactionDiffusionPreset(
name: '波纹',
du: 0.16,
dv: 0.08,
feed: 0.014,
kill: 0.054,
),
ReactionDiffusionPreset(
name: '分裂',
du: 0.16,
dv: 0.08,
feed: 0.039,
kill: 0.058,
),
ReactionDiffusionPreset(
name: '脉冲',
du: 0.16,
dv: 0.08,
feed: 0.025,
kill: 0.060,
),
ReactionDiffusionPreset(
name: '虫洞',
du: 0.16,
dv: 0.08,
feed: 0.078,
kill: 0.061,
),
];
}


⚡ 2.2 高性能渲染器

import 'package:flutter/material.dart';

/// 反应扩散渲染器
class ReactionDiffusionRenderer {
final ReactionDiffusionGrid grid;

late Uint8List _pixels;
late List<Color> _colorMap;

ReactionDiffusionRenderer(this.grid) {
_pixels = Uint8List(grid.width * grid.height * 4);
_buildColorMap();
}

/// 构建颜色映射表
void _buildColorMap() {
_colorMap = List.generate(256, (i) {
final t = i / 255.0;

if (t < 0.5) {
final s = t * 2;
return Color.lerp(
const Color(0xFF0a1628),
const Color(0xFF1e90ff),
s,
)!;
} else {
final s = (t 0.5) * 2;
return Color.lerp(
const Color(0xFF1e90ff),
const Color(0xFFffffff),
s,
)!;
}
});
}

/// 设置颜色方案
void setColorScheme(List<Color> colors) {
_colorMap = [];
for (int i = 0; i < 256; i++) {
final t = i / 255.0;
final idx = t * (colors.length 1);
final lower = idx.floor();
final upper = (lower + 1).clamp(0, colors.length 1);
final frac = idx lower;

_colorMap.add(Color.lerp(colors[lower], colors[upper], frac)!);
}
}

/// 设置热力图颜色方案
void setHeatmapScheme() {
_colorMap = List.generate(256, (i) {
final t = i / 255.0;

if (t < 0.25) {
return Color.lerp(const Color(0xFF000033), const Color(0xFF0000ff), t * 4)!;
} else if (t < 0.5) {
return Color.lerp(const Color(0xFF0000ff), const Color(0xFF00ffff), (t 0.25) * 4)!;
} else if (t < 0.75) {
return Color.lerp(const Color(0xFF00ffff), const Color(0xFFFFFF00), (t 0.5) * 4)!;
} else {
return Color.lerp(const Color(0xFFFFFF00), const Color(0xFFFF0000), (t 0.75) * 4)!;
}
});
}

/// 渲染到像素缓冲区
Uint8List render() {
final u = grid.u;
final v = grid.v;

for (int i = 0; i < u.length; i++) {
final value = (u[i] v[i]).clamp(0.0, 1.0);
final colorIdx = (value * 255).round();
final color = _colorMap[colorIdx];

final pixelIdx = i * 4;
_pixels[pixelIdx] = color.red;
_pixels[pixelIdx + 1] = color.green;
_pixels[pixelIdx + 2] = color.blue;
_pixels[pixelIdx + 3] = 255;
}

return _pixels;
}

/// 渲染带动态颜色
Uint8List renderWithTime(double time) {
final u = grid.u;
final v = grid.v;

for (int i = 0; i < u.length; i++) {
final value = (u[i] v[i]).clamp(0.0, 1.0);
final hue = (200 + value * 80 + time * 20) % 360;
final color = HSVColor.fromAHSV(1, hue, 0.7, value * 0.9 + 0.1).toColor();

final pixelIdx = i * 4;
_pixels[pixelIdx] = color.red;
_pixels[pixelIdx + 1] = color.green;
_pixels[pixelIdx + 2] = color.blue;
_pixels[pixelIdx + 3] = 255;
}

return _pixels;
}

/// 获取像素数据
Uint8List get pixels => _pixels;
}

/// 斑图类型检测器
class PatternDetector {
final ReactionDiffusionGrid grid;

PatternDetector(this.grid);

/// 计算平均浓度
double averageConcentration() {
final u = grid.u;
var sum = 0.0;
for (final value in u) {
sum += value;
}
return sum / u.length;
}

/// 计算方差
double variance() {
final avg = averageConcentration();
final u = grid.u;
var sum = 0.0;
for (final value in u) {
sum += (value avg) * (value avg);
}
return sum / u.length;
}

/// 检测图案类型
String detectPatternType() {
final var = variance();

if (var < 0.01) return '均匀';
if (var < 0.05) return '弱图案';
if (var < 0.1) return '中等图案';
return '强图案';
}

/// 计算图案密度
double patternDensity() {
final u = grid.u;
final v = grid.v;
var count = 0;

for (int i = 0; i < u.length; i++) {
if (u[i] v[i] > 0.5) count++;
}

return count / u.length;
}
}


🎨 2.3 交互式控制器

import 'package:flutter/material.dart';

/// 反应扩散控制器
class ReactionDiffusionController extends ChangeNotifier {
late ReactionDiffusionGrid _grid;
late ReactionDiffusionRenderer _renderer;

bool _isRunning = false;
int _stepsPerFrame = 10;
int _totalSteps = 0;
int _selectedPreset = 0;
double _time = 0;

ReactionDiffusionController({
required int width,
required int height,
}) {
_grid = ReactionDiffusionGrid(width: width, height: height);
_renderer = ReactionDiffusionRenderer(_grid);

_grid.setPreset(ReactionDiffusionPreset.presets[0]);
_grid.addRandomSeeds(10, 5);
}

ReactionDiffusionGrid get grid => _grid;
ReactionDiffusionRenderer get renderer => _renderer;
bool get isRunning => _isRunning;
int get stepsPerFrame => _stepsPerFrame;
int get totalSteps => _totalSteps;
int get selectedPreset => _selectedPreset;
double get time => _time;

/// 更新
void update() {
if (_isRunning) {
_grid.update(_stepsPerFrame);
_totalSteps += _stepsPerFrame;
_time += 0.016;
notifyListeners();
}
}

/// 开始/暂停
void toggleRunning() {
_isRunning = !_isRunning;
notifyListeners();
}

/// 设置每帧步数
void setStepsPerFrame(int steps) {
_stepsPerFrame = steps.clamp(1, 50);
notifyListeners();
}

/// 选择预设
void selectPreset(int index) {
if (index >= 0 && index < ReactionDiffusionPreset.presets.length) {
_selectedPreset = index;
_grid.setPreset(ReactionDiffusionPreset.presets[index]);
notifyListeners();
}
}

/// 添加种子
void addSeedAt(int x, int y, {int radius = 5}) {
_grid.addSeed(x, y, radius);
notifyListeners();
}

/// 重置
void reset() {
_grid.reset();
_grid.addRandomSeeds(10, 5);
_totalSteps = 0;
_time = 0;
notifyListeners();
}

/// 设置参数
void setParameters({
double? du,
double? dv,
double? feed,
double? kill,
}) {
if (du != null) _grid.du = du;
if (dv != null) _grid.dv = dv;
if (feed != null) _grid.feed = feed;
if (kill != null) _grid.kill = kill;
notifyListeners();
}

/// 渲染
Uint8List render() {
return _renderer.renderWithTime(_time);
}
}


📦 三、完整示例代码

以下是完整的反应扩散系统可视化示例代码:

import 'package:flutter/material.dart';
import 'dart:math';
import 'dart:typed_data';

void main() {
runApp(const ReactionDiffusionApp());
}

class ReactionDiffusionApp extends StatelessWidget {
const ReactionDiffusionApp({super.key});


Widget build(BuildContext context) {
return MaterialApp(
title: '反应扩散系统',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, brightness: Brightness.dark),
useMaterial3: true,
),
home: const ReactionDiffusionHomePage(),
debugShowCheckedModeBanner: false,
);
}
}

class ReactionDiffusionHomePage extends StatelessWidget {
const ReactionDiffusionHomePage({super.key});


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('🎨 反应扩散系统'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildCard(
context,
title: '图灵斑图',
description: '经典 Gray-Scott 模型',
icon: Icons.blur_on,
color: Colors.blue,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const TuringPatternDemo()),
),
),
_buildCard(
context,
title: '斑图画廊',
description: '多种参数预设对比',
icon: Icons.grid_view,
color: Colors.purple,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const PatternGalleryDemo()),
),
),
_buildCard(
context,
title: '交互绘制',
description: '手动添加种子',
icon: Icons.draw,
color: Colors.orange,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const InteractivePatternDemo()),
),
),
_buildCard(
context,
title: '参数探索',
description: '实时调节参数',
icon: Icons.tune,
color: Colors.teal,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const ParameterExplorerDemo()),
),
),
],
),
);
}

Widget _buildCard(
BuildContext context, {
required String title,
required String description,
required IconData icon,
required Color color,
required VoidCallback onTap,
}) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: color, size: 28),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(color: Colors.grey[600], fontSize: 14),
),
],
),
),
Icon(Icons.chevron_right, color: Colors.grey[400]),
],
),
),
),
);
}
}

/// 反应扩散网格(简化版)
class RDGrid {
final int width, height;
late Float32List _u, _v, _uNext, _vNext;
double du = 0.16, dv = 0.08, feed = 0.055, kill = 0.062;

RDGrid({required this.width, required this.height}) {
_u = Float32List(width * height);
_v = Float32List(width * height);
_uNext = Float32List(width * height);
_vNext = Float32List(width * height);
for (int i = 0; i < _u.length; i++) { _u[i] = 1.0; _v[i] = 0.0; }
}

void addSeed(int cx, int cy, int r) {
for (int y = r; y <= r; y++) {
for (int x = r; x <= r; x++) {
if (x*x + y*y <= r*r) {
final idx = ((cy + y + height) % height) * width + ((cx + x + width) % width);
_u[idx] = 0.5; _v[idx] = 0.25;
}
}
}
}

void addRandomSeeds(int count, int radius) {
final rand = Random();
for (int i = 0; i < count; i++) addSeed(rand.nextInt(width), rand.nextInt(height), radius);
}

double _lap(Float32List g, int x, int y) {
return g[y * width + ((x1+width)%width)] + g[y * width + ((x+1)%width)] +
g[((y1+height)%height) * width + x] + g[((y+1)%height) * width + x] 4 * g[y * width + x];
}

void step() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
final i = y * width + x;
final u = _u[i], v = _v[i];
final uvv = u * v * v;
_uNext[i] = (u + du * _lap(_u, x, y) uvv + feed * (1 u)).clamp(0.0, 1.0);
_vNext[i] = (v + dv * _lap(_v, x, y) + uvv (feed + kill) * v).clamp(0.0, 1.0);
}
}
final t = _u; _u = _uNext; _uNext = t;
final s = _v; _v = _vNext; _vNext = s;
}

void update(int n) { for (int i = 0; i < n; i++) step(); }
Float32List get u => _u;
Float32List get v => _v;
void reset() { for (int i = 0; i < _u.length; i++) { _u[i] = 1.0; _v[i] = 0.0; } }
}

/// 参数预设
class Preset {
final String name;
final double feed, kill;
const Preset(this.name, this.feed, this.kill);
static const presets = [
Preset('斑点', 0.035, 0.065),
Preset('条纹', 0.035, 0.060),
Preset('迷宫', 0.029, 0.057),
Preset('珊瑚', 0.0545, 0.062),
Preset('分裂', 0.014, 0.054),
Preset('波纹', 0.039, 0.058),
];
}

/// 图灵斑图演示
class TuringPatternDemo extends StatefulWidget {
const TuringPatternDemo({super.key});

State<TuringPatternDemo> createState() => _TuringPatternDemoState();
}

class _TuringPatternDemoState extends State<TuringPatternDemo> with SingleTickerProviderStateMixin {
late AnimationController _ctrl;
late RDGrid _grid;
late Uint8List _pixels;
bool _running = true;
int _steps = 10, _total = 0, _preset = 0;
double _time = 0;


void initState() {
super.initState();
_grid = RDGrid(width: 200, height: 200);
_pixels = Uint8List(200 * 200 * 4);
_applyPreset(0);
_ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
_ctrl.addListener(_update);
}

void _update() {
if (_running) {
_grid.update(_steps);
_total += _steps;
_time += 0.016;
_render();
setState(() {});
}
}

void _applyPreset(int i) {
final p = Preset.presets[i];
_grid.feed = p.feed;
_grid.kill = p.kill;
_preset = i;
}

void _render() {
final u = _grid.u, v = _grid.v;
for (int i = 0; i < u.length; i++) {
final val = (u[i] v[i]).clamp(0.0, 1.0);
final hue = (200 + val * 80 + _time * 10) % 360;
final c = HSVColor.fromAHSV(1, hue, 0.7, val * 0.9 + 0.1).toColor();
final p = i * 4;
_pixels[p] = c.red; _pixels[p+1] = c.green; _pixels[p+2] = c.blue; _pixels[p+3] = 255;
}
}


void dispose() { _ctrl.removeListener(_update); _ctrl.dispose(); super.dispose(); }


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('图灵斑图')),
body: Column(children: [
Expanded(child: Center(child: CustomPaint(painter: RDPainter(_pixels, 200, 200), size: const Size(200, 200)))),
_buildControls(),
]),
);
}

Widget _buildControls() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(color: Colors.grey[900], borderRadius: const BorderRadius.vertical(top: Radius.circular(20))),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Text('步数: $_total', style: const TextStyle(color: Colors.white70)),
const SizedBox(height: 8),
Wrap(spacing: 8, children: List.generate(Preset.presets.length, (i) => ChoiceChip(
label: Text(Preset.presets[i].name),
selected: _preset == i,
onSelected: (s) { if (s) { _applyPreset(i); _grid.reset(); _grid.addRandomSeeds(10, 5); _total = 0; setState(() {}); } },
))),
const SizedBox(height: 8),
Row(children: [
const Text('速度: ', style: TextStyle(color: Colors.white70)),
Expanded(child: Slider(value: _steps.toDouble(), min: 1, max: 30, divisions: 29, onChanged: (v) => setState(() => _steps = v.toInt()))),
Text('$_steps', style: const TextStyle(color: Colors.blue)),
]),
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
ElevatedButton(onPressed: () => setState(() => _running = !_running), child: Text(_running ? '暂停' : '继续')),
ElevatedButton(onPressed: () { _grid.reset(); _grid.addRandomSeeds(10, 5); _total = 0; setState(() {}); }, child: const Text('重置')),
]),
]),
);
}
}

class RDPainter extends CustomPainter {
final Uint8List pixels;
final int w, h;
RDPainter(this.pixels, this.w, this.h);

void paint(Canvas canvas, Size size) {
final paint = Paint();
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
final i = (y * w + x) * 4;
paint.color = Color.fromARGB(pixels[i+3], pixels[i], pixels[i+1], pixels[i+2]);
canvas.drawRect(Rect.fromLTWH(x.toDouble(), y.toDouble(), 1, 1), paint);
}
}
}

bool shouldRepaint(covariant RDPainter old) => true;
}

/// 斑图画廊演示
class PatternGalleryDemo extends StatefulWidget {
const PatternGalleryDemo({super.key});

State<PatternGalleryDemo> createState() => _PatternGalleryDemoState();
}

class _PatternGalleryDemoState extends State<PatternGalleryDemo> with TickerProviderStateMixin {
final List<RDGrid> _grids = [];
final List<Uint8List> _pixels = [];
late AnimationController _ctrl;
double _time = 0;


void initState() {
super.initState();
for (int i = 0; i < Preset.presets.length; i++) {
final p = Preset.presets[i];
final g = RDGrid(width: 100, height: 100);
g.feed = p.feed; g.kill = p.kill;
g.addRandomSeeds(5, 3);
_grids.add(g);
_pixels.add(Uint8List(100 * 100 * 4));
}
_ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
_ctrl.addListener(_update);
}

void _update() {
_time += 0.016;
for (int i = 0; i < _grids.length; i++) {
_grids[i].update(5);
_renderIdx(i);
}
setState(() {});
}

void _renderIdx(int idx) {
final g = _grids[idx], u = g.u, v = g.v, p = _pixels[idx];
for (int i = 0; i < u.length; i++) {
final val = (u[i] v[i]).clamp(0.0, 1.0);
final hue = (idx * 60 + val * 120) % 360;
final c = HSVColor.fromAHSV(1, hue, 0.8, val * 0.8 + 0.2).toColor();
final pi = i * 4;
p[pi] = c.red; p[pi+1] = c.green; p[pi+2] = c.blue; p[pi+3] = 255;
}
}


void dispose() { _ctrl.removeListener(_update); _ctrl.dispose(); super.dispose(); }


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('斑图画廊')),
body: GridView.builder(
padding: const EdgeInsets.all(8),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 8, mainAxisSpacing: 8),
itemCount: Preset.presets.length,
itemBuilder: (ctx, i) => Column(children: [
Expanded(child: CustomPaint(painter: RDPainter(_pixels[i], 100, 100), size: Size.infinite)),
Padding(padding: const EdgeInsets.all(8), child: Text(Preset.presets[i].name, style: const TextStyle(fontWeight: FontWeight.bold))),
]),
),
);
}
}

/// 交互绘制演示
class InteractivePatternDemo extends StatefulWidget {
const InteractivePatternDemo({super.key});

State<InteractivePatternDemo> createState() => _InteractivePatternDemoState();
}

class _InteractivePatternDemoState extends State<InteractivePatternDemo> with SingleTickerProviderStateMixin {
late AnimationController _ctrl;
late RDGrid _grid;
late Uint8List _pixels;
bool _running = true;
int _brush = 5;
double _time = 0;


void initState() {
super.initState();
_grid = RDGrid(width: 200, height: 200);
_pixels = Uint8List(200 * 200 * 4);
_grid.feed = 0.0545; _grid.kill = 0.062;
_ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
_ctrl.addListener(_update);
}

void _update() {
if (_running) {
_grid.update(10);
_time += 0.016;
_render();
setState(() {});
}
}

void _render() {
final u = _grid.u, v = _grid.v;
for (int i = 0; i < u.length; i++) {
final val = (u[i] v[i]).clamp(0.0, 1.0);
final c = HSVColor.fromAHSV(1, 200 + val * 80, 0.7, val * 0.9 + 0.1).toColor();
final p = i * 4;
_pixels[p] = c.red; _pixels[p+1] = c.green; _pixels[p+2] = c.blue; _pixels[p+3] = 255;
}
}


void dispose() { _ctrl.removeListener(_update); _ctrl.dispose(); super.dispose(); }


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('交互绘制')),
body: Column(children: [
Expanded(child: GestureDetector(
onPanStart: (d) => _addSeed(d.localPosition),
onPanUpdate: (d) => _addSeed(d.localPosition),
child: Center(child: CustomPaint(painter: RDPainter(_pixels, 200, 200), size: const Size(200, 200))),
)),
_buildControls(),
]),
);
}

void _addSeed(Offset pos) => _grid.addSeed(pos.dx.toInt(), pos.dy.toInt(), _brush);

Widget _buildControls() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(color: Colors.grey[900], borderRadius: const BorderRadius.vertical(top: Radius.circular(20))),
child: Column(mainAxisSize: MainAxisSize.min, children: [
const Text('触摸屏幕添加种子', style: TextStyle(color: Colors.white70)),
const SizedBox(height: 8),
Row(children: [
const Text('笔刷: ', style: TextStyle(color: Colors.white70)),
Expanded(child: Slider(value: _brush.toDouble(), min: 2, max: 20, divisions: 18, onChanged: (v) => setState(() => _brush = v.toInt()))),
Text('$_brush', style: const TextStyle(color: Colors.orange)),
]),
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
ElevatedButton(onPressed: () => setState(() => _running = !_running), child: Text(_running ? '暂停' : '继续')),
ElevatedButton(onPressed: () { _grid.reset(); setState(() {}); }, child: const Text('清空')),
]),
]),
);
}
}

/// 参数探索演示
class ParameterExplorerDemo extends StatefulWidget {
const ParameterExplorerDemo({super.key});

State<ParameterExplorerDemo> createState() => _ParameterExplorerDemoState();
}

class _ParameterExplorerDemoState extends State<ParameterExplorerDemo> with SingleTickerProviderStateMixin {
late AnimationController _ctrl;
late RDGrid _grid;
late Uint8List _pixels;
double _feed = 0.055, _kill = 0.062, _du = 0.16, _dv = 0.08, _time = 0;


void initState() {
super.initState();
_grid = RDGrid(width: 150, height: 150);
_pixels = Uint8List(150 * 150 * 4);
_grid.addRandomSeeds(8, 4);
_ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
_ctrl.addListener(_update);
}

void _update() {
_grid.update(8);
_time += 0.016;
_render();
setState(() {});
}

void _render() {
final u = _grid.u, v = _grid.v;
for (int i = 0; i < u.length; i++) {
final val = (u[i] v[i]).clamp(0.0, 1.0);
final c = HSVColor.fromAHSV(1, 180 + val * 100, 0.8, val * 0.85 + 0.15).toColor();
final p = i * 4;
_pixels[p] = c.red; _pixels[p+1] = c.green; _pixels[p+2] = c.blue; _pixels[p+3] = 255;
}
}


void dispose() { _ctrl.removeListener(_update); _ctrl.dispose(); super.dispose(); }


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('参数探索')),
body: Column(children: [
Expanded(child: Center(child: CustomPaint(painter: RDPainter(_pixels, 150, 150), size: const Size(150, 150)))),
_buildControls(),
]),
);
}

Widget _buildControls() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(color: Colors.grey[900], borderRadius: const BorderRadius.vertical(top: Radius.circular(20))),
child: Column(mainAxisSize: MainAxisSize.min, children: [
_slider('Feed (供给)', _feed, 0.01, 0.10, (v) { _feed = v; _grid.feed = v; }),
_slider('Kill (衰减)', _kill, 0.03, 0.07, (v) { _kill = v; _grid.kill = v; }),
_slider('Du (激活子扩散)', _du, 0.10, 0.25, (v) { _du = v; _grid.du = v; }),
_slider('Dv (抑制子扩散)', _dv, 0.04, 0.12, (v) { _dv = v; _grid.dv = v; }),
ElevatedButton(onPressed: () { _grid.reset(); _grid.addRandomSeeds(8, 4); setState(() {}); }, child: const Text('重置')),
]),
);
}

Widget _slider(String label, double val, double min, double max, Function(double) cb) {
return Row(children: [
SizedBox(width: 120, child: Text(label, style: const TextStyle(color: Colors.white70, fontSize: 12))),
Expanded(child: Slider(value: val, min: min, max: max, onChanged: (v) { cb(v); setState(() {}); })),
SizedBox(width: 50, child: Text(val.toStringAsFixed(3), style: const TextStyle(color: Colors.teal))),
]);
}
}


📝 四、数学原理深入解析

📐 4.1 稳定性分析

反应扩散系统的稳定性分析是理解斑图形成的关键:

线性稳定性分析:

考虑均匀稳态解 (u₀, v₀):
u₀ = 1, v₀ = 0(无图案状态)

引入小扰动:
u = u₀ + εu', v = v₀ + εv'

线性化方程:
∂u'/∂t = Du∇²u' – f·u' – 2u₀v₀·v'
∂v'/∂t = Dv∇²v' + f·v' + 2u₀v₀·u'

特征值分析:
J = | -f – k²Du -2u₀v₀ |
| 2u₀v₀ f-k – k²Dv |

图案形成条件:存在 k 使得 Re(λ) > 0

扩散不稳定性(Turing 不稳定性):

条件1:无扩散时稳定
∂f/∂u – ∂g/∂u < 0
∂f/∂v · ∂g/∂u – ∂f/∂u · ∂g/∂v > 0

条件2:有扩散时不稳定
Du · ∂g/∂v + Dv · ∂f/∂u > 0

关键洞察:
抑制子必须比激活子扩散更快
Dv > Du(通常 Dv/Du > 2)


🔄 4.2 斑图尺度与波长

特征波长计算:

最不稳定波数 k_c:
k_c² = √((∂g/∂v)·(∂f/∂u) / (Du·Dv))

特征波长 λ:
λ = 2π/k_c

影响波长的因素:
– 扩散系数比 Dv/Du
– 反应速率参数 f, k
– 网格尺寸和边界条件

尺度选择示意:

小尺度(高波数): 大尺度(低波数):
┌─────────────┐ ┌─────────────┐
│●●●●●●●●●●●●│ │ ●●● │
│●●●●●●●●●●●●│ │ ●●●●●●● │
│●●●●●●●●●●●●│ │ ●●● ●●● │
│●●●●●●●●●●●●│ │ ●●● ●●● │
│●●●●●●●●●●●●│ │ ●●● ●●● │
└─────────────┘ └─────────────┘

斑点密集 斑点稀疏


🌸 4.3 其他反应扩散模型

Gierer-Meinhardt 模型:

∂u/∂t = Du∇²u + u²/v – u
∂v/∂t = Dv∇²v + u² – v

特点:
– 激活子自我催化
– 抑制子由激活子产生
– 适合模拟局部激活、长程抑制

Brusselator 模型:

∂u/∂t = Du∇²u + A – (B+1)u + u²v
∂v/∂t = Dv∇²v + Bu – u²v

特点:
– 开放系统模型
– 化学振荡
– Hopf 分岔

Oregonator 模型(BZ 反应):

∂u/∂t = Du∇²u + u(1-u) – f·v·(u-q)/(u+q)
∂v/∂t = Dv∇²v + u – v

特点:
– BZ 化学反应
– 螺旋波
– 时空混沌


🎯 4.4 边界条件与初始条件

边界条件类型:

类型数学描述物理意义
周期边界 u(0) = u(N) 环面拓扑
零通量边界 ∂u/∂n = 0 无物质流出
固定边界 u(边界) = 常数 浓度固定
反射边界 u(-x) = u(x) 镜像对称

初始条件的影响:

随机初始扰动:
– 产生自然、有机的图案
– 每次运行结果不同
– 适合模拟自然现象

规则初始扰动:
– 产生对称图案
– 可预测结果
– 适合艺术创作

条形初始扰动:
– 产生平行条纹
– 方向由扰动方向决定
– 模拟斑马条纹


🔬 五、高级应用场景

🎨 5.1 实时可视化

反应扩散系统在实时可视化中的独特优势:

性能优化策略:

class OptimizedRDSimulation {
late Float32List _u, _v;
final int _width, _height;
bool _useSIMD = false;

// 分块计算
void updateChunked(int chunkSize) {
for (int y0 = 0; y0 < _height; y0 += chunkSize) {
for (int x0 = 0; x0 < _width; x0 += chunkSize) {
_updateChunk(x0, y0, chunkSize, chunkSize);
}
}
}

// 多线程计算(使用 Isolate)
Future<void> updateParallel() async {
final results = await Future.wait([
compute(_computeRegion, RegionParams(0, 0, _width ~/ 2, _height ~/ 2)),
compute(_computeRegion, RegionParams(_width ~/ 2, 0, _width ~/ 2, _height ~/ 2)),
compute(_computeRegion, RegionParams(0, _height ~/ 2, _width ~/ 2, _height ~/ 2)),
compute(_computeRegion, RegionParams(_width ~/ 2, _height ~/ 2, _width ~/ 2, _height ~/ 2)),
]);
// 合并结果
}
}


🌐 5.2 生成艺术应用

参数化艺术生成:

class RDArtGenerator {
final RDGrid grid;
final ColorScheme colors;

RDArtGenerator(this.grid, this.colors);

/// 生成艺术作品
Uint8List generateArtwork({
required int steps,
required double timeOffset,
}) {
grid.update(steps);

final pixels = Uint8List(grid.width * grid.height * 4);
final u = grid.u;
final v = grid.v;

for (int i = 0; i < u.length; i++) {
final val = (u[i] v[i]).clamp(0.0, 1.0);

// 多层颜色映射
final layer1 = colors.primary.withOpacity(val);
final layer2 = colors.secondary.withOpacity(1 val);
final mixed = Color.alphaBlend(layer1, layer2);

// 添加噪声纹理
final noise = _noise(i, timeOffset);
final finalColor = Color.lerp(mixed, colors.tertiary, noise * 0.2)!;

final p = i * 4;
pixels[p] = finalColor.red;
pixels[p + 1] = finalColor.green;
pixels[p + 2] = finalColor.blue;
pixels[p + 3] = 255;
}

return pixels;
}

double _noise(int index, double time) {
return (sin(index * 0.1 + time) + 1) / 2;
}
}


📱 5.3 鸿蒙多端适配

设备性能适配:

class AdaptiveRDConfig {
static RDConfig getConfig(BuildContext context) {
final deviceInfo = DeviceInfo.get(context);

switch (deviceInfo.type) {
case DeviceType.phone:
return RDConfig(
width: 150,
height: 150,
stepsPerFrame: 5,
colorDepth: ColorDepth.medium,
);
case DeviceType.tablet:
return RDConfig(
width: 250,
height: 250,
stepsPerFrame: 10,
colorDepth: ColorDepth.high,
);
case DeviceType.tv:
return RDConfig(
width: 400,
height: 400,
stepsPerFrame: 15,
colorDepth: ColorDepth.ultra,
);
}
}
}


📊 六、性能优化策略

⚡ 6.1 计算优化

向量化计算:

// 使用 Float32List 批量操作
void vectorizedLaplacian(Float32List src, Float32List dst, int w, int h) {
for (int y = 1; y < h 1; y++) {
for (int x = 1; x < w 1; x++) {
final i = y * w + x;
dst[i] = src[i1] + src[i+1] + src[iw] + src[i+w] 4 * src[i];
}
}
}

内存布局优化:

// 使用连续内存布局
class OptimizedGrid {
// 交错存储 u 和 v
late Float32List _data; // [u0, v0, u1, v1, …]

double getU(int i) => _data[i * 2];
double getV(int i) => _data[i * 2 + 1];
void setU(int i, double v) => _data[i * 2] = v;
void setV(int i, double v) => _data[i * 2 + 1] = v;
}


💾 6.2 渲染优化

增量渲染:

class IncrementalRenderer {
int _lastRenderStep = 0;

Uint8List renderIfNeeded(RDGrid grid, int currentStep) {
if (currentStep _lastRenderStep < 5) {
return _cachedPixels;
}
_lastRenderStep = currentStep;
return _render(grid);
}
}

GPU 加速(使用 Shader):

// 概念性代码,展示 GPU 加速思路
class GPURenderer {
late FragmentShader _shader;

void initialize() {
_shader = FragmentShader('reaction_diffusion.glsl');
}

void render(Canvas canvas, RDGrid grid) {
_shader.setFloat(0, grid.feed);
_shader.setFloat(1, grid.kill);
_shader.setFloat(2, grid.du);
_shader.setFloat(3, grid.dv);

canvas.drawRect(
Rect.fromLTWH(0, 0, grid.width.toDouble(), grid.height.toDouble()),
Paint()..shader = _shader,
);
}
}


🎓 七、学习资源与拓展

📚 推荐阅读

主题资源难度
图灵斑图理论 《形态发生的化学基础》- 图灵 ⭐⭐⭐
反应扩散方程 《数学生物学》- Murray ⭐⭐⭐
Gray-Scott 模型 Pearson 的在线教程 ⭐⭐
偏微分方程 《偏微分方程数值解法》 ⭐⭐⭐
生成艺术 《生成艺术》- Matt Pearson ⭐⭐

🔗 相关项目

  • Ready:反应扩散系统模拟器
  • Gray-Scott Explorer:在线参数探索工具
  • Processing RD Examples:创意编程示例
  • Karl Sims 的 RD 教程:经典教程

🎯 练习建议

  • 基础练习:实现简单的 Gray-Scott 模型
  • 进阶练习:探索参数空间,发现新图案
  • 挑战练习:实现 GPU 加速版本

  • 📝 八、总结

    本篇文章深入探讨了反应扩散系统的数学原理及其在 Flutter 中的可视化实现。

    ✅ 核心知识点回顾

    知识点说明
    🎨图灵斑图 激活子-抑制子相互作用产生图案
    📐Gray-Scott 模型 经典的反应扩散方程
    🔬参数空间 不同参数产生不同图案类型
    🌿自然界应用 豹纹、斑马条纹、珊瑚纹理
    ⚡性能优化 向量化计算、增量渲染
    📱多端适配 根据设备性能调整网格大小

    ⭐ 最佳实践要点

    • ✅ 使用周期边界条件避免边缘效应
    • ✅ 预计算颜色映射提升渲染性能
    • ✅ 根据设备性能调整网格分辨率
    • ✅ 提供交互式参数调节功能

    🚀 进阶方向

    • 🔮 三维反应扩散系统
    • ✨ 多种化学物质耦合
    • 🎨 GPU 着色器加速
    • 📊 实时参数空间探索

    💡 提示:本文代码基于 Flutter for Harmony 开发,可在鸿蒙设备上流畅运行。

    赞(0)
    未经允许不得转载:171主机测评 » Flutter for Harmony 跨平台开发实战:反应扩散系统——图灵斑图的动态生成
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址