本文讲解使用 Flutter 实现 Generative Artistry 教程里面的第六个图形 Circle Packing(圆形填充)
前言
懒癌发作,好久没写博客了…
本文讲解使用 Flutter 实现 Generative Artistry 教程里面的第六个图形 Circle Packing(圆形填充)。
实现这个图形的思路大致是:
- 创建一个圆。
- 判断这个圆是否与其他圆发生碰撞。
- 没有则不断增大圆的半径,再次进行碰撞检测,直至最大半径。
- 绘制更多的圆形,重复以上。
初始化
照旧创建一个 CirclePacking
控件以及 CirclePackingPainter
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class CirclePacking extends StatelessWidget { @override Widget build(BuildContext context) { return CustomPaint( painter: CirclePackingPainter(), ); } }
class CirclePackingPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) {}
@override bool shouldRepaint(CirclePackingPainter oldDelegate) => true; }
|
然后定义一个 Circle
类,代表绘制的圆形。
1 2 3 4
| class Circle { Point center; double radius; }
|
在声明绘制需要的变量。
1 2 3 4 5 6 7 8 9 10 11 12
| List circles = [];
double minRaidus = 2;
double maxRaidus = 100;
int totalCircles = 500;
int createCircleAttemps = 500;
Random random = Random();
|
创建圆形
使用 _createCircles
方法生成需要绘制的圆形。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void _createCircles(Canvas canvas, Size size) { Circle circle = Circle() ..radius = minRaidus ..center = Point( random.nextDouble() * size.width, random.nextDouble() * size.height, );
for (var i = minRaidus; i < maxRaidus; i++) { circle.radius = i; if (_doesHaveCollision(circle, size)) { circle.radius--; break; } }
circles.add(circle); }
|
使用 _doesHaveCollision
方法进行碰撞检测,目前都返回 false
。
1 2 3
| bool _doesHaveCollision(Circle circle, Size size) { return false; }
|
绘制圆形
创建一个 _drawCircles
方法绘制圆形。
1 2 3 4 5 6 7 8 9 10 11 12
| void _drawCircles(Canvas canvas) { Paint paint = Paint() ..strokeWidth = 0.5 ..isAntiAlias = true ..color = Colors.black ..style = PaintingStyle.stroke;
circles.asMap().forEach((key, circle) { final Offset offset = Offset(circle.center.x, circle.center.y); canvas.drawCircle(offset, circle.radius, paint); }); }
|
在 paint
方法里调用创建和绘制圆形的方法。
1 2 3 4 5 6 7
| @override void paint(Canvas canvas, Size size) { for (var i = 0; i < totalCircles; i++) { _createCircles(canvas, size); _drawCircles(canvas); } }
|
噔噔噔,一团乱麻!
碰撞检测
完善圆形之间的碰撞检测方法 _doesHaveCollision
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| bool _doesHaveCollision(Circle circle, Size size) { for (var i = 0; i < circles.length; i++) { Circle otherCircle = circles[i]; double r2 = circle.radius + otherCircle.radius;
if (r2 >= circle.center.distanceTo(otherCircle.center) - 1) { return true; } }
if (circle.center.x + circle.radius >= size.width || circle.center.x - circle.radius <= 0) { return true; } if (circle.center.y + circle.radius >= size.height || circle.center.y - circle.radius <= 0) { return true; }
return false; }
|
最后需要做的是在创建圆形之前的检测,更新创建圆形的方法 _createCircles
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| void _createCircles(Canvas canvas, Size size) { Circle circle; bool circleSafeToDraw = false;
for (var i = 0; i < createCircleAttemps; i++) { circle = Circle() ..radius = minRaidus ..center = Point( random.nextDouble() * size.width, random.nextDouble() * size.height, );
if (_doesHaveCollision(circle, size)) { continue; } else { circleSafeToDraw = true; break; } }
if (!circleSafeToDraw) { return; }
for (var i = minRaidus; i < maxRaidus; i++) { circle.radius = i; if (_doesHaveCollision(circle, size)) { circle.radius--; break; } }
circles.add(circle); }
|
大功告成 🎉
最终代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
| import 'dart:math';
import 'package:flutter/material.dart';
class Circle { Point center; double radius; }
class CirclePacking extends StatelessWidget { @override Widget build(BuildContext context) { return CustomPaint( painter: CirclePackingPainter(), ); } }
class CirclePackingPainter extends CustomPainter { List circles = []; double minRaidus = 2; double maxRaidus = 100; int totalCircles = 500; int createCircleAttemps = 500; Random random = Random();
void _createCircles(Canvas canvas, Size size) { Circle circle; bool circleSafeToDraw = false;
for (var i = 0; i < createCircleAttemps; i++) { circle = Circle() ..radius = minRaidus ..center = Point( random.nextDouble() * size.width, random.nextDouble() * size.height, );
if (_doesHaveCollision(circle, size)) { continue; } else { circleSafeToDraw = true; break; } }
if (!circleSafeToDraw) { return; }
for (var i = minRaidus; i < maxRaidus; i++) { circle.radius = i; if (_doesHaveCollision(circle, size)) { circle.radius--; break; } }
circles.add(circle); }
bool _doesHaveCollision(Circle circle, Size size) { for (var i = 0; i < circles.length; i++) { Circle otherCircle = circles[i]; double r2 = circle.radius + otherCircle.radius;
if (r2 >= circle.center.distanceTo(otherCircle.center) - 1) { return true; } }
if (circle.center.x + circle.radius >= size.width || circle.center.x - circle.radius <= 0) { return true; } if (circle.center.y + circle.radius >= size.height || circle.center.y - circle.radius <= 0) { return true; }
return false; }
void _drawCircles(Canvas canvas) { Paint paint = Paint() ..strokeWidth = 0.5 ..isAntiAlias = true ..style = PaintingStyle.stroke;
circles.asMap().forEach((key, circle) { paint.color = Colors.black; Offset offset = Offset(circle.center.x, circle.center.y); canvas.drawCircle(offset, circle.radius, paint); }); }
@override void paint(Canvas canvas, Size size) { for (var i = 0; i < totalCircles; i++) { _createCircles(canvas, size); _drawCircles(canvas); } }
@override bool shouldRepaint(CirclePackingPainter oldDelegate) => true; }
|