在这里插入图片描述

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


🌀 一、Mandelbrot 集:无限复杂的数学之美

📚 1.1 分形几何的历史渊源

分形几何(Fractal Geometry)是由数学家**本华·曼德博(Benoît Mandelbrot)**在 1975 年提出的概念,用于描述自然界中那些传统欧几里得几何无法描述的不规则形状。“分形"一词源自拉丁语 “fractus”,意为"破碎的”、“不规则的”。

历史里程碑

年份 人物 贡献
1879 格奥尔格·康托尔 康托尔集,第一个分形
1904 海里格·冯·科赫 科赫雪花曲线
1905 皮埃尔·法图 研究迭代有理函数
1915 瓦茨瓦夫·谢尔宾斯基 谢尔宾斯基三角形
1918 加斯顿·朱利亚 朱利亚集合研究
1918 费利克斯·豪斯多夫 分形维数概念
1980 本华·曼德博 发现 Mandelbrot 集
1985 Heinz-Otto Peitgen 分形可视化普及

分形的核心特征

特征 描述 数学表达
🔄自相似性 局部与整体相似 f(λx) = λ^D · f(x)
📐分数维数 维数不是整数 D = log(N)/log(S)
♾️无限细节 放大后仍有结构 lim(n→∞) detail
🎯简单规则 复杂来自简单迭代 z_{n+1} = f(z_n)
🌊混沌边缘 有序与无序之间 边界分形

🌿 1.2 自然界中的分形

分形几何是描述自然界的最佳语言,因为自然界中充满了分形结构:

植物界的分形

蕨类植物叶片:
        ┌─────────────┐
        │    ╱╲       │
        │   ╱  ╲      │
        │  ╱ ╱╲ ╲     │
        │ ╱ ╱  ╲ ╲    │
        │╱ ╱╱╲╱╲╲ ╲   │
        │  ╱  ╲  ╲    │
        │ ╱    ╲   ╲  │
        └─────────────┘
每个小叶片都是整体的缩小版

罗马花椰菜:
每个小花都是整个花椰菜的缩小版
分形维数约为 2.7

自然分形实例

对象 分形维数 特点
🌊 海岸线 1.1-1.3 越精细测量越长
❄️ 雪花 ~1.7 六重对称分支
⚡ 闪电 ~1.6 随机分支结构
🌿 蕨叶 ~1.5 自相似叶片
🫁 肺部 ~2.9 分支气管网络
🧬 血管 ~2.7 分形分布
🏔️ 山脉 ~2.2 表面粗糙度

📐 1.3 Mandelbrot 集的数学定义

Mandelbrot 集是复平面上满足特定条件的点集,由一个极其简单的迭代公式定义:

核心迭代公式

z_{n+1} = z_n² + c

其中:
- z_0 = 0(初始值)
- c 是复平面上的点
- 迭代观察 z_n 的行为

详细数学推导

设复数 c = a + bi,z_n = x_n + y_n·i,则迭代公式展开为:

x_{n+1} = x_n² - y_n² + a
y_{n+1} = 2·x_n·y_n + b

初始条件:x_0 = 0, y_0 = 0

判定条件

条件 判断 颜色处理
z_n 保持有界(< 2)
z_n 趋向无穷(≥ 2)

逃逸半径定理

如果 |z_n| > 2,则序列必定趋向无穷

证明:
设 |z| > 2, |c| ≤ 2
|z² + c| ≥ |z|² - |c| > |z|² - |z| = |z|(|z| - 1) > |z|

因此一旦 |z| > 2,序列单调递增,趋向无穷。

Mandelbrot 集的边界

复平面示意:

        Im (虚轴)
         ↑
    1.5  │    ░░░░░░░░░░░░░░░░░░░░░░░░░
         │   ░░░░░░░░░░░░░░░░░░░░░░░░░░░
    1.0  │  ░░░░░░████████████████░░░░░░
         │  ░░░░██████████████████░░░░░░
    0.5  │  ░░░████████████████████░░░░░
         │  ░░██████████████████████░░░░
    0.0  │──░░██████████████████████░░░░───→ Re (实轴)
         │  ░░██████████████████████░░░░
   -0.5  │  ░░░████████████████████░░░░░
         │  ░░░░██████████████████░░░░░░
   -1.0  │  ░░░░░░████████████████░░░░░░
         │   ░░░░░░░░░░░░░░░░░░░░░░░░░░░
   -1.5  │    ░░░░░░░░░░░░░░░░░░░░░░░░░
         │
        -2.0 -1.5 -1.0 -0.5  0  0.5  1.0

        █ = Mandelbrot 集(黑色区域)
        ░ = 逃逸区域(按迭代次数着色)

🔬 1.4 Mandelbrot 集的著名结构

Mandelbrot 集包含许多著名的子结构,每个都有独特的数学性质:

主心脏区域(Main Cardioid)

形状:心形曲线
方程:r = (1 - cos(θ))/2,中心在 (-1/4, 0)
性质:此区域内的点迭代后收敛到固定点

    ╱╲
   ╱  ╲
  ╱    ╲
 ╱      ╲
╱        ╲
╲        ╱
 ╲      ╱
  ╲    ╱
   ╲  ╱
    ╲╱

主圆盘(Main Bulb)

形状:圆形
位置:中心在 (-1, 0),半径 1/4
性质:此区域内的点迭代后收敛到 2-周期轨道

海马谷(Seahorse Valley)

位置:主心脏与主圆盘连接处附近
特点:放大后呈现海马形状的螺旋结构
分形维数:接近 2

     ╱╲
    ╱  ╲
   ╱    ╲
  ╱      ╲
 ╱   ╱╲   ╲
╱   ╱  ╲   ╲
    ╱    ╲
   ╱      ╲

象谷(Elephant Valley)

位置:主心脏顶部
特点:放大后呈现大象鼻子形状的卷曲结构

著名放大位置

名称 坐标 放大倍数 特点
海马谷 (-0.75, 0.1) 10^6 螺旋海马
象谷 (0.28, 0.008) 10^7 卷曲象鼻
三角谷 (-0.16, 1.04) 10^5 三角分形
螺旋臂 (-0.761, -0.085) 10^4 双螺旋
卫星 (-1.75, 0) 10^2 小型复制品

🔗 1.5 Julia 集合与 Mandelbrot 集的关系

Julia 集合是 Mandelbrot 集的"亲戚",两者有着深刻的数学联系:

Julia 集的定义

对于固定的复数 c,Julia 集 J_c 定义为:
- 迭代公式:z_{n+1} = z_n² + c
- 初始值 z_0 在复平面上变化
- 观察 z_n 的行为

与 Mandelbrot 集的区别:
- Mandelbrot:c 变化,z_0 = 0
- Julia:c 固定,z_0 变化

两种集合的关系

特性 Mandelbrot 集 Julia 集
📐参数 c 变化,z₀ = 0 c 固定,z₀ 变化
🎯结果 一个集合 无限多个集合
🔗关系 参数空间 动力系统空间
🎨形状 著名的"海马" 各种对称图案
📊连通性 整体连通 取决于 c 的位置

关键定理(法图-朱利亚定理)

对于复数 c:
- 如果 c ∈ Mandelbrot 集,则 J_c 是连通的
- 如果 c ∉ Mandelbrot 集,则 J_c 是不连通的(法图尘)

这个定理建立了两种集合之间的深刻联系。

Julia 集示例

c = -0.4 + 0.6i(连通的 Julia 集)    c = -0.8 + 0.156i(法图尘)

     ╱╲╱╲╱╲╱╲                           ·  ·   ·   ·
    ╱  ╲  ╱  ╲                        ·   ·  ·    ·   ·
   ╱    ╲╱    ╲                      ·  ·    ·  ·     ·
  ╱      ╲      ╲                    ·    ·  ·    ·   ·
 ╱        ╲      ╲                   ·   ·     ·     ·
╱          ╲      ╲                  ·      ·    ·  ·
              ╱                         ·   ·    ·
             ╱                          ·     ·    ·

🎯 1.6 分形的应用领域

分形几何在众多领域有着广泛的应用:

领域 应用 效果
🎨计算机图形学 纹理生成、地形模拟、云彩渲染 自然景观
📡天线设计 分形天线(Sierpinski、Koch) 多频段接收
📊数据压缩 分形压缩算法 高压缩比
🎵音乐可视化 动态分形演化 视觉震撼
🔬科学建模 复杂系统模拟 自然现象
💰金融分析 股价波动建模 风险评估
🏥医学影像 肿瘤边界分析 诊断辅助
🌐网络科学 互联网拓扑 流量优化

分形天线示例

传统天线 vs 分形天线

传统单极天线:        Sierpinski 分形天线:
    │                     ▲
    │                    ╱ ╲
    │                   ╱   ╲
    │                  ╱ ╲ ╱ ╲
    │                 ╱   ╱   ╲
    │                ╱ ╲ ╱ ╲ ╱ ╲
   ─┴─              ───────────

分形天线优势:
- 多频段工作
- 尺寸更小
- 性能更好

🎵 1.7 分形与音乐的关联

分形与音乐有着天然的联系,许多音乐作品具有分形特征:

音乐中的分形

音乐元素 分形特征 例子
🎼节奏 不同时间尺度的相似模式 非洲鼓乐
🎹旋律 自相似的主题变奏 巴赫赋格
🎸和声 分层的声音结构 电子音乐
🔊音色 分形的频谱结构 钟声、铃声

分形音乐可视化

音频特征 → 分形参数映射

低音 (Bass)    →  缩放深度
中频 (Mid)     →  迭代次数
高频 (Treble)  →  颜色偏移
能量 (Energy)  →  动画速度
节拍 (Beat)    →  脉冲效果

🔧 二、Mandelbrot 集的 Dart 实现

🧮 2.1 复数运算基础

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

/// 复数类 - 分形计算的基础
class Complex {
  final double real;
  final double imaginary;
  
  const Complex(this.real, this.imaginary);
  
  /// 零复数
  static const Complex zero = Complex(0, 0);
  
  /// 单位复数
  static const Complex one = Complex(1, 0);
  
  /// 虚数单位
  static const Complex i = Complex(0, 1);
  
  /// 从极坐标创建
  factory Complex.fromPolar(double r, double theta) {
    return Complex(r * cos(theta), r * sin(theta));
  }
  
  /// 加法
  Complex operator +(Complex other) => Complex(
    real + other.real,
    imaginary + other.imaginary,
  );
  
  /// 减法
  Complex operator -(Complex other) => Complex(
    real - other.real,
    imaginary - other.imaginary,
  );
  
  /// 乘法
  Complex operator *(Complex other) => Complex(
    real * other.real - imaginary * other.imaginary,
    real * other.imaginary + imaginary * other.real,
  );
  
  /// 标量乘法
  Complex operator *(double scalar) => Complex(
    real * scalar,
    imaginary * scalar,
  );
  
  /// 除法
  Complex operator /(Complex other) {
    final denominator = other.real * other.real + other.imaginary * other.imaginary;
    return Complex(
      (real * other.real + imaginary * other.imaginary) / denominator,
      (imaginary * other.real - real * other.imaginary) / denominator,
    );
  }
  
  /// 模的平方
  double get magnitudeSquared => real * real + imaginary * imaginary;
  
  /// 模
  double get magnitude => sqrt(magnitudeSquared);
  
  /// 幅角
  double get argument => atan2(imaginary, real);
  
  /// 共轭
  Complex get conjugate => Complex(real, -imaginary);
  
  /// 平方
  Complex get squared => this * this;
  
  /// 立方
  Complex get cubed => this * this * this;
  
  /// n 次方
  Complex pow(int n) {
    if (n == 0) return one;
    if (n == 1) return this;
    if (n < 0) return one / pow(-n);
  
    Complex result = one;
    for (int i = 0; i < n; i++) {
      result = result * this;
    }
    return result;
  }
  
  /// 平方根
  Complex get sqrt {
    final r = magnitude;
    final theta = argument / 2;
    return Complex.fromPolar(sqrt(r), theta);
  }
  
  /// 指数 e^z
  Complex get exp => Complex.fromPolar(exp(real), imaginary);
  
  /// 自然对数 ln(z)
  Complex get log => Complex(magnitude.log, argument);
  
  
  String toString() {
    if (imaginary == 0) return real.toStringAsFixed(4);
    if (real == 0) return '${imaginary.toStringAsFixed(4)}i';
    return '$real ${imaginary >= 0 ? '+' : '-'} ${imaginary.abs().toStringAsFixed(4)}i';
  }
  
  
  bool operator ==(Object other) {
    if (other is! Complex) return false;
    return real == other.real && imaginary == other.imaginary;
  }
  
  
  int get hashCode => Object.hash(real, imaginary);
}

⚡ 2.2 Mandelbrot 迭代计算器

/// Mandelbrot 迭代结果
class MandelbrotResult {
  final int iterations;
  final bool escaped;
  final double smoothValue;
  final double finalMagnitude;
  
  const MandelbrotResult({
    required this.iterations,
    required this.escaped,
    required this.smoothValue,
    required this.finalMagnitude,
  });
}

/// Mandelbrot 计算器
class MandelbrotCalculator {
  final int maxIterations;
  final double escapeRadius;
  final double escapeRadiusSquared;
  
  MandelbrotCalculator({
    this.maxIterations = 100,
    this.escapeRadius = 2.0,
  }) : escapeRadiusSquared = escapeRadius * escapeRadius;
  
  /// 计算单个点的迭代结果
  MandelbrotResult calculate(Complex c) {
    Complex z = Complex.zero;
    int iterations = 0;
  
    while (iterations < maxIterations && z.magnitudeSquared < escapeRadiusSquared) {
      z = z.squared + c;
      iterations++;
    }
  
    final escaped = iterations < maxIterations;
    final finalMagnitude = z.magnitude;
  
    // 平滑着色值(使用连续着色算法)
    double smoothValue = 0;
    if (escaped) {
      // 连续着色公式
      // smooth = n + 1 - log(log(|z_n|)) / log(2)
      final logZn = log(finalMagnitudeSquared);
      final nu = log(logZn / log(2)) / log(2);
      smoothValue = iterations + 1 - nu;
    }
  
    return MandelbrotResult(
      iterations: iterations,
      escaped: escaped,
      smoothValue: smoothValue,
      finalMagnitude: finalMagnitude,
    );
  }
  
  /// 使用周期检测优化计算
  MandelbrotResult calculateWithPeriodDetection(Complex c) {
    Complex z = Complex.zero;
    int iterations = 0;
  
    // 周期检测
    Complex zOld = Complex.zero;
    int period = 0;
  
    while (iterations < maxIterations && z.magnitudeSquared < escapeRadiusSquared) {
      z = z.squared + c;
      iterations++;
    
      // 检查是否进入周期轨道
      if ((z - zOld).magnitudeSquared < 1e-10) {
        // 发现周期轨道,点在集合内
        return MandelbrotResult(
          iterations: maxIterations,
          escaped: false,
          smoothValue: 0,
          finalMagnitude: z.magnitude,
        );
      }
    
      period++;
      if (period > 20) {
        period = 0;
        zOld = z;
      }
    }
  
    final escaped = iterations < maxIterations;
    double smoothValue = 0;
    if (escaped) {
      final logZn = log(z.magnitudeSquared);
      final nu = log(logZn / log(2)) / log(2);
      smoothValue = iterations + 1 - nu;
    }
  
    return MandelbrotResult(
      iterations: iterations,
      escaped: escaped,
      smoothValue: smoothValue,
      finalMagnitude: z.magnitude,
    );
  }
  
  /// 批量计算区域
  Float32List calculateRegion({
    required int width,
    required int height,
    required Complex center,
    required double scale,
    bool usePeriodDetection = false,
  }) {
    final result = Float32List(width * height);
  
    for (int py = 0; py < height; py++) {
      for (int px = 0; px < width; px++) {
        final x = center.real + (px - width / 2) * scale;
        final y = center.imaginary + (py - height / 2) * scale;
      
        final mandelbrotResult = usePeriodDetection
            ? calculateWithPeriodDetection(Complex(x, y))
            : calculate(Complex(x, y));
      
        if (mandelbrotResult.escaped) {
          result[py * width + px] = mandelbrotResult.smoothValue / maxIterations;
        } else {
          result[py * width + px] = -1; // 属于集合内部
        }
      }
    }
  
    return result;
  }
  
  /// 计算边界估计
  static bool isInMainCardioid(Complex c) {
    // 主心脏区域检测
    final q = (c.real - 0.25) * (c.real - 0.25) + c.imaginary * c.imaginary;
    return q * (q + (c.real - 0.25)) <= 0.25 * c.imaginary * c.imaginary;
  }
  
  static bool isInMainBulb(Complex c) {
    // 主圆盘检测
    return (c.real + 1) * (c.real + 1) + c.imaginary * c.imaginary <= 0.0625;
  }
  
  /// 快速计算(跳过已知在集合内的区域)
  MandelbrotResult calculateFast(Complex c) {
    // 快速检测是否在主心脏或主圆盘内
    if (isInMainCardioid(c) || isInMainBulb(c)) {
      return MandelbrotResult(
        iterations: maxIterations,
        escaped: false,
        smoothValue: 0,
        finalMagnitude: 0,
      );
    }
  
    return calculateWithPeriodDetection(c);
  }
}

🎨 2.3 分形着色方案

/// 分形着色器
class FractalColorScheme {
  final String name;
  final List<Color> colors;
  final List<double> positions;
  
  const FractalColorScheme({
    required this.name,
    required this.colors,
    required this.positions,
  });
  
  /// 经典配色
  static const FractalColorScheme classic = FractalColorScheme(
    name: 'Classic',
    colors: [
      Color(0xFF000076),
      Color(0xFF0026B3),
      Color(0xFF0066FF),
      Color(0xFF00B3FF),
      Color(0xFF00FFFF),
      Color(0xFF00FF80),
      Color(0xFF00FF00),
      Color(0xFF80FF00),
      Color(0xFFFFFF00),
      Color(0xFFFF8000),
      Color(0xFFFF0000),
      Color(0xFF800000),
      Color(0xFF000000),
    ],
    positions: [0, 0.05, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.75, 0.85, 0.95, 1.0],
  );
  
  /// 火焰配色
  static const FractalColorScheme fire = FractalColorScheme(
    name: 'Fire',
    colors: [
      Color(0xFF000000),
      Color(0xFF1A0000),
      Color(0xFF330000),
      Color(0xFF4D0000),
      Color(0xFF660000),
      Color(0xFF800000),
      Color(0xFF990000),
      Color(0xFFB30000),
      Color(0xFFCC0000),
      Color(0xFFE60000),
      Color(0xFFFF0000),
      Color(0xFFFF3300),
      Color(0xFFFF6600),
      Color(0xFFFF9900),
      Color(0xFFFFCC00),
      Color(0xFFFFFF00),
      Color(0xFFFFFFFF),
    ],
    positions: [0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.7, 0.8, 0.9, 1.0],
  );
  
  /// 海洋配色
  static const FractalColorScheme ocean = FractalColorScheme(
    name: 'Ocean',
    colors: [
      Color(0xFF000010),
      Color(0xFF000020),
      Color(0xFF000040),
      Color(0xFF000060),
      Color(0xFF000080),
      Color(0xFF0020A0),
      Color(0xFF0040C0),
      Color(0xFF0060E0),
      Color(0xFF0080FF),
      Color(0xFF00A0FF),
      Color(0xFF00C0FF),
      Color(0xFF00E0FF),
      Color(0xFF40F0FF),
      Color(0xFF80FFFF),
      Color(0xFFC0FFFF),
    ],
    positions: [0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
  );
  
  /// 迷幻配色
  static const FractalColorScheme psychedelic = FractalColorScheme(
    name: 'Psychedelic',
    colors: [
      Color(0xFFFF00FF),
      Color(0xFF00FFFF),
      Color(0xFFFFFF00),
      Color(0xFFFF00FF),
      Color(0xFF00FF00),
      Color(0xFF0000FF),
      Color(0xFFFF0000),
      Color(0xFF00FF00),
      Color(0xFFFF00FF),
    ],
    positions: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0],
  );
  
  /// 极光配色
  static const FractalColorScheme aurora = FractalColorScheme(
    name: 'Aurora',
    colors: [
      Color(0xFF000020),
      Color(0xFF001040),
      Color(0xFF002060),
      Color(0xFF004080),
      Color(0xFF0080A0),
      Color(0xFF00C0C0),
      Color(0xFF00FF80),
      Color(0xFF40FF40),
      Color(0xFF80FF00),
      Color(0xFFC0FF00),
      Color(0xFFFFFF00),
      Color(0xFFFFC000),
      Color(0xFFFF8000),
    ],
    positions: [0, 0.08, 0.15, 0.22, 0.3, 0.38, 0.46, 0.54, 0.62, 0.7, 0.78, 0.88, 1.0],
  );
  
  /// 所有预设配色
  static const List<FractalColorScheme> presets = [
    classic,
    fire,
    ocean,
    psychedelic,
    aurora,
  ];
  
  /// 根据值获取颜色
  Color getColor(double value) {
    if (value < 0) return const Color(0xFF000000);
  
    value = value.clamp(0.0, 1.0);
  
    for (int i = 0; i < positions.length - 1; i++) {
      if (value >= positions[i] && value <= positions[i + 1]) {
        final t = (value - positions[i]) / (positions[i + 1] - positions[i]);
        return Color.lerp(colors[i], colors[i + 1], t)!;
      }
    }
  
    return colors.last;
  }
  
  /// 根据迭代次数和时间获取动态颜色
  Color getAnimatedColor(double value, double time, {double speed = 0.1}) {
    if (value < 0) return const Color(0xFF000000);
  
    // 添加时间偏移实现动画效果
    final animatedValue = (value + time * speed) % 1.0;
    return getColor(animatedValue);
  }
  
  /// 获取带脉冲效果的颜色
  Color getPulsedColor(double value, double time, double pulse) {
    if (value < 0) return const Color(0xFF000000);
  
    final baseColor = getColor(value);
    final pulseFactor = 0.5 + 0.5 * sin(time * 2 + pulse * pi);
  
    return Color.lerp(baseColor, Colors.white, pulseFactor * 0.3)!;
  }
}

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

import 'package:flutter/material.dart';
import 'package:just_audio_ohos/just_audio_ohos.dart';
import 'package:audio_session/audio_session.dart';

/// 分形视图状态
class FractalViewState {
  final Complex center;
  final double scale;
  final int maxIterations;
  final FractalColorScheme colorScheme;
  final double rotation;
  
  const FractalViewState({
    this.center = const Complex(-0.5, 0),
    this.scale = 0.004,
    this.maxIterations = 100,
    this.colorScheme = FractalColorScheme.classic,
    this.rotation = 0,
  });
  
  FractalViewState copyWith({
    Complex? center,
    double? scale,
    int? maxIterations,
    FractalColorScheme? colorScheme,
    double? rotation,
  }) {
    return FractalViewState(
      center: center ?? this.center,
      scale: scale ?? this.scale,
      maxIterations: maxIterations ?? this.maxIterations,
      colorScheme: colorScheme ?? this.colorScheme,
      rotation: rotation ?? this.rotation,
    );
  }
}

/// 音频驱动的分形控制器
class AudioFractalController extends ChangeNotifier {
  final AudioPlayer _player = AudioPlayer();
  final MandelbrotCalculator _calculator = MandelbrotCalculator(maxIterations: 150);
  
  FractalViewState _state = const FractalViewState();
  
  bool _isPlaying = false;
  Duration _position = Duration.zero;
  Duration _duration = Duration.zero;
  
  Float32List _audioData = Float32List(128);
  double _energy = 0;
  double _bass = 0;
  double _mid = 0;
  double _treble = 0;
  
  double _time = 0;
  double _zoomPhase = 0;
  double _colorPhase = 0;
  
  bool get isPlaying => _isPlaying;
  Duration get position => _position;
  Duration get duration => _duration;
  FractalViewState get state => _state;
  Float32List get audioData => _audioData;
  double get energy => _energy;
  double get bass => _bass;
  double get mid => _mid;
  double get treble => _treble;
  AudioPlayer get player => _player;
  double get time => _time;
  MandelbrotCalculator get calculator => _calculator;
  
  /// 初始化
  Future<void> initialize() async {
    final session = await AudioSession.instance;
    await session.configure(const AudioSessionConfiguration.music());
  
    _player.playerStateStream.listen((state) {
      _isPlaying = state.playing;
      notifyListeners();
    });
  
    _player.positionStream.listen((position) {
      _position = position;
      notifyListeners();
    });
  
    _player.durationStream.listen((duration) {
      _duration = duration ?? Duration.zero;
      notifyListeners();
    });
  }
  
  /// 加载网络音频
  Future<void> loadAudio(String url) async {
    try {
      await _player.setUrl(url);
    } catch (e) {
      debugPrint('加载音频失败: $e');
    }
  }
  
  /// 更新
  void update(double dt) {
    _time += dt;
    _zoomPhase += dt;
    _colorPhase += dt * 2;
  
    // 更新音频数据
    _updateAudioData();
  
    // 计算音频特征
    _calculateAudioFeatures();
  
    // 更新分形参数
    _updateFractalParams();
  
    notifyListeners();
  }
  
  void _updateAudioData() {
    final random = Random();
  
    for (int i = 0; i < 128; i++) {
      if (_isPlaying) {
        final freq = (i / 128) * 8 + 1;
        final wave1 = sin(_time * freq) * 0.4;
        final wave2 = sin(_time * freq * 1.5 + pi / 3) * 0.3;
        final noise = (random.nextDouble() - 0.5) * 0.15;
        final bassBoost = i < 32 ? 0.3 : 0;
      
        _audioData[i] = _audioData[i] * 0.85 + 
            (wave1 + wave2 + noise + bassBoost) * 0.15;
      } else {
        _audioData[i] *= 0.95;
      }
    }
  }
  
  void _calculateAudioFeatures() {
    double totalEnergy = 0;
    double bassEnergy = 0;
    double midEnergy = 0;
    double trebleEnergy = 0;
  
    for (int i = 0; i < 128; i++) {
      final value = _audioData[i].abs();
      totalEnergy += value;
    
      if (i < 32) {
        bassEnergy += value;
      } else if (i < 96) {
        midEnergy += value;
      } else {
        trebleEnergy += value;
      }
    }
  
    _energy = totalEnergy / 128;
    _bass = bassEnergy / 32;
    _mid = midEnergy / 64;
    _treble = trebleEnergy / 32;
  }
  
  void _updateFractalParams() {
    // 音频驱动的缩放
    final zoomFactor = 1 - _energy * 0.3;
    final newScale = 0.004 * zoomFactor;
  
    // 音频驱动的迭代次数
    final newIterations = (100 + _energy * 100).toInt();
  
    // 音频驱动的旋转
    final newRotation = _time * 0.1 * (1 + _mid);
  
    _state = _state.copyWith(
      scale: newScale,
      maxIterations: newIterations,
      rotation: newRotation,
    );
  }
  
  /// 缩放
  void zoom(double factor) {
    _state = _state.copyWith(scale: _state.scale * factor);
    notifyListeners();
  }
  
  /// 平移
  void pan(Offset delta) {
    final newCenter = Complex(
      _state.center.real - delta.dx * _state.scale,
      _state.center.imaginary - delta.dy * _state.scale,
    );
    _state = _state.copyWith(center: newCenter);
    notifyListeners();
  }
  
  /// 设置配色方案
  void setColorScheme(FractalColorScheme scheme) {
    _state = _state.copyWith(colorScheme: scheme);
    notifyListeners();
  }
  
  /// 设置中心点
  void setCenter(Complex center) {
    _state = _state.copyWith(center: center);
    notifyListeners();
  }
  
  /// 播放/暂停
  Future<void> togglePlay() async {
    if (_isPlaying) {
      await _player.pause();
    } else {
      await _player.play();
    }
  }
  
  /// 跳转
  Future<void> seek(Duration position) async {
    await _player.seek(position);
  }
  
  
  void dispose() {
    _player.dispose();
    super.dispose();
  }
}

📦 三、完整示例代码

以下是完整的 Mandelbrot 分形音乐可视化示例代码:

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';

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

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

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

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('🌀 Mandelbrot 分形'), backgroundColor: Theme.of(context).colorScheme.inversePrimary),
      body: ListView(padding: const EdgeInsets.all(16), children: [
        _buildCard(context, title: '基础分形', description: '静态 Mandelbrot 集', icon: Icons.grain, color: Colors.deepPurple,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const BasicFractalDemo()))),
        _buildCard(context, title: '动态着色', description: '颜色动画效果', icon: Icons.palette, color: Colors.pink,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const AnimatedColorDemo()))),
        _buildCard(context, title: '交互探索', description: '缩放和平移', icon: Icons.zoom_in, color: Colors.cyan,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const InteractiveFractalDemo()))),
        _buildCard(context, title: '音乐分形', description: '音频驱动的演化', icon: Icons.music_note, color: Colors.orange,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const MusicFractalDemo()))),
        _buildCard(context, title: 'Julia 集', description: 'Julia 集合探索', icon: Icons.blur_on, color: Colors.indigo,
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const JuliaSetDemo()))),
      ]),
    );
  }

  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 Complex {
  final double real, imaginary;
  const Complex(this.real, this.imaginary);
  Complex operator +(Complex o) => Complex(real + o.real, imaginary + o.imaginary);
  Complex operator *(Complex o) => Complex(real * o.real - imaginary * o.imaginary, real * o.imaginary + imaginary * o.real);
  Complex get squared => this * this;
  double get magnitudeSquared => real * real + imaginary * imaginary;
}

/// 基础分形演示
class BasicFractalDemo extends StatefulWidget {
  const BasicFractalDemo({super.key});
  
  State<BasicFractalDemo> createState() => _BasicFractalDemoState();
}

class _BasicFractalDemoState extends State<BasicFractalDemo> {
  int _maxIterations = 100;
  double _centerX = -0.5, _centerY = 0, _scale = 0.004;
  int _colorSchemeIndex = 0;
  
  final List<FractalColorScheme> _colorSchemes = FractalColorScheme.presets;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('基础分形')),
      body: Column(children: [
        Expanded(child: CustomPaint(painter: BasicFractalPainter(_centerX, _centerY, _scale, _maxIterations, _colorSchemes[_colorSchemeIndex]), size: Size.infinite)),
        _buildControls(),
      ]),
    );
  }

  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      color: Colors.black12,
      child: Column(children: [
        Row(children: [
          const Text('迭代: ', style: TextStyle(color: Colors.white)),
          Expanded(child: Slider(value: _maxIterations.toDouble(), min: 20, max: 300, divisions: 28,
              onChanged: (v) => setState(() => _maxIterations = v.toInt()))),
          Text('$_maxIterations', style: const TextStyle(color: Colors.white)),
        ]),
        Row(children: [
          const Text('缩放: ', style: TextStyle(color: Colors.white)),
          Expanded(child: Slider(value: -log(_scale), min: 4, max: 12,
              onChanged: (v) => setState(() => _scale = exp(-v)))),
        ]),
        const SizedBox(height: 8),
        Wrap(spacing: 8, children: List.generate(_colorSchemes.length, (i) => 
          ChoiceChip(label: Text(_colorSchemes[i].name), selected: _colorSchemeIndex == i,
              onSelected: (_) => setState(() => _colorSchemeIndex = i)))),
      ]),
    );
  }
}

class BasicFractalPainter extends CustomPainter {
  final double centerX, centerY, scale;
  final int maxIterations;
  final FractalColorScheme colorScheme;
  
  BasicFractalPainter(this.centerX, this.centerY, this.scale, this.maxIterations, this.colorScheme);
  
  
  void paint(Canvas canvas, Size size) {
    final pixelSize = 2;
    for (int py = 0; py < size.height.toInt(); py += pixelSize) {
      for (int px = 0; px < size.width.toInt(); px += pixelSize) {
        final x = centerX + (px - size.width / 2) * scale;
        final y = centerY + (py - size.height / 2) * scale;
      
        final color = _calculatePoint(x, y);
        canvas.drawRect(Rect.fromLTWH(px.toDouble(), py.toDouble(), pixelSize.toDouble(), pixelSize.toDouble()), Paint()..color = color);
      }
    }
  }
  
  Color _calculatePoint(double cx, double cy) {
    // 快速检测主心脏和主圆盘
    final q = (cx - 0.25) * (cx - 0.25) + cy * cy;
    if (q * (q + (cx - 0.25)) <= 0.25 * cy * cy) {
      return const Color(0xFF000000);
    }
    if ((cx + 1) * (cx + 1) + cy * cy <= 0.0625) {
      return const Color(0xFF000000);
    }
  
    var zr = 0.0, zi = 0.0;
    int iter = 0;
  
    while (iter < maxIterations && zr * zr + zi * zi < 4) {
      final temp = zr * zr - zi * zi + cx;
      zi = 2 * zr * zi + cy;
      zr = temp;
      iter++;
    }
  
    if (iter == maxIterations) return const Color(0xFF000000);
  
    // 平滑着色
    final logZn = log(zr * zr + zi * zi) / 2;
    final nu = log(logZn / log(2)) / log(2);
    final smooth = (iter + 1 - nu) / maxIterations;
  
    return colorScheme.getColor(smooth);
  }
  
  
  bool shouldRepaint(covariant BasicFractalPainter old) => 
      centerX != old.centerX || centerY != old.centerY || scale != old.scale || maxIterations != old.maxIterations;
}

/// 动态着色演示
class AnimatedColorDemo extends StatefulWidget {
  const AnimatedColorDemo({super.key});
  
  State<AnimatedColorDemo> createState() => _AnimatedColorDemoState();
}

class _AnimatedColorDemoState extends State<AnimatedColorDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _time = 0;
  int _colorScheme = 0;
  double _colorSpeed = 1.0;
  double _zoom = 1.0;
  double _centerX = -0.5, _centerY = 0;

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

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('动态着色')),
      body: Stack(children: [
        CustomPaint(
          painter: AnimatedColorPainter(
            time: _time,
            colorScheme: _colorScheme,
            zoom: _zoom,
            centerX: _centerX,
            centerY: _centerY,
          ),
          size: Size.infinite,
        ),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(16)),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(children: [
            const Text('配色方案:', style: TextStyle(color: Colors.white)),
            const SizedBox(width: 10),
            for (int i = 0; i < 4; i++)
              GestureDetector(
                onTap: () => setState(() => _colorScheme = i),
                child: Container(
                  width: 30, height: 30,
                  margin: const EdgeInsets.symmetric(horizontal: 4),
                  decoration: BoxDecoration(
                    color: _colorScheme == i ? Colors.white : Colors.grey[800],
                    borderRadius: BorderRadius.circular(8),
                    border: Border.all(color: _getColorForScheme(i), width: 2),
                  ),
                  child: Center(child: Text('$i', style: TextStyle(color: _getColorForScheme(i), fontSize: 12))),
                ),
              ),
          ]),
          const SizedBox(height: 12),
          Row(children: [
            const Text('速度:', style: TextStyle(color: Colors.white)),
            Expanded(child: Slider(value: _colorSpeed, min: 0.1, max: 3, onChanged: (v) => setState(() => _colorSpeed = v))),
          ]),
          Row(children: [
            const Text('缩放:', style: TextStyle(color: Colors.white)),
            Expanded(child: Slider(value: _zoom, min: 0.5, max: 5, onChanged: (v) => setState(() => _zoom = v))),
          ]),
        ],
      ),
    );
  }
  
  Color _getColorForScheme(int scheme) {
    switch (scheme) {
      case 0: return Colors.deepPurple;
      case 1: return Colors.orange;
      case 2: return Colors.cyan;
      case 3: return Colors.pink;
      default: return Colors.white;
    }
  }
}

class AnimatedColorPainter extends CustomPainter {
  final double time;
  final int colorScheme;
  final double zoom;
  final double centerX, centerY;
  
  AnimatedColorPainter({
    required this.time,
    required this.colorScheme,
    required this.zoom,
    required this.centerX,
    required this.centerY,
  });
  
  
  void paint(Canvas canvas, Size canvasSize) {
    final pixelSize = (3 / zoom).floor().clamp(1, 4);
    final scale = 0.004 / zoom;
    final maxIter = (100 * zoom).floor().clamp(50, 300);
  
    for (int py = 0; py < canvasSize.height; py += pixelSize) {
      for (int px = 0; px < canvasSize.width; px += pixelSize) {
        final x = centerX + (px - canvasSize.width / 2) * scale;
        final y = centerY + (py - canvasSize.height / 2) * scale;
      
        final color = _calculatePoint(x, y, maxIter);
        canvas.drawRect(
          Rect.fromLTWH(px.toDouble(), py.toDouble(), pixelSize.toDouble(), pixelSize.toDouble()),
          Paint()..color = color,
        );
      }
    }
  }
  
  Color _calculatePoint(double cx, double cy, int maxIter) {
    final q = (cx - 0.25) * (cx - 0.25) + cy * cy;
    if (q * (q + (cx - 0.25)) <= 0.25 * cy * cy) return const Color(0xFF000000);
    if ((cx + 1) * (cx + 1) + cy * cy <= 0.0625) return const Color(0xFF000000);
  
    var zr = 0.0, zi = 0.0;
    int iter = 0;
  
    while (iter < maxIter && zr * zr + zi * zi < 256) {
      final temp = zr * zr - zi * zi + cx;
      zi = 2 * zr * zi + cy;
      zr = temp;
      iter++;
    }
  
    if (iter == maxIter) return const Color(0xFF000000);
  
    final logZn = log(sqrt(zr * zr + zi * zi));
    final nu = log(logZn / log(2)) / log(2);
    final smooth = (iter + 1 - nu) / maxIter;
  
    return _getColor(smooth, iter, maxIter);
  }
  
  Color _getColor(double smooth, int iter, int maxIter) {
    final t = smooth.clamp(0.0, 1.0);
  
    switch (colorScheme) {
      case 0:
        final hue = (t * 360 + time * 50) % 360;
        return HSVColor.fromAHSV(1, hue, 0.8, 1).toColor();
      case 1:
        final hue = (t * 180 + time * 30) % 360;
        return HSVColor.fromAHSV(1, hue, 0.9, 0.8 + sin(t * pi + time) * 0.2).toColor();
      case 2:
        final r = ((sin(t * pi * 2 + time) * 0.5 + 0.5) * 255).toInt();
        final g = ((sin(t * pi * 2 + time + pi * 2 / 3) * 0.5 + 0.5) * 255).toInt();
        final b = ((sin(t * pi * 2 + time + pi * 4 / 3) * 0.5 + 0.5) * 255).toInt();
        return Color.fromRGBO(r, g, b, 1);
      case 3:
        final wave = sin(t * pi * 4 + time * 2);
        final hue = (t * 300 + wave * 30 + time * 40) % 360;
        return HSVColor.fromAHSV(1, hue, 0.7 + wave * 0.3, 1).toColor();
      default:
        return HSVColor.fromAHSV(1, t * 360, 0.8, 1).toColor();
    }
  }
  
  
  bool shouldRepaint(covariant AnimatedColorPainter old) => 
      time != old.time || colorScheme != old.colorScheme || zoom != old.zoom;
}

/// 交互探索演示
class InteractiveFractalDemo extends StatefulWidget {
  const InteractiveFractalDemo({super.key});
  
  State<InteractiveFractalDemo> createState() => _InteractiveFractalDemoState();
}

class _InteractiveFractalDemoState extends State<InteractiveFractalDemo> {
  double _centerX = -0.5, _centerY = 0, _scale = 0.004;
  int _maxIterations = 100;
  
  void _handleScaleStart(ScaleStartDetails details) {}
  
  void _handleScaleUpdate(ScaleUpdateDetails details) {
    setState(() {
      _centerX -= details.focalPointDelta.dx * _scale;
      _centerY -= details.focalPointDelta.dy * _scale;
      _scale *= 1 / details.scale;
      _scale = _scale.clamp(1e-15, 0.01);
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('交互探索')),
      body: GestureDetector(
        onScaleStart: _handleScaleStart,
        onScaleUpdate: _handleScaleUpdate,
        child: Stack(children: [
          CustomPaint(painter: BasicFractalPainter(_centerX, _centerY, _scale, _maxIterations, FractalColorScheme.classic), size: Size.infinite),
          Positioned(bottom: 20, left: 20, child: Container(
            padding: const EdgeInsets.all(8),
            decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(8)),
            child: Text('缩放: ${_scale.toStringAsExponential(2)}', style: const TextStyle(color: Colors.white70, fontSize: 12)),
          )),
        ]),
      ),
    );
  }
}

/// 音乐分形演示
class MusicFractalDemo extends StatefulWidget {
  const MusicFractalDemo({super.key});
  
  State<MusicFractalDemo> createState() => _MusicFractalDemoState();
}

class _MusicFractalDemoState extends State<MusicFractalDemo> with TickerProviderStateMixin {
  late AnimationController _animController;
  late AudioPlayer _audioPlayer;
  Float32List _audioData = Float32List(128);
  bool _isPlaying = false;
  Duration _position = Duration.zero;
  Duration _duration = Duration.zero;
  double _energy = 0, _bass = 0, _mid = 0, _high = 0;
  double _time = 0;
  double _centerX = -0.5, _centerY = 0;
  double _zoom = 1.0;
  int _colorMode = 0;
  double _pulseIntensity = 0;
  
  static const String _audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  
  void initState() {
    super.initState();
    _initAudio();
    _animController = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..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));
    _audioPlayer.positionStream.listen((p) => setState(() => _position = p));
    _audioPlayer.durationStream.listen((d) => setState(() => _duration = d ?? Duration.zero));
  
    try { await _audioPlayer.setUrl(_audioUrl); } catch (e) { debugPrint('加载失败: $e'); }
  }
  
  void _update() {
    _time += 0.016;
  
    for (int i = 0; i < 128; i++) {
      if (_isPlaying) {
        final freq = (i / 128) * 12 + 1;
        final phase = _time * freq * 0.5;
        final wave = sin(phase) * 0.35 + sin(phase * 1.618) * 0.25 + sin(phase * 0.618) * 0.2;
        final bassBoost = i < 20 ? 0.4 * sin(_time * 2) : 0;
        final midBoost = i >= 20 && i < 60 ? 0.2 * sin(_time * 4) : 0;
        final highBoost = i >= 60 ? 0.15 * sin(_time * 8) : 0;
        _audioData[i] = _audioData[i] * 0.7 + (wave + bassBoost + midBoost + highBoost) * 0.3;
      } else {
        _audioData[i] *= 0.92;
      }
    }
  
    double total = 0, bassE = 0, midE = 0, highE = 0;
    for (int i = 0; i < 128; i++) {
      total += _audioData[i].abs();
      if (i < 20) bassE += _audioData[i].abs();
      else if (i < 60) midE += _audioData[i].abs();
      else highE += _audioData[i].abs();
    }
  
    final prevEnergy = _energy;
    _energy = total / 128;
    _bass = bassE / 20;
    _mid = midE / 40;
    _high = highE / 48;
  
    if (_energy > prevEnergy * 1.3) {
      _pulseIntensity = (_energy - prevEnergy).clamp(0.0, 1.0);
    }
    _pulseIntensity *= 0.9;
  
    setState(() {});
  }

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('音乐分形')),
      body: Stack(children: [
        CustomPaint(
          painter: MusicFractalPainter(
            time: _time,
            energy: _energy,
            bass: _bass,
            mid: _mid,
            high: _high,
            isPlaying: _isPlaying,
            zoom: _zoom,
            centerX: _centerX,
            centerY: _centerY,
            colorMode: _colorMode,
            pulseIntensity: _pulseIntensity,
            audioData: _audioData,
          ),
          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.deepPurple),
            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.deepPurple : Colors.grey[800],
                borderRadius: BorderRadius.circular(12),
              ),
              child: Text(_isPlaying ? '播放中' : '暂停', style: const TextStyle(color: Colors.white, fontSize: 12)),
            ),
          ]),
          const SizedBox(height: 12),
          Slider(
            value: _duration.inMilliseconds > 0 ? _position.inMilliseconds.toDouble().clamp(0, _duration.inMilliseconds.toDouble()) : 0,
            max: _duration.inMilliseconds > 0 ? _duration.inMilliseconds.toDouble() : 1,
            onChanged: (v) => _audioPlayer.seek(Duration(milliseconds: v.toInt())),
            activeColor: Colors.deepPurple,
          ),
          const SizedBox(height: 8),
          Row(children: [
            IconButton(
              icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.deepPurple, size: 36),
              onPressed: () => _isPlaying ? _audioPlayer.pause() : _audioPlayer.play(),
            ),
            const SizedBox(width: 16),
            Expanded(
              child: Column(children: [
                Row(children: [
                  const Text('缩放:', style: TextStyle(color: Colors.white70, fontSize: 12)),
                  Expanded(child: Slider(value: _zoom, min: 0.5, max: 5, onChanged: (v) => setState(() => _zoom = v), activeColor: Colors.orange)),
                ]),
                Row(children: [
                  const Text('配色:', style: TextStyle(color: Colors.white70, fontSize: 12)),
                  for (int i = 0; i < 4; i++)
                    GestureDetector(
                      onTap: () => setState(() => _colorMode = i),
                      child: Container(
                        width: 24, height: 24,
                        margin: const EdgeInsets.symmetric(horizontal: 4),
                        decoration: BoxDecoration(
                          color: _colorMode == i ? Colors.white : Colors.grey[700],
                          borderRadius: BorderRadius.circular(6),
                          border: Border.all(color: [Colors.deepPurple, Colors.orange, Colors.cyan, Colors.pink][i], width: 2),
                        ),
                      ),
                    ),
                ]),
              ]),
            ),
          ]),
          const SizedBox(height: 8),
          _buildEnergyBars(),
        ],
      ),
    );
  }
  
  Widget _buildEnergyBars() {
    return Row(children: [
      Expanded(child: _buildBar('低频', _bass, Colors.red)),
      const SizedBox(width: 8),
      Expanded(child: _buildBar('中频', _mid, Colors.yellow)),
      const SizedBox(width: 8),
      Expanded(child: _buildBar('高频', _high, Colors.cyan)),
    ]);
  }
  
  Widget _buildBar(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: 40,
        decoration: BoxDecoration(color: Colors.grey[800], borderRadius: BorderRadius.circular(4)),
        child: Align(
          alignment: Alignment.bottomCenter,
          child: AnimatedContainer(
            duration: const Duration(milliseconds: 50),
            height: (value * 40).clamp(2.0, 40.0),
            decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(4)),
          ),
        ),
      ),
    ]);
  }
}

class MusicFractalPainter extends CustomPainter {
  final double time;
  final double energy;
  final double bass;
  final double mid;
  final double high;
  final bool isPlaying;
  final double zoom;
  final double centerX, centerY;
  final int colorMode;
  final double pulseIntensity;
  final Float32List audioData;
  
  MusicFractalPainter({
    required this.time,
    required this.energy,
    required this.bass,
    required this.mid,
    required this.high,
    required this.isPlaying,
    required this.zoom,
    required this.centerX,
    required this.centerY,
    required this.colorMode,
    required this.pulseIntensity,
    required this.audioData,
  });
  
  
  void paint(Canvas canvas, Size canvasSize) {
    final bgColor = Color.lerp(
      const Color(0xFF000015),
      Color.lerp(const Color(0xFF150025), const Color(0xFF001525), mid)!,
      energy * 0.5,
    )!;
    canvas.drawRect(Rect.fromLTWH(0, 0, canvasSize.width, canvasSize.height), Paint()..color = bgColor);
  
    final pixelSize = (2 + (1 - energy) * 2).floor().clamp(1, 4);
    final scale = 0.004 / zoom * (1 - bass * 0.2);
    final maxIter = (80 + energy * 60 + zoom * 20).floor().clamp(50, 200);
  
    final dynamicCenterX = centerX + sin(time * 0.5) * high * 0.1;
    final dynamicCenterY = centerY + cos(time * 0.7) * mid * 0.1;
  
    for (int py = 0; py < canvasSize.height; py += pixelSize) {
      for (int px = 0; px < canvasSize.width; px += pixelSize) {
        final x = dynamicCenterX + (px - canvasSize.width / 2) * scale;
        final y = dynamicCenterY + (py - canvasSize.height / 2) * scale;
      
        final color = _calculatePoint(x, y, maxIter, px.toDouble(), py.toDouble(), canvasSize);
        canvas.drawRect(
          Rect.fromLTWH(px.toDouble(), py.toDouble(), pixelSize.toDouble(), pixelSize.toDouble()),
          Paint()..color = color,
        );
      }
    }
  
    if (isPlaying && pulseIntensity > 0.1) {
      _drawPulseEffect(canvas, canvasSize);
    }
  }
  
  Color _calculatePoint(double cx, double cy, int maxIter, double px, double py, Size canvasSize) {
    final q = (cx - 0.25) * (cx - 0.25) + cy * cy;
    if (q * (q + (cx - 0.25)) <= 0.25 * cy * cy) return const Color(0xFF000000);
    if ((cx + 1) * (cx + 1) + cy * cy <= 0.0625) return const Color(0xFF000000);
  
    var zr = 0.0, zi = 0.0;
    int iter = 0;
  
    final audioIdx = ((px / canvasSize.width) * 64).floor().clamp(0, 63);
    final audioInfluence = audioData[audioIdx] * 0.3;
  
    while (iter < maxIter && zr * zr + zi * zi < 256) {
      final temp = zr * zr - zi * zi + cx + audioInfluence * 0.001;
      zi = 2 * zr * zi + cy;
      zr = temp;
      iter++;
    }
  
    if (iter == maxIter) return const Color(0xFF000000);
  
    final logZn = log(sqrt(zr * zr + zi * zi));
    final nu = log(logZn / log(2)) / log(2);
    final smooth = (iter + 1 - nu) / maxIter;
  
    return _getColor(smooth, iter, maxIter);
  }
  
  Color _getColor(double smooth, int iter, int maxIter) {
    final t = smooth.clamp(0.0, 1.0);
  
    switch (colorMode) {
      case 0:
        final hue = (t * 360 + time * 40 + energy * 80) % 360;
        final saturation = 0.7 + bass * 0.3;
        final value = 0.8 + pulseIntensity * 0.2;
        return HSVColor.fromAHSV(1, hue, saturation, value).toColor();
      case 1:
        final hue = (t * 240 + time * 60 + bass * 60) % 360;
        final wave = sin(t * pi * 6 + time * 3);
        return HSVColor.fromAHSV(1, hue, 0.8 + wave * 0.2, 0.9 + energy * 0.1).toColor();
      case 2:
        final r = ((sin(t * pi * 2 + time * 2) * 0.5 + 0.5 + bass * 0.3).clamp(0.0, 1.0) * 255).toInt();
        final g = ((sin(t * pi * 2 + time * 2 + pi * 2 / 3) * 0.5 + 0.5 + mid * 0.3).clamp(0.0, 1.0) * 255).toInt();
        final b = ((sin(t * pi * 2 + time * 2 + pi * 4 / 3) * 0.5 + 0.5 + high * 0.3).clamp(0.0, 1.0) * 255).toInt();
        return Color.fromRGBO(r, g, b, 1);
      case 3:
        final hue = (t * 300 + time * 30 + sin(time + t * 10) * 30) % 360;
        final pulse = sin(time * 10 + t * 20) * pulseIntensity;
        return HSVColor.fromAHSV(0.9 + pulse * 0.1, hue, 0.85, 1).toColor();
      default:
        return HSVColor.fromAHSV(1, t * 360, 0.8, 1).toColor();
    }
  }
  
  void _drawPulseEffect(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final maxRadius = max(size.width, size.height) * 0.6;
  
    for (int i = 0; i < 3; i++) {
      final radius = (maxRadius * (0.3 + i * 0.2) * (1 + pulseIntensity * 0.5)).clamp(10.0, maxRadius);
      final alpha = (pulseIntensity * 0.3 * (1 - i * 0.3)).clamp(0.0, 1.0);
      final paint = Paint()
        ..color = HSVColor.fromAHSV(alpha, 280 + i * 30, 0.6, 1).toColor()
        ..style = PaintingStyle.stroke
        ..strokeWidth = 2 + pulseIntensity * 3;
      canvas.drawCircle(center, radius, paint);
    }
  }
  
  
  bool shouldRepaint(covariant MusicFractalPainter old) => 
      time != old.time || energy != old.energy || bass != old.bass || 
      zoom != old.zoom || colorMode != old.colorMode || pulseIntensity != old.pulseIntensity;
}

/// Julia 集演示
class JuliaSetDemo extends StatefulWidget {
  const JuliaSetDemo({super.key});
  
  State<JuliaSetDemo> createState() => _JuliaSetDemoState();
}

class _JuliaSetDemoState extends State<JuliaSetDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _time = 0;
  int _presetIndex = 0;
  
  final List<Complex> _presets = [
    const Complex(-0.4, 0.6),
    const Complex(0.285, 0.01),
    const Complex(-0.70176, -0.3842),
    const Complex(-0.8, 0.156),
    const Complex(0.355, 0.355),
  ];
  
  final List<String> _presetNames = ['Dendrite', 'Spiral', 'Dragon', 'Lightning', 'Symmetric'];

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

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Julia 集')),
      body: Column(children: [
        Expanded(child: CustomPaint(painter: JuliaSetPainter(_presets[_presetIndex], _time), size: Size.infinite)),
        Container(
          padding: const EdgeInsets.all(16),
          color: Colors.black12,
          child: Wrap(spacing: 8, children: List.generate(_presets.length, (i) => 
            ChoiceChip(label: Text(_presetNames[i]), selected: _presetIndex == i,
                onSelected: (_) => setState(() => _presetIndex = i)))),
        ),
      ]),
    );
  }
}

class JuliaSetPainter extends CustomPainter {
  final Complex c;
  final double time;
  
  JuliaSetPainter(this.c, this.time);
  
  
  void paint(Canvas canvas, Size size) {
    final pixelSize = 2;
    final scale = 0.004;
    const maxIter = 100;
  
    for (int py = 0; py < size.height.toInt(); py += pixelSize) {
      for (int px = 0; px < size.width.toInt(); px += pixelSize) {
        var zr = (px - size.width / 2) * scale;
        var zi = (py - size.height / 2) * scale;
        int iter = 0;
      
        while (iter < maxIter && zr * zr + zi * zi < 4) {
          final temp = zr * zr - zi * zi + c.real;
          zi = 2 * zr * zi + c.imaginary;
          zr = temp;
          iter++;
        }
      
        if (iter < maxIter) {
          final hue = ((iter + time * 30) % 360).abs();
          final color = HSVColor.fromAHSV(1, hue, 0.8, 1).toColor();
          canvas.drawRect(Rect.fromLTWH(px.toDouble(), py.toDouble(), pixelSize.toDouble(), pixelSize.toDouble()), Paint()..color = color);
        }
      }
    }
  }
  
  
  bool shouldRepaint(covariant JuliaSetPainter old) => time != old.time || c != old.c;
}

📝 四、总结

本篇文章深入探讨了 Mandelbrot 分形在音乐可视化中的应用,从复数运算到音频驱动的动态演化,构建了具有"无限复杂感"的分形动画效果。

✅ 核心知识点回顾

知识点 说明
🌀分形几何 自相似性、分数维数
📐复数运算 加减乘除、模、幅角
迭代计算 Mandelbrot、Julia 集
🎨连续着色 平滑着色算法
🎵音频驱动 能量映射参数

⭐ 最佳实践要点

  • ✅ 使用快速检测跳过主心脏和主圆盘
  • ✅ 周期检测优化计算
  • ✅ 平滑着色避免色带
  • ✅ 音频特征控制分形参数
  • ✅ 多种配色方案切换

🚀 进阶方向

  • 🔮 GPU 着色器加速
  • ✨ 3D 分形渲染
  • 👆 多点触控缩放
  • ⚡ 实时分形视频
  • 🌐 分形动画导出

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐