install
dependencies:
p5: ^0.0.5
main.dart
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;
}
}