it营大地专栏

Flutter使用P5

发布于 3 个月前 作者 sinazl 142 次浏览 来自 分享

Flutter使用P5

install

import 'package:flutter/material.dart';

import "package:p5/p5.dart";
import "sketch.dart";

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: HomePage());
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() {
    return _HomePageState();
  }
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  MySketch sketch;
  PAnimator animator;

  @override
  void initState() {
    super.initState();
    sketch = new MySketch();
    // 需要动画师连续调用草图中的draw()方法,
    // 否则只有在检测到触摸事件时才会调用它。
    animator = new PAnimator(this);
    animator.addListener(() {
      setState(() {
        sketch.redraw();
      });
    });
    animator.run();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: Center(child: PWidget(sketch)));
  }
}

sketch.dart

import 'dart:math' as math;
import 'package:flutter/material.dart';
import "package:p5/p5.dart";

Blob blob;
List<Blob> blobs = [];
double zoom = 1;
double yoff = 0;

/// 从一个范围内映射一个数字去另一个范围。
double map(num v, num start1, num stop1, num start2, num stop2) {
  return (v - start1) / (stop1 - start1) * (stop2 - start2) + start2;
}

var perlin;
const TWO_PI = math.pi * 2;
const PERLIN_YWRAPB = 4;
const PERLIN_YWRAP = 1 << PERLIN_YWRAPB;
const PERLIN_ZWRAPB = 8;
const PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB;
const PERLIN_SIZE = 4095;

var perlinOctaves = 4; // default to medium smooth
var perlinAmpFalloff = 0.5; // 50% reduction/octave

double scaledCosine(double i) {
  return 0.5 * (1.0 - math.cos(i * math.pi));
}

/// 返回所定义坐标的柏林噪声值。柏林噪声是个用来生成比 random() 所能生成更自然及更谐波的随机数字系列。在 1980 年代有 Ken Perlin 所发明,柏林噪声至今常被用在图形应用程序中生成程序纹理、自然运动、形状、地形等等。
double noise(double x, double y, [double z]) {
  y = y ?? 0;
  z = z ?? 0;

  if (perlin == null) {
    perlin = List(PERLIN_SIZE + 1);
    for (var i = 0; i < PERLIN_SIZE + 1; i++) {
      // perlin[i] = Math.random();
      perlin[i] = math.Random().nextDouble();
    }
  }

  if (x < 0) {
    x = -x;
  }
  if (y < 0) {
    y = -y;
  }
  if (z < 0) {
    z = -z;
  }

  var xi = x.floor(), yi = y.floor(), zi = z.floor();
  var xf = x - xi;
  var yf = y - yi;
  var zf = z - zi;
  var rxf, ryf;

  double r = 0;
  var ampl = 0.5;

  var n1, n2, n3;

  for (var o = 0; o < perlinOctaves; o++) {
    var of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB);

    rxf = scaledCosine(xf);
    ryf = scaledCosine(yf);

    n1 = perlin[of & PERLIN_SIZE];
    n1 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n1);
    n2 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];
    n2 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2);
    n1 += ryf * (n2 - n1);

    of += PERLIN_ZWRAP;
    n2 = perlin[of & PERLIN_SIZE];
    n2 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n2);
    n3 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE];
    n3 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3);
    n2 += ryf * (n3 - n2);

    n1 += scaledCosine(zf) * (n2 - n1);

    r += n1 * ampl;
    ampl *= perlinAmpFalloff;
    xi <<= 1;
    xf *= 2;
    yi <<= 1;
    yf *= 2;
    zi <<= 1;
    zf *= 2;

    if (xf >= 1.0) {
      xi++;
      xf--;
    }
    if (yf >= 1.0) {
      yi++;
      yf--;
    }
    if (zf >= 1.0) {
      zi++;
      zf--;
    }
  }
  return r;
}

/// 计算一个介于两个数字之间所定义的插值量位置的数字。amt 参数为两个值之间的插值量,0.0 为第一个值,0.1 为非常接近第一个值,0.5 为两者之间等等。lerp 函数可用来沿着直线制作动画及绘制虚线。
double lerp(double start, double stop, double amt) {
  return amt * (stop - start) + start;
}

class MySketch extends PPainter {
  void setup() {
    // fullScreen();
    size(300, 300);
    blob = Blob(0, 0, 50);
  }

  void draw() {
    background(Colors.black);
    translate(width / 2, height / 2);
    double newzoom = 128 / blob.r;
    zoom = lerp(zoom, newzoom, 0.1);
    scale(zoom, zoom);
    blob.show(this);
  }
}

class Blob {
  double x, y, r;
  Blob(this.x, this.y, this.r);

  show(PPainter p) {
    const double depth = 35;
    p.fill(p.color(0, 255, 0));
    p.noStroke();
    p.push();
    p.beginShape();
    double xoff = 0;
    for (double a = 0; a < TWO_PI; a += 0.001) {
      double offset = map(noise(xoff, yoff), 0, 1, -depth, depth);
      double _r = r + offset;
      double x = _r * math.cos(a);
      double y = _r * math.sin(a);
      p.vertex(x, y);

      xoff += 0.09;
    }
    p.endShape();
    p.pop();
    yoff += 0.02;
  }
}
回到顶部