Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、分形几何与自相似性:无限细节的数学之美

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


🌀 一、分形几何:无限细节的数学世界

📚 1.1 分形的历史与概念

分形(Fractal)一词由数学家本华·曼德博(Benoît Mandelbrot)于1975年创造,源自拉丁语"fractus",意为"破碎的"或"不规则的"。分形是一种在不同尺度上具有自相似性的几何形状。

分形发展里程碑

年份 人物 贡献
17世纪 莱布尼茨 递归自相似概念
1883 康托尔 康托尔集
1904 科赫 科赫雪花
1915 谢尔宾斯基 谢尔宾斯基三角形
1918 朱利亚 朱利亚集合
1980 曼德博 曼德博集合
现代 计算机 实时渲染与艺术应用

分形的核心特征

复制代码
分形的定义特征:

1. 自相似性 (Self-similarity)
   - 整体与局部相似
   - 不同尺度下结构相同
   - 可以是精确的或统计的

2. 无限细节 (Infinite detail)
   - 无论放大多少倍
   - 总能看到新的细节
   - 永远不会"平滑"

3. 分数维度 (Fractional dimension)
   - 维度不是整数
   - 介于线和面之间
   - 科赫曲线维度 ≈ 1.26

4. 简单规则生成复杂形态
   - 简单的迭代公式
   - 产生极其复杂的图案
   - 混沌与秩序的统一

分形在自然界中的存在

复制代码
自然界中的分形结构:

植物:
- 树枝的分叉模式
- 蕨类叶片的形态
- 花椰菜的结构
- 闪电的分支

地理:
- 海岸线的形状
- 河流的水系
- 山脉的轮廓
- 云朵的边缘

生物:
- 血管网络
- 支气管树
- 神经系统
- 细胞分裂

这些自然分形启发了数学分形的研究!

🔬 1.2 曼德博集合:数学最美的发现

曼德博集合的定义

复制代码
曼德博集合 M 定义为:

M = { c ∈ ℂ : zₙ₊₁ = zₙ² + c, z₀ = 0, n→∞ 时 zₙ 有界 }

简单来说:
- 对于复平面上的每个点 c
- 迭代 z = z² + c,从 z = 0 开始
- 如果迭代结果不发散到无穷,则 c 属于曼德博集合

迭代过程:
z₀ = 0
z₁ = z₀² + c = c
z₂ = z₁² + c = c² + c
z₃ = z₂² + c = (c² + c)² + c
...

判断是否发散:
- 如果 |z| > 2,则一定会发散
- 如果经过 N 次迭代后 |z| ≤ 2,认为 c 属于集合

曼德博集合的数学性质

复制代码
曼德博集合的神奇性质:

1. 连通性
   - 集合是连通的(一个整体)
   - 但边界极其复杂
   - 边界维度约为 2

2. 自相似性
   - 边界上存在无限多个"小曼德博"
   - 每个小曼德博都与整体相似
   - 但不完全相同

3. 普适性
   - 包含所有可能的朱利亚集合
   - 每个点 c 对应一个朱利亚集合
   - 曼德博集合是朱利亚集合的"目录"

4. 周期轨道
   - 主心形区域:周期1
   - 圆盘:周期2
   - 更小的圆盘:更高周期

曼德博集合的关键区域

复制代码
曼德博集合的主要结构:

           ╭─────────────────╮
          ╱                   ╲
         ╱    主心形区域       ╲
        │    (周期1轨道)        │
        │         ○             │
         ╲    周期2圆盘        ╱
          ╲      (●)         ╱
           ╲               ╱
            ╲             ╱
             ╰───────────╯
              触角和分支

主心形区域(Cardioid):
- 方程:r = (1 - cos(θ))/2
- 内部点迭代收敛到固定点
- 周期1轨道

周期2圆盘:
- 圆心在 c = -1
- 半径 1/4
- 迭代在两个值之间振荡

触角(Antennae):
- 从主集合延伸出的细丝
- 包含无限多的小曼德博集合
- 是最复杂的区域

🎨 1.3 朱利亚集合:曼德博的镜像

朱利亚集合的定义

复制代码
朱利亚集合 J(c) 定义为:

对于固定的复数 c:
J(c) = { z₀ ∈ ℂ : zₙ₊₁ = zₙ² + c, n→∞ 时 zₙ 有界 }

与曼德博集合的区别:
- 曼德博:固定 z₀ = 0,变化 c
- 朱利亚:固定 c,变化 z₀

每个 c 值对应一个不同的朱利亚集合!

朱利亚集合的分类:
1. 连通朱利亚集合(c 在曼德博集合内部)
   - 形成一个连通的整体
   - 边界复杂但完整

2. 康托尔朱利亚集合(c 在曼德博集合外部)
   - 分散成无数个点
   - 像尘埃一样分布

经典朱利亚集合形态

复制代码
不同 c 值产生的朱利亚集合:

c = -0.4 + 0.6i(龙形):
     ╱╲╱╲╱╲╱╲
    ╱    ╲    ╲
   ╱      ╲    ╲
  ╱        ╲    ╲
 ╱          ╲   ╱
╱            ╲╱╱
    连通的龙形图案
    c 在曼德博集合内部

c = 0.285 + 0.01i(螺旋):
    ╭───────╮
   ╱  ╭───╮  ╲
  │  ╱     ╲  │
  │ │   ○   │ │
  │  ╲     ╱  │
   ╲  ╰───╯  ╱
    ╰───────╯
    美丽的螺旋结构
    接近曼德博边界

c = -0.8 + 0.156i(树枝):
      ╱╲
     ╱  ╲
    ╱    ╲
   ╱  ╱╲  ╲
  ╱  ╱  ╲  ╲
 ╱  ╱    ╲  ╲
    树枝状分形
    典型的自相似结构

c = -2(直线):
────────────────
    退化为直线
    在曼德博边界上

朱利亚集合与曼德博集合的关系

复制代码
曼德博集合是朱利亚集合的"地图":

曼德博集合内部:
- c 在主心形内 → 朱利亚集合是变形的圆
- c 在周期2圆盘 → 朱利亚集合有对称结构
- c 在小圆盘内 → 朱利亚集合有对应周期结构

曼德博集合边界:
- 朱利亚集合最复杂
- 边界分形维度最高
- 视觉效果最丰富

曼德博集合外部:
- 朱利亚集合是康托尔尘埃
- 分散成无数孤立点
- 不形成连通区域

通过曼德博集合可以"导航"所有朱利亚集合!

📐 1.4 其他经典分形

科赫雪花(Koch Snowflake)

复制代码
科赫雪花的构造过程:

第0代(等边三角形):
    ╱╲
   ╱  ╲
  ╱    ╲
 ╱      ╲
╱────────╲

第1代:
    ╱╲
   ╱  ╲
  ╱ ╱╲ ╲
 ╱      ╲
╱────────╲

第2代:
      ╱╲
     ╱  ╲
    ╱╱╲╱╲╲
   ╱      ╲
  ╱ ╱╲╱╲ ╱╲ ╲
 ╱        ╲
╱──────────╲

构造规则:
- 每条边分成三等份
- 中间部分向外突出成等边三角形
- 无限重复

数学性质:
- 周长:无限(每代增加 4/3 倍)
- 面积:有限(收敛到初始三角形的 8/5 倍)
- 维度:log(4)/log(3) ≈ 1.26

谢尔宾斯基三角形(Sierpinski Triangle)

复制代码
谢尔宾斯基三角形的构造:

第0代:        第1代:        第2代:
    ▲              ▲              ▲
   ▲▲            ▲ ▲            ▲ ▲
  ▲▲▲▲          ▲   ▲          ▲   ▲
 ▲▲▲▲▲▲        ▲▲▲ ▲▲▲        ▲▲▲ ▲▲▲
                            ▲   ▲   ▲   ▲
                           ▲▲▲ ▲▲▲ ▲▲▲ ▲▲▲

构造规则:
- 从实心三角形开始
- 移除中心的倒三角形
- 对剩余的三个小三角形重复

数学性质:
- 面积:趋于 0
- 维度:log(3)/log(2) ≈ 1.58
- 自相似:每个小三角形与整体相同

巴恩斯利蕨(Barnsley Fern)

复制代码
巴恩斯利蕨的迭代函数系统:

使用四个仿射变换:
f₁(x,y) = (0, 0.16y)                    概率 1%
f₂(x,y) = (0.85x + 0.04y, -0.04x + 0.85y + 1.6)  概率 85%
f₃(x,y) = (0.2x - 0.26y, 0.23x + 0.22y + 1.6)    概率 7%
f₄(x,y) = (-0.15x + 0.28y, 0.26x + 0.24y + 0.44) 概率 7%

结果:
        🌿
       🌿🌿
      🌿  🌿
     🌿🌿🌿🌿
    🌿      🌿
   🌿🌿    🌿🌿
  🌿  🌿  🌿  🌿
 🌿🌿🌿🌿🌿🌿🌿🌿
🌿              🌿

- 完美模拟真实蕨类植物
- 展示了分形在自然界中的普遍性
- 简单规则产生复杂形态

🎵 二、分形与音乐

🎼 2.1 分形音乐的概念

音乐中的分形结构

复制代码
音乐天然具有分形特性:

1. 节奏层次
   全音符 → 二分音符 → 四分音符 → 八分音符
   每个层次都是上一层的细分
   自相似的时间结构

2. 旋律结构
   乐句 → 乐段 → 乐章 → 整部作品
   不同尺度上的相似模式
   主题的变奏与重复

3. 和声进行
   和弦 → 和弦进行 → 调性区域 → 整体调性布局
   多层次的组织结构

4. 音色频谱
   基频 → 泛音列 → 谐波结构
   频率域的分形特征

分形音乐生成方法

复制代码
使用分形原理生成音乐:

1. 一维映射(音高)
   将分形迭代值映射到音高
   zₙ 的实部或虚部 → MIDI音符号
   
   示例:
   z = 0.5 + 0.3i → 音高 C4
   z = 0.7 + 0.1i → 音高 E4
   z = -0.2 + 0.4i → 音高 G4

2. 二维映射(音高+时间)
   实部 → 音高
   虚部 → 时间或节奏
   
3. 逃逸时间映射
   迭代次数 → 音高或力度
   快速逃逸 → 高音/强音
   慢速逃逸 → 低音/弱音

4. 自相似旋律
   使用 L-系统生成
   规则:A → AB, B → A
   结果:A, AB, ABA, ABAAB, ...

🎚️ 2.2 音频驱动的分形可视化

音频参数映射到分形参数

复制代码
将音频特征映射到分形参数:

1. 能量 → 迭代深度
   能量高 → 更多迭代 → 更多细节
   能量低 → 较少迭代 → 简单图案

2. 频率分布 → 颜色映射
   低频 → 红色系
   中频 → 绿色系
   高频 → 蓝色系

3. 节奏 → 缩放动画
   强拍 → 快速缩放
   弱拍 → 缓慢变化

4. 音色 → 分形类型
   纯净音色 → 曼德博集合
   复杂音色 → 朱利亚集合
   噪声音色 → 混沌图案

5. 音高 → 复平面位置
   低音 → 左下区域
   高音 → 右上区域

实时音频分形系统

复制代码
实时音频分形可视化流程:

┌─────────────┐
│  音频输入   │
└──────┬──────┘
       ↓
┌─────────────┐
│  FFT分析    │ → 频谱数据
└──────┬──────┘
       ↓
┌─────────────┐
│  特征提取   │ → 能量、频率、节奏
└──────┬──────┘
       ↓
┌─────────────┐
│  参数映射   │ → 分形参数
└──────┬──────┘
       ↓
┌─────────────┐
│  分形渲染   │ → 图像输出
└─────────────┘

关键参数:
- c 值:由音频特征决定
- 迭代次数:由能量决定
- 缩放中心:由主频决定
- 颜色方案:由音色决定

📊 2.3 分形维度与音乐复杂度

音乐复杂度的分形度量

复制代码
使用分形维度分析音乐复杂度:

1. 旋律线维度
   - 简单旋律:维度接近 1
   - 复杂旋律:维度接近 1.5
   - 随机旋律:维度接近 2

2. 节奏模式维度
   - 规则节奏:维度接近 1
   - 切分节奏:维度 1.2-1.5
   - 复杂节奏:维度接近 2

3. 频谱维度
   - 纯音:维度接近 0
   - 乐音:维度 0.5-1
   - 噪音:维度接近 2

计算方法:
使用盒子计数法(Box Counting)
N(ε) ∝ ε^(-D)
其中 ε 是盒子大小,N 是覆盖所需的盒子数

🔧 三、Dart/Flutter 中的分形实现

🌀 3.1 曼德博集合计算器

dart 复制代码
import 'dart:math';

class Complex {
  final double real;
  final double imag;
  
  const Complex(this.real, this.imag);
  
  Complex operator +(Complex other) => Complex(real + other.real, imag + other.imag);
  Complex operator *(Complex other) => Complex(real * other.real - imag * other.imag, real * other.imag + imag * other.real);
  
  double get magnitude => sqrt(real * real + imag * imag);
  
  Complex squared() => Complex(real * real - imag * imag, 2 * real * imag);
}

class MandelbrotCalculator {
  final int maxIterations;
  final double escapeRadius;
  
  MandelbrotCalculator({this.maxIterations = 100, this.escapeRadius = 2.0});
  
  int calculateEscapeTime(Complex c) {
    var z = const Complex(0, 0);
    
    for (int i = 0; i < maxIterations; i++) {
      z = z.squared() + c;
      if (z.magnitude > escapeRadius) {
        return i;
      }
    }
    
    return maxIterations;
  }
  
  bool isInSet(Complex c) => calculateEscapeTime(c) == maxIterations;
  
  double calculateSmoothEscapeTime(Complex c) {
    var z = const Complex(0, 0);
    
    for (int i = 0; i < maxIterations; i++) {
      z = z.squared() + c;
      if (z.magnitude > escapeRadius) {
        return i + 1 - log(log(z.magnitude)) / log(2);
      }
    }
    
    return maxIterations.toDouble();
  }
}

🎨 3.2 朱利亚集合计算器

dart 复制代码
class JuliaCalculator {
  final Complex c;
  final int maxIterations;
  final double escapeRadius;
  
  JuliaCalculator({required this.c, this.maxIterations = 100, this.escapeRadius = 2.0});
  
  int calculateEscapeTime(Complex z0) {
    var z = z0;
    
    for (int i = 0; i < maxIterations; i++) {
      z = z.squared() + c;
      if (z.magnitude > escapeRadius) {
        return i;
      }
    }
    
    return maxIterations;
  }
  
  double calculateSmoothEscapeTime(Complex z0) {
    var z = z0;
    
    for (int i = 0; i < maxIterations; i++) {
      z = z.squared() + c;
      if (z.magnitude > escapeRadius) {
        return i + 1 - log(log(z.magnitude)) / log(2);
      }
    }
    
    return maxIterations.toDouble();
  }
  
  static List<Complex> get presetCValues => [
    const Complex(-0.4, 0.6),    // 龙形
    const Complex(0.285, 0.01),  // 螺旋
    const Complex(-0.8, 0.156),  // 树枝
    const Complex(-0.70176, -0.3842), // 闪电
    const Complex(-0.835, -0.2321),   // 雪花
    const Complex(0.45, 0.1428),      // 星云
  ];
}

🎵 3.3 音频驱动的分形控制器

dart 复制代码
import 'dart:typed_data';

class AudioFractalController {
  Complex center = const Complex(-0.5, 0);
  double zoom = 1.0;
  int maxIterations = 100;
  
  Complex juliaC = const Complex(-0.4, 0.6);
  
  Float32List audioData = Float32List(64);
  double energy = 0;
  double bass = 0;
  double mid = 0;
  double treble = 0;
  
  void updateFromAudio(Float32List data) {
    audioData = data;
    
    double total = 0, bassE = 0, midE = 0, trebleE = 0;
    
    for (int i = 0; i < data.length; i++) {
      total += data[i];
      if (i < data.length * 0.25) {
        bassE += data[i];
      } else if (i < data.length * 0.6) {
        midE += data[i];
      } else {
        trebleE += data[i];
      }
    }
    
    energy = total / data.length;
    bass = bassE / (data.length * 0.25);
    mid = midE / (data.length * 0.35);
    treble = trebleE / (data.length * 0.4);
  }
  
  Complex get dynamicJuliaC => Complex(
    juliaC.real + (mid - 0.5) * 0.2,
    juliaC.imag + (treble - 0.5) * 0.2,
  );
  
  int get dynamicMaxIterations => (maxIterations * (1 + energy * 0.5)).toInt();
  
  double get dynamicZoom => zoom * (1 + bass * 2);
}

💻 四、完整代码实现

dart 复制代码
import 'package:flutter/material.dart';
import 'package:just_audio_ohos/just_audio_ohos.dart';
import 'package:audio_session/audio_session.dart';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'dart:async';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '分形几何',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple, brightness: Brightness.dark),
        useMaterial3: true,
      ),
      home: const FractalHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class Complex {
  final double real;
  final double imag;
  
  const Complex(this.real, this.imag);
  
  Complex operator +(Complex other) => Complex(real + other.real, imag + other.imag);
  Complex operator *(Complex other) => Complex(real * other.real - imag * other.imag, real * other.imag + imag * other.real);
  
  double get magnitude => sqrt(real * real + imag * imag);
  
  Complex squared() => Complex(real * real - imag * imag, 2 * real * imag);
  
  @override
  String toString() => 'Complex($real, $imag)';
}

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

  @override
  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: '无限细节的数学奇迹', icon: Icons.blur_on, color: Colors.deepPurple,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const MandelbrotDemo()))),
          _buildCard(context, title: '朱利亚集合', description: '曼德博的镜像世界', icon: Icons.filter_vintage, color: Colors.purple,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const JuliaSetDemo()))),
          _buildCard(context, title: '音乐分形', description: '音频驱动的动态分形', icon: Icons.music_note, color: Colors.indigo,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const MusicFractalDemo()))),
          _buildCard(context, title: '科赫雪花', description: '无限边界的有限面积', icon: Icons.ac_unit, color: Colors.cyan,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const KochSnowflakeDemo()))),
          _buildCard(context, title: '谢尔宾斯基', description: '自相似的三角形', icon: Icons.change_history, color: Colors.teal,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SierpinskiDemo()))),
          _buildCard(context, title: '分形树', description: '递归生长的树形结构', icon: Icons.nature, color: Colors.green,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const FractalTreeDemo()))),
          _buildCard(context, title: '巴恩斯利蕨', description: '自然界的分形', icon: Icons.eco, color: Colors.lightGreen,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const BarnsleyFernDemo()))),
          _buildCard(context, title: '分形动画', description: '缩放与颜色动画', icon: Icons.animation, color: Colors.pink,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const FractalAnimationDemo()))),
        ],
      ),
    );
  }

  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 MandelbrotDemo extends StatefulWidget {
  const MandelbrotDemo({super.key});
  @override
  State<MandelbrotDemo> createState() => _MandelbrotDemoState();
}

class _MandelbrotDemoState extends State<MandelbrotDemo> {
  Complex _center = const Complex(-0.5, 0);
  double _zoom = 1.0;
  int _maxIterations = 100;
  int _colorScheme = 0;
  bool _isRendering = false;
  ui.Image? _cachedImage;
  
  final TextEditingController _realController = TextEditingController(text: '-0.5');
  final TextEditingController _imagController = TextEditingController(text: '0');

  @override
  void initState() {
    super.initState();
    _renderFractal();
  }

  void _renderFractal() {
    if (_isRendering) return;
    setState(() => _isRendering = true);
    
    Future.delayed(const Duration(milliseconds: 50), () {
      if (!mounted) return;
      _generateImage().then((image) {
        if (mounted) {
          setState(() {
            _cachedImage = image;
            _isRendering = false;
          });
        }
      });
    });
  }

  Future<ui.Image> _generateImage() async {
    const width = 800;
    const height = 800;
    final pixels = Int32List(width * height);
    
    final scale = 3.0 / (_zoom * width);
    
    for (int py = 0; py < height; py++) {
      for (int px = 0; px < width; px++) {
        final x = (px - width / 2) * scale + _center.real;
        final y = (py - height / 2) * scale + _center.imag;
        
        final escapeTime = _calculateMandelbrot(Complex(x, y), _maxIterations);
        pixels[py * width + px] = _getColor(escapeTime, _maxIterations);
      }
    }
    
    final completer = Completer<ui.Image>();
    ui.decodeImageFromPixels(
      Uint8List.view(pixels.buffer),
      width,
      height,
      ui.PixelFormat.rgba8888,
      completer.complete,
    );
    
    return completer.future;
  }

  int _calculateMandelbrot(Complex c, int maxIter) {
    var z = const Complex(0, 0);
    for (int i = 0; i < maxIter; i++) {
      z = z.squared() + c;
      if (z.magnitude > 2) return i;
    }
    return maxIter;
  }

  int _getColor(int escapeTime, int maxIter) {
    if (escapeTime == maxIter) return 0xFF000000;
    
    final t = escapeTime / maxIter;
    int r, g, b;
    
    switch (_colorScheme) {
      case 0:
        r = (9 * (1 - t) * t * t * t * 255).toInt();
        g = (15 * (1 - t) * (1 - t) * t * t * 255).toInt();
        b = (8.5 * (1 - t) * (1 - t) * (1 - t) * t * 255).toInt();
        break;
      case 1:
        r = (t * 255).toInt();
        g = ((1 - t) * 255).toInt();
        b = (sin(t * pi) * 255).toInt();
        break;
      default:
        final hue = t * 360;
        r = ((1 + sin(hue * pi / 180)) * 127.5).toInt();
        g = ((1 + sin((hue - 120) * pi / 180)) * 127.5).toInt();
        b = ((1 + sin((hue - 240) * pi / 180)) * 127.5).toInt();
    }
    
    return 0xFF000000 | (r << 16) | (g << 8) | b;
  }

  void _zoomIn() {
    setState(() {
      _zoom *= 1.5;
      _maxIterations = (100 + _zoom * 10).toInt();
    });
    _renderFractal();
  }

  void _zoomOut() {
    setState(() {
      _zoom /= 1.5;
      if (_zoom < 0.1) _zoom = 0.1;
      _maxIterations = (100 + _zoom * 10).toInt();
    });
    _renderFractal();
  }

  void _goToLocation(double real, double imag) {
    setState(() {
      _center = Complex(real, imag);
      _zoom = 100;
      _maxIterations = 200;
    });
    _renderFractal();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('曼德博集合')),
      body: Stack(children: [
        GestureDetector(
          onTapDown: (details) {
            final renderBox = context.findRenderObject() as RenderBox;
            final size = renderBox.size;
            final dx = details.localPosition.dx - size.width / 2;
            final dy = details.localPosition.dy - size.height / 2;
            final scale = 3.0 / (_zoom * min(size.width, size.height));
            setState(() {
              _center = Complex(_center.real + dx * scale, _center.imag + dy * scale);
            });
            _renderFractal();
          },
          child: CustomPaint(
            painter: ImagePainter(_cachedImage),
            size: Size.infinite,
          ),
        ),
        if (_isRendering)
          const Center(child: CircularProgressIndicator()),
        Positioned(top: 20, left: 20, child: _buildInfo()),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildInfo() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(8)),
      child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
        Text('缩放: ${_zoom.toStringAsFixed(1)}x', style: const TextStyle(color: Colors.white, fontSize: 14)),
        Text('中心: (${_center.real.toStringAsFixed(4)}, ${_center.imag.toStringAsFixed(4)})', style: const TextStyle(color: Colors.white70, fontSize: 12)),
        Text('迭代: $_maxIterations', style: const TextStyle(color: Colors.white70, fontSize: 12)),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.8), borderRadius: BorderRadius.circular(12)),
      child: Column(mainAxisSize: MainAxisSize.min, children: [
        Row(children: [
          Expanded(child: TextField(controller: _realController, decoration: const InputDecoration(labelText: '实部', labelStyle: TextStyle(color: Colors.white70)),
              style: const TextStyle(color: Colors.white), keyboardType: TextInputType.number)),
          const SizedBox(width: 8),
          Expanded(child: TextField(controller: _imagController, decoration: const InputDecoration(labelText: '虚部', labelStyle: TextStyle(color: Colors.white70)),
              style: const TextStyle(color: Colors.white), keyboardType: TextInputType.number)),
          IconButton(icon: const Icon(Icons.my_location, color: Colors.deepPurple),
              onPressed: () => _goToLocation(double.tryParse(_realController.text) ?? -0.5, double.tryParse(_imagController.text) ?? 0)),
        ]),
        const SizedBox(height: 8),
        Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
          IconButton(icon: const Icon(Icons.add, color: Colors.deepPurple), onPressed: _zoomIn),
          IconButton(icon: const Icon(Icons.remove, color: Colors.deepPurple), onPressed: _zoomOut),
          IconButton(icon: const Icon(Icons.refresh, color: Colors.deepPurple), onPressed: () {
            setState(() {
              _center = const Complex(-0.5, 0);
              _zoom = 1.0;
              _maxIterations = 100;
            });
            _renderFractal();
          }),
          IconButton(icon: const Icon(Icons.palette, color: Colors.deepPurple), onPressed: () {
            setState(() => _colorScheme = (_colorScheme + 1) % 3);
            _renderFractal();
          }),
        ]),
        const SizedBox(height: 8),
        Wrap(spacing: 8, children: [
          _buildPresetChip('海马谷', -0.75, 0.1),
          _buildPresetChip('螺旋', -0.745, 0.113),
          _buildPresetChip('闪电', -1.25066, 0.02012),
          _buildPresetChip('象谷', 0.275, 0.0),
        ]),
      ]),
    );
  }
  
  Widget _buildPresetChip(String label, double real, double imag) {
    return ActionChip(
      label: Text(label, style: const TextStyle(color: Colors.white, fontSize: 11)),
      backgroundColor: Colors.deepPurple.withOpacity(0.5),
      onPressed: () => _goToLocation(real, imag),
    );
  }

  @override
  void dispose() {
    _realController.dispose();
    _imagController.dispose();
    super.dispose();
  }
}

class ImagePainter extends CustomPainter {
  final ui.Image? image;
  
  ImagePainter(this.image);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = Colors.black);
    if (image != null) {
      canvas.drawImageRect(image!, Rect.fromLTWH(0, 0, image!.width.toDouble(), image!.height.toDouble()),
          Rect.fromLTWH(0, 0, size.width, size.height), Paint());
    }
  }
  
  @override
  bool shouldRepaint(covariant ImagePainter old) => image != old.image;
}

class JuliaSetDemo extends StatefulWidget {
  const JuliaSetDemo({super.key});
  @override
  State<JuliaSetDemo> createState() => _JuliaSetDemoState();
}

class _JuliaSetDemoState extends State<JuliaSetDemo> {
  Complex _c = const Complex(-0.4, 0.6);
  int _maxIterations = 100;
  bool _isRendering = false;
  ui.Image? _cachedImage;
  
  final List<Map<String, dynamic>> _presets = [
    {'name': '龙形', 'c': const Complex(-0.4, 0.6)},
    {'name': '螺旋', 'c': const Complex(0.285, 0.01)},
    {'name': '树枝', 'c': const Complex(-0.8, 0.156)},
    {'name': '闪电', 'c': const Complex(-0.70176, -0.3842)},
    {'name': '雪花', 'c': const Complex(-0.835, -0.2321)},
    {'name': '星云', 'c': const Complex(0.45, 0.1428)},
  ];

  @override
  void initState() {
    super.initState();
    _renderFractal();
  }

  void _renderFractal() {
    if (_isRendering) return;
    setState(() => _isRendering = true);
    
    Future.delayed(const Duration(milliseconds: 50), () {
      if (!mounted) return;
      _generateImage().then((image) {
        if (mounted) {
          setState(() {
            _cachedImage = image;
            _isRendering = false;
          });
        }
      });
    });
  }

  Future<ui.Image> _generateImage() async {
    const width = 800;
    const height = 800;
    final pixels = Int32List(width * height);
    
    const scale = 4.0 / width;
    
    for (int py = 0; py < height; py++) {
      for (int px = 0; px < width; px++) {
        final x = (px - width / 2) * scale;
        final y = (py - height / 2) * scale;
        
        final escapeTime = _calculateJulia(Complex(x, y), _c, _maxIterations);
        pixels[py * width + px] = _getColor(escapeTime, _maxIterations);
      }
    }
    
    final completer = Completer<ui.Image>();
    ui.decodeImageFromPixels(
      Uint8List.view(pixels.buffer),
      width,
      height,
      ui.PixelFormat.rgba8888,
      completer.complete,
    );
    
    return completer.future;
  }

  int _calculateJulia(Complex z0, Complex c, int maxIter) {
    var z = z0;
    for (int i = 0; i < maxIter; i++) {
      z = z.squared() + c;
      if (z.magnitude > 2) return i;
    }
    return maxIter;
  }

  int _getColor(int escapeTime, int maxIter) {
    if (escapeTime == maxIter) return 0xFF000000;
    
    final t = escapeTime / maxIter;
    final hue = t * 360;
    final r = ((1 + sin(hue * pi / 180)) * 127.5).toInt();
    final g = ((1 + sin((hue - 120) * pi / 180)) * 127.5).toInt();
    final b = ((1 + sin((hue - 240) * pi / 180)) * 127.5).toInt();
    
    return 0xFF000000 | (r << 16) | (g << 8) | b;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('朱利亚集合')),
      body: Stack(children: [
        CustomPaint(painter: ImagePainter(_cachedImage), size: Size.infinite),
        if (_isRendering) const Center(child: CircularProgressIndicator()),
        Positioned(top: 20, left: 20, child: _buildInfo()),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildInfo() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(8)),
      child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
        Text('c = ${_c.real.toStringAsFixed(4)} + ${_c.imag.toStringAsFixed(4)}i', style: const TextStyle(color: Colors.white, fontSize: 14)),
        Text('迭代: $_maxIterations', style: const TextStyle(color: Colors.white70, fontSize: 12)),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.8), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Wrap(spacing: 8, runSpacing: 8, children: _presets.map((p) => ActionChip(
          label: Text(p['name'], style: const TextStyle(color: Colors.white, fontSize: 11)),
          backgroundColor: _c == p['c'] ? Colors.purple : Colors.purple.withOpacity(0.3),
          onPressed: () {
            setState(() => _c = p['c'] as Complex);
            _renderFractal();
          },
        )).toList()),
        const SizedBox(height: 8),
        Row(children: [
          Expanded(child: Slider(value: _c.real, min: -2, max: 2, onChanged: (v) {
            setState(() => _c = Complex(v, _c.imag));
            _renderFractal();
          }, activeColor: Colors.purple)),
          Expanded(child: Slider(value: _c.imag, min: -2, max: 2, onChanged: (v) {
            setState(() => _c = Complex(_c.real, v));
            _renderFractal();
          }, activeColor: Colors.purple)),
        ]),
      ]),
    );
  }
}

class MusicFractalDemo extends StatefulWidget {
  const MusicFractalDemo({super.key});
  @override
  State<MusicFractalDemo> createState() => _MusicFractalDemoState();
}

class _MusicFractalDemoState extends State<MusicFractalDemo> with TickerProviderStateMixin {
  late AnimationController _animController;
  late AudioPlayer _audioPlayer;
  
  Complex _c = const Complex(-0.4, 0.6);
  double _time = 0;
  
  bool _isPlaying = false;
  Float32List _audioData = Float32List(64);
  double _energy = 0, _bass = 0, _mid = 0, _treble = 0;
  
  static const String _audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  @override
  void initState() {
    super.initState();
    _initAudio();
    _animController = AnimationController(vsync: this, duration: const Duration(milliseconds: 33))..repeat();
    _animController.addListener(_update);
  }
  
  Future<void> _initAudio() async {
    _audioPlayer = AudioPlayer();
    final session = await AudioSession.instance;
    await session.configure(const AudioSessionConfiguration.music());
    _audioPlayer.playerStateStream.listen((s) => setState(() => _isPlaying = s.playing));
    try { await _audioPlayer.setUrl(_audioUrl); } catch (e) { debugPrint('加载失败: $e'); }
  }
  
  void _update() {
    _time += 0.033;
    for (int i = 0; i < 64; i++) {
      if (_isPlaying) {
        final freq = (i / 64) * 8 + 1;
        _audioData[i] = _audioData[i] * 0.85 + (sin(_time * freq) * 0.5 + 0.5) * 0.15;
      } else {
        _audioData[i] *= 0.95;
      }
    }
    
    double total = 0, bassE = 0, midE = 0, trebleE = 0;
    for (int i = 0; i < 64; i++) {
      total += _audioData[i];
      if (i < 16) bassE += _audioData[i];
      else if (i < 40) midE += _audioData[i];
      else trebleE += _audioData[i];
    }
    _energy = total / 64;
    _bass = bassE / 16;
    _mid = midE / 24;
    _treble = trebleE / 24;
    
    _c = Complex(-0.4 + (_mid - 0.5) * 0.3, 0.6 + (_treble - 0.5) * 0.3);
    setState(() {});
  }

  @override
  void dispose() {
    _animController.dispose();
    _audioPlayer.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('音乐分形')),
      body: Stack(children: [
        CustomPaint(painter: MusicFractalPainter(_c, _energy, _time, _bass, _mid, _treble), size: Size.infinite),
        Positioned(bottom: 30, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.8), borderRadius: BorderRadius.circular(16)),
      child: Column(mainAxisSize: MainAxisSize.min, children: [
        Row(children: [
          const Icon(Icons.music_note, color: Colors.indigo),
          const SizedBox(width: 8),
          const Expanded(child: Text('SoundHelix - Song 1', style: TextStyle(color: Colors.white, fontSize: 14))),
          Container(padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
              decoration: BoxDecoration(color: _isPlaying ? Colors.indigo : Colors.grey[800], borderRadius: BorderRadius.circular(12)),
              child: Text(_isPlaying ? '播放中' : '暂停', style: const TextStyle(color: Colors.white, fontSize: 12))),
        ]),
        const SizedBox(height: 12),
        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
          IconButton(icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.indigo, size: 36),
              onPressed: () => _isPlaying ? _audioPlayer.pause() : _audioPlayer.play()),
        ]),
        Row(children: [
          Expanded(child: _buildMeter('低频', _bass, Colors.red)),
          const SizedBox(width: 8),
          Expanded(child: _buildMeter('中频', _mid, Colors.yellow)),
          const SizedBox(width: 8),
          Expanded(child: _buildMeter('高频', _treble, Colors.cyan)),
        ]),
      ]),
    );
  }
  
  Widget _buildMeter(String label, double value, Color color) {
    return Column(children: [
      Text(label, style: const TextStyle(color: Colors.white70, fontSize: 10)),
      const SizedBox(height: 4),
      Container(height: 30, decoration: BoxDecoration(color: Colors.grey[800], borderRadius: BorderRadius.circular(4)),
          child: Align(alignment: Alignment.bottomCenter,
              child: AnimatedContainer(duration: const Duration(milliseconds: 50), height: (value * 30).clamp(2.0, 30.0),
                  decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(4))))),
    ]);
  }
}

class MusicFractalPainter extends CustomPainter {
  final Complex c;
  final double energy;
  final double time;
  final double bass;
  final double mid;
  final double treble;
  
  MusicFractalPainter(this.c, this.energy, this.time, this.bass, this.mid, this.treble);
  
  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final scale = min(size.width, size.height) * 0.2;
    
    final bgColor = Color.lerp(const Color(0xFF050510), const Color(0xFF100520), energy)!;
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = bgColor);
    
    final maxIter = (50 + energy * 100).toInt();
    final pixelScale = 3.0 / (size.width * 0.4);
    
    for (int py = 0; py < size.height; py += 2) {
      for (int px = 0; px < size.width; px += 2) {
        final x = (px - size.width / 2) * pixelScale;
        final y = (py - size.height / 2) * pixelScale;
        
        var z = Complex(x, y);
        int iter = 0;
        for (; iter < maxIter; iter++) {
          z = z.squared() + c;
          if (z.magnitude > 2) break;
        }
        
        if (iter < maxIter) {
          final t = iter / maxIter;
          final hue = (t * 180 + time * 30) % 360;
          final alpha = (0.3 + t * 0.5) * (0.5 + energy * 0.5);
          canvas.drawRect(Rect.fromLTWH(px.toDouble(), py.toDouble(), 2, 2), Paint()..color = HSVColor.fromAHSV(alpha, hue, 0.9, 1).toColor());
        }
      }
    }
  }
  
  @override
  bool shouldRepaint(covariant MusicFractalPainter old) => true;
}

class KochSnowflakeDemo extends StatefulWidget {
  const KochSnowflakeDemo({super.key});
  @override
  State<KochSnowflakeDemo> createState() => _KochSnowflakeDemoState();
}

class _KochSnowflakeDemoState extends State<KochSnowflakeDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  int _iterations = 3;
  double _time = 0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('科赫雪花')),
      body: Stack(children: [
        CustomPaint(painter: KochSnowflakePainter(_iterations, _time), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Text('迭代次数: $_iterations', style: const TextStyle(color: Colors.white70, fontSize: 12)),
        Slider(value: _iterations.toDouble(), min: 0, max: 6, divisions: 6, 
            onChanged: (v) => setState(() => _iterations = v.toInt()), activeColor: Colors.cyan),
        Text('边数: ${3 * pow(4, _iterations)}', style: const TextStyle(color: Colors.white54, fontSize: 10)),
      ]),
    );
  }
}

class KochSnowflakePainter extends CustomPainter {
  final int iterations;
  final double time;
  
  KochSnowflakePainter(this.iterations, this.time);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    final center = Offset(size.width / 2, size.height / 2);
    final radius = min(size.width, size.height) * 0.35;
    
    final p1 = center + Offset(0, -radius);
    final p2 = center + Offset(radius * cos(pi / 6), radius * sin(pi / 6));
    final p3 = center + Offset(-radius * cos(pi / 6), radius * sin(pi / 6));
    
    final points = <Offset>[];
    points.addAll(_generateKochCurve(p1, p2, iterations));
    points.addAll(_generateKochCurve(p2, p3, iterations));
    points.addAll(_generateKochCurve(p3, p1, iterations));
    
    final path = Path()..moveTo(points.first.dx, points.first.dy);
    for (final point in points.skip(1)) {
      path.lineTo(point.dx, point.dy);
    }
    path.close();
    
    final hue = (time * 20) % 360;
    canvas.drawPath(path, Paint()..color = HSVColor.fromAHSV(0.8, hue, 0.7, 1).toColor()..style = PaintingStyle.stroke..strokeWidth = 1.5);
    canvas.drawPath(path, Paint()..color = HSVColor.fromAHSV(0.2, hue, 0.5, 1).toColor()..style = PaintingStyle.fill);
  }
  
  List<Offset> _generateKochCurve(Offset p1, Offset p2, int depth) {
    if (depth == 0) return [p1, p2];
    
    final dx = p2.dx - p1.dx;
    final dy = p2.dy - p1.dy;
    
    final pA = Offset(p1.dx + dx / 3, p1.dy + dy / 3);
    final pB = Offset(p1.dx + dx * 2 / 3, p1.dy + dy * 2 / 3);
    
    final angle = -pi / 3;
    final pC = Offset(
      pA.dx + (pB.dx - pA.dx) * cos(angle) - (pB.dy - pA.dy) * sin(angle),
      pA.dy + (pB.dx - pA.dx) * sin(angle) + (pB.dy - pA.dy) * cos(angle),
    );
    
    return [
      ..._generateKochCurve(p1, pA, depth - 1),
      ..._generateKochCurve(pA, pC, depth - 1),
      ..._generateKochCurve(pC, pB, depth - 1),
      ..._generateKochCurve(pB, p2, depth - 1),
    ];
  }
  
  @override
  bool shouldRepaint(covariant KochSnowflakePainter old) => true;
}

class SierpinskiDemo extends StatefulWidget {
  const SierpinskiDemo({super.key});
  @override
  State<SierpinskiDemo> createState() => _SierpinskiDemoState();
}

class _SierpinskiDemoState extends State<SierpinskiDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  int _iterations = 5;
  double _time = 0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('谢尔宾斯基三角形')),
      body: Stack(children: [
        CustomPaint(painter: SierpinskiPainter(_iterations, _time), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Text('迭代次数: $_iterations', style: const TextStyle(color: Colors.white70, fontSize: 12)),
        Slider(value: _iterations.toDouble(), min: 0, max: 8, divisions: 8, 
            onChanged: (v) => setState(() => _iterations = v.toInt()), activeColor: Colors.teal),
        Text('三角形数: ${pow(3, _iterations)}', style: const TextStyle(color: Colors.white54, fontSize: 10)),
      ]),
    );
  }
}

class SierpinskiPainter extends CustomPainter {
  final int iterations;
  final double time;
  
  SierpinskiPainter(this.iterations, this.time);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    final center = Offset(size.width / 2, size.height / 2);
    final radius = min(size.width, size.height) * 0.4;
    
    final p1 = center + Offset(0, -radius);
    final p2 = center + Offset(radius * cos(pi / 6), radius * sin(pi / 6));
    final p3 = center + Offset(-radius * cos(pi / 6), radius * sin(pi / 6));
    
    _drawSierpinski(canvas, p1, p2, p3, iterations);
  }
  
  void _drawSierpinski(Canvas canvas, Offset p1, Offset p2, Offset p3, int depth) {
    if (depth == 0) {
      final hue = ((p1.dx + p1.dy) * 0.5 + time * 20) % 360;
      final path = Path()..moveTo(p1.dx, p1.dy)..lineTo(p2.dx, p2.dy)..lineTo(p3.dx, p3.dy)..close();
      canvas.drawPath(path, Paint()..color = HSVColor.fromAHSV(0.7, hue, 0.8, 1).toColor());
      return;
    }
    
    final m1 = Offset((p1.dx + p2.dx) / 2, (p1.dy + p2.dy) / 2);
    final m2 = Offset((p2.dx + p3.dx) / 2, (p2.dy + p3.dy) / 2);
    final m3 = Offset((p3.dx + p1.dx) / 2, (p3.dy + p1.dy) / 2);
    
    _drawSierpinski(canvas, p1, m1, m3, depth - 1);
    _drawSierpinski(canvas, m1, p2, m2, depth - 1);
    _drawSierpinski(canvas, m3, m2, p3, depth - 1);
  }
  
  @override
  bool shouldRepaint(covariant SierpinskiPainter old) => true;
}

class FractalTreeDemo extends StatefulWidget {
  const FractalTreeDemo({super.key});
  @override
  State<FractalTreeDemo> createState() => _FractalTreeDemoState();
}

class _FractalTreeDemoState extends State<FractalTreeDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  int _depth = 9;
  double _angle = 25;
  double _lengthRatio = 0.7;
  double _time = 0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('分形树')),
      body: Stack(children: [
        CustomPaint(painter: FractalTreePainter(_depth, _angle, _lengthRatio, _time), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Row(children: [
          Expanded(child: Column(children: [
            Text('深度: $_depth', style: const TextStyle(color: Colors.white70, fontSize: 11)),
            Slider(value: _depth.toDouble(), min: 1, max: 12, divisions: 11, 
                onChanged: (v) => setState(() => _depth = v.toInt()), activeColor: Colors.green),
          ])),
          Expanded(child: Column(children: [
            Text('角度: ${_angle.toStringAsFixed(0)}°', style: const TextStyle(color: Colors.white70, fontSize: 11)),
            Slider(value: _angle, min: 10, max: 60, 
                onChanged: (v) => setState(() => _angle = v), activeColor: Colors.green),
          ])),
        ]),
        Text('长度比: ${_lengthRatio.toStringAsFixed(2)}', style: const TextStyle(color: Colors.white70, fontSize: 11)),
        Slider(value: _lengthRatio, min: 0.5, max: 0.9, 
            onChanged: (v) => setState(() => _lengthRatio = v), activeColor: Colors.green),
      ]),
    );
  }
}

class FractalTreePainter extends CustomPainter {
  final int depth;
  final double angle;
  final double lengthRatio;
  final double time;
  
  FractalTreePainter(this.depth, this.angle, this.lengthRatio, this.time);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    final startX = size.width / 2;
    final startY = size.height * 0.85;
    final startLength = size.height * 0.25;
    
    _drawBranch(canvas, Offset(startX, startY), -pi / 2, startLength, depth);
  }
  
  void _drawBranch(Canvas canvas, Offset start, double angle, double length, int currentDepth) {
    if (currentDepth == 0 || length < 2) return;
    
    final end = Offset(start.dx + length * cos(angle), start.dy + length * sin(angle));
    
    final hue = (120 + (depth - currentDepth) * 15 + time * 10) % 360;
    final strokeWidth = (currentDepth * 1.5).clamp(1.0, 15.0);
    
    canvas.drawLine(start, end, Paint()
      ..color = HSVColor.fromAHSV(0.8, hue, 0.8, 1).toColor()
      ..strokeWidth = strokeWidth
      ..strokeCap = StrokeCap.round);
    
    final windOffset = sin(time * 2 + start.dy * 0.01) * 0.05;
    final newAngle = angle * 180 / pi;
    
    _drawBranch(canvas, end, (newAngle - this.angle + windOffset * 30) * pi / 180, length * lengthRatio, currentDepth - 1);
    _drawBranch(canvas, end, (newAngle + this.angle + windOffset * 30) * pi / 180, length * lengthRatio, currentDepth - 1);
  }
  
  @override
  bool shouldRepaint(covariant FractalTreePainter old) => true;
}

class BarnsleyFernDemo extends StatefulWidget {
  const BarnsleyFernDemo({super.key});
  @override
  State<BarnsleyFernDemo> createState() => _BarnsleyFernDemoState();
}

class _BarnsleyFernDemoState extends State<BarnsleyFernDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  int _points = 50000;
  double _time = 0;
  List<Offset> _fernPoints = [];

  @override
  void initState() {
    super.initState();
    _generateFern();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }
  
  void _generateFern() {
    _fernPoints = [];
    double x = 0, y = 0;
    final random = Random(42);
    
    for (int i = 0; i < _points; i++) {
      final r = random.nextDouble();
      double newX, newY;
      
      if (r < 0.01) {
        newX = 0;
        newY = 0.16 * y;
      } else if (r < 0.86) {
        newX = 0.85 * x + 0.04 * y;
        newY = -0.04 * x + 0.85 * y + 1.6;
      } else if (r < 0.93) {
        newX = 0.2 * x - 0.26 * y;
        newY = 0.23 * x + 0.22 * y + 1.6;
      } else {
        newX = -0.15 * x + 0.28 * y;
        newY = 0.26 * x + 0.24 * y + 0.44;
      }
      
      x = newX;
      y = newY;
      _fernPoints.add(Offset(x, y));
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('巴恩斯利蕨')),
      body: Stack(children: [
        CustomPaint(painter: BarnsleyFernPainter(_fernPoints, _time), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Text('点数: $_points', style: const TextStyle(color: Colors.white70, fontSize: 12)),
        Slider(value: _points.toDouble(), min: 10000, max: 100000, divisions: 9, 
            onChanged: (v) {
              setState(() => _points = v.toInt());
              _generateFern();
            }, activeColor: Colors.lightGreen),
      ]),
    );
  }
}

class BarnsleyFernPainter extends CustomPainter {
  final List<Offset> fernPoints;
  final double time;
  
  BarnsleyFernPainter(this.fernPoints, this.time);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    final scale = min(size.width, size.height) / 12;
    final offsetX = size.width / 2;
    final offsetY = size.height * 0.95;
    
    for (int i = 0; i < fernPoints.length; i++) {
      final p = fernPoints[i];
      final x = offsetX + p.dx * scale;
      final y = offsetY - p.dy * scale;
      
      final hue = (120 + p.dy * 10 + time * 5) % 360;
      final alpha = 0.3 + (i / fernPoints.length) * 0.5;
      
      canvas.drawCircle(Offset(x, y), 1, Paint()..color = HSVColor.fromAHSV(alpha, hue, 0.8, 0.8).toColor());
    }
  }
  
  @override
  bool shouldRepaint(covariant BarnsleyFernPainter old) => true;
}

class FractalAnimationDemo extends StatefulWidget {
  const FractalAnimationDemo({super.key});
  @override
  State<FractalAnimationDemo> createState() => _FractalAnimationDemoState();
}

class _FractalAnimationDemoState extends State<FractalAnimationDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _time = 0;
  int _fractalType = 0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('分形动画')),
      body: Stack(children: [
        CustomPaint(painter: FractalAnimationPainter(_time, _fractalType), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Text('分形类型', style: const TextStyle(color: Colors.white70, fontSize: 12)),
        const SizedBox(height: 8),
        Wrap(spacing: 8, children: [
          ActionChip(label: const Text('螺旋', style: TextStyle(color: Colors.white, fontSize: 11)),
              backgroundColor: _fractalType == 0 ? Colors.pink : Colors.pink.withOpacity(0.3),
              onPressed: () => setState(() => _fractalType = 0)),
          ActionChip(label: const Text('漩涡', style: TextStyle(color: Colors.white, fontSize: 11)),
              backgroundColor: _fractalType == 1 ? Colors.pink : Colors.pink.withOpacity(0.3),
              onPressed: () => setState(() => _fractalType = 1)),
          ActionChip(label: const Text('星系', style: TextStyle(color: Colors.white, fontSize: 11)),
              backgroundColor: _fractalType == 2 ? Colors.pink : Colors.pink.withOpacity(0.3),
              onPressed: () => setState(() => _fractalType = 2)),
        ]),
      ]),
    );
  }
}

class FractalAnimationPainter extends CustomPainter {
  final double time;
  final int fractalType;
  
  FractalAnimationPainter(this.time, this.fractalType);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF050510));
    
    final center = Offset(size.width / 2, size.height / 2);
    final scale = min(size.width, size.height) * 0.4;
    
    switch (fractalType) {
      case 0:
        _drawSpiralFractal(canvas, center, scale);
        break;
      case 1:
        _drawVortexFractal(canvas, center, scale);
        break;
      case 2:
        _drawGalaxyFractal(canvas, center, scale);
        break;
    }
  }
  
  void _drawSpiralFractal(Canvas canvas, Offset center, double scale) {
    for (int i = 0; i < 500; i++) {
      final t = i / 500;
      final angle = t * 10 * pi + time;
      final r = t * scale;
      
      final x = center.dx + r * cos(angle);
      final y = center.dy + r * sin(angle);
      
      final hue = (t * 360 + time * 50) % 360;
      final alpha = 0.3 + t * 0.5;
      
      canvas.drawCircle(Offset(x, y), 2 + t * 3, Paint()..color = HSVColor.fromAHSV(alpha, hue, 0.8, 1).toColor());
    }
  }
  
  void _drawVortexFractal(Canvas canvas, Offset center, double scale) {
    for (int ring = 0; ring < 20; ring++) {
      final ringRadius = (ring / 20) * scale;
      final pointsInRing = 20 + ring * 5;
      
      for (int i = 0; i < pointsInRing; i++) {
        final angle = (i / pointsInRing) * 2 * pi + time * (1 + ring * 0.1);
        final r = ringRadius + sin(time * 3 + ring) * 10;
        
        final x = center.dx + r * cos(angle);
        final y = center.dy + r * sin(angle);
        
        final hue = (ring * 18 + time * 30) % 360;
        canvas.drawCircle(Offset(x, y), 2, Paint()..color = HSVColor.fromAHSV(0.7, hue, 0.9, 1).toColor());
      }
    }
  }
  
  void _drawGalaxyFractal(Canvas canvas, Offset center, double scale) {
    final arms = 4;
    for (int arm = 0; arm < arms; arm++) {
      final armAngle = (arm / arms) * 2 * pi;
      
      for (int i = 0; i < 200; i++) {
        final t = i / 200;
        final spiralAngle = armAngle + t * 4 * pi + time * 0.5;
        final r = t * scale;
        
        final spread = sin(t * 20 + time * 2) * 20 * t;
        final x = center.dx + r * cos(spiralAngle) + spread * cos(spiralAngle + pi / 2);
        final y = center.dy + r * sin(spiralAngle) + spread * sin(spiralAngle + pi / 2);
        
        final hue = (arm * 90 + t * 60 + time * 20) % 360;
        final alpha = 0.2 + (1 - t) * 0.6;
        
        canvas.drawCircle(Offset(x, y), 1 + (1 - t) * 2, Paint()..color = HSVColor.fromAHSV(alpha, hue, 0.8, 1).toColor());
      }
    }
    
    for (int i = 0; i < 100; i++) {
      final angle = (i / 100) * 2 * pi;
      final r = 20 + sin(time * 5 + i * 0.5) * 10;
      final x = center.dx + r * cos(angle);
      final y = center.dy + r * sin(angle);
      canvas.drawCircle(Offset(x, y), 1, Paint()..color = Colors.white.withOpacity(0.5));
    }
  }
  
  @override
  bool shouldRepaint(covariant FractalAnimationPainter old) => true;
}

🎯 五、总结与展望

📝 5.1 本文要点回顾

复制代码
分形几何核心概念:

1. 自相似性
   - 整体与局部的相似
   - 不同尺度下的重复结构
   - 数学与自然的统一

2. 曼德博集合
   - z = z² + c 迭代
   - 无限复杂的边界
   - 包含所有朱利亚集合

3. 朱利亚集合
   - 固定 c 值的迭代
   - 与曼德博集合的对应关系
   - 丰富的视觉形态

4. 经典分形
   - 科赫雪花:无限周长有限面积
   - 谢尔宾斯基:自相似三角形
   - 巴恩斯利蕨:自然形态模拟

5. 音乐与分形
   - 音乐的分形结构
   - 音频驱动的分形可视化
   - 分形维度与音乐复杂度

🚀 5.2 实际应用场景

复制代码
分形几何的应用领域:

1. 艺术与设计
   - 生成艺术
   - 纹理设计
   - 建筑设计

2. 科学研究
   - 自然现象建模
   - 信号处理
   - 数据压缩

3. 音乐可视化
   - 实时音频分形
   - 音乐视频特效
   - 沉浸式体验

4. 教育工具
   - 数学可视化
   - 概念演示
   - 交互式学习

📚 5.3 扩展阅读

  • 《The Fractal Geometry of Nature》- Benoît Mandelbrot
  • 《Chaos and Fractals: New Frontiers of Science》
  • 曼德博集合在线探索工具
  • 分形音乐生成研究

💡 提示:分形几何是数学与艺术的完美结合,通过简单的迭代规则可以产生无限复杂的美丽图案。在音乐可视化中,分形提供了一种将声音转化为视觉的独特方式。

相关推荐
松叶似针2 小时前
Flutter三方库适配OpenHarmony【doc_text】— OpenHarmony 插件工程搭建与配置文件详解
flutter
阿林来了2 小时前
Flutter三方库适配OpenHarmony【flutter_web_auth】— OAuth2 协议基础与认证流程拆解
flutter
早點睡3904 小时前
Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、贝塞尔流体律动:三阶贝塞尔曲线的“呼吸“感
flutter
2501_921930834 小时前
进阶实战 Flutter for OpenHarmony:响应式状态机系统 - 复杂状态流转实现
flutter
松叶似针4 小时前
Flutter三方库适配OpenHarmony【doc_text】— Word 文档格式深度科普:从 OLE2 到 OOXML
flutter·harmonyos
空白诗4 小时前
Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、粒子系统与流体模拟:动态粒子的视觉盛宴
flutter·harmonyos
空白诗5 小时前
Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、混沌理论与奇异吸引子:从洛伦兹到音乐的动态艺术
flutter·harmonyos
2501_921930835 小时前
进阶实战 Flutter for OpenHarmony:高性能列表虚拟化系统 - 大数据量渲染优化实现
flutter
2501_921930836 小时前
进阶实战 Flutter for OpenHarmony:自定义渲染引擎系统 - RenderObject 底层绘制实现
flutter