IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Flutter 绘制图形 Circle Packing

    冷石发表于 2022-08-29 09:01:22
    love 0

    本文讲解使用 Flutter 实现 Generative Artistry 教程里面的第六个图形 Circle Packing(圆形填充)

    前言

    懒癌发作,好久没写博客了…

    本文讲解使用 Flutter 实现 Generative Artistry 教程里面的第六个图形 Circle Packing(圆形填充)。

    实现这个图形的思路大致是:

    1. 创建一个圆。
    2. 判断这个圆是否与其他圆发生碰撞。
    3. 没有则不断增大圆的半径,再次进行碰撞检测,直至最大半径。
    4. 绘制更多的圆形,重复以上。

    初始化

    照旧创建一个 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<Circle> 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,
    );

    // 如果碰撞检测失败跳过这个圆形,进行下一次尝试
    // 否则跳出循环将这个圆形加入 circles 数组
    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<Circle> 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,
    );

    // 如果碰撞检测失败跳过这个圆形,进行下一次尝试
    // 否则跳出循环将这个圆形加入 circles 数组
    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;
    }


沪ICP备19023445号-2号
友情链接