circle_packing 1.0.0 copy "circle_packing: ^1.0.0" to clipboard
circle_packing: ^1.0.0 copied to clipboard

Circle packing algorithm for Flutter - pack circles of varying sizes into a target circular boundary.

example/lib/main.dart

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

void main() => runApp(const App());

class App extends StatelessWidget {
  const App({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: const CirclePackingVizPage(),
    );
  }
}

class CirclePackingVizPage extends StatefulWidget {
  const CirclePackingVizPage({super.key});
  @override
  State<CirclePackingVizPage> createState() => _CirclePackingVizPageState();
}

class _CirclePackingVizPageState extends State<CirclePackingVizPage> {
  final _rng = math.Random(2);
  List<double> _values = [];
  List<Circle> _circles = [];
  double _w = 380, _h = 380;

  @override
  void initState() {
    super.initState();
    _values = _randomValues(18);
    _recompute();
  }

  List<double> _randomValues(int n) {
    return List<double>.generate(n, (_) {
      final u = _rng.nextDouble();
      final v = math.pow(u, 2.2).toDouble();
      return math.max(0.002, v);
    });
  }

  void _recompute() {
    final r = math.min(_w, _h) / 2.0;
    final circles = circlePack(values: _values, targetRadius: r);
    setState(() => _circles = circles);
  }

  @override
  Widget build(BuildContext context) {
    final targetR = math.min(_w, _h) / 2.0;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Circle Packing Visualizer'),
        actions: [
          TextButton(
            onPressed: () {
              setState(() => _values = _randomValues(_values.length));
              _recompute();
            },
            child: const Text('Randomize'),
          ),
        ],
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(12),
            child: Row(
              children: [
                Expanded(
                  child: Slider(
                    value: _w,
                    min: 220,
                    max: 700,
                    onChanged: (v) => setState(() => _w = v),
                    onChangeEnd: (_) => _recompute(),
                    label: 'width ${_w.toStringAsFixed(0)}',
                  ),
                ),
                Expanded(
                  child: Slider(
                    value: _h,
                    min: 220,
                    max: 700,
                    onChanged: (v) => setState(() => _h = v),
                    onChangeEnd: (_) => _recompute(),
                    label: 'height ${_h.toStringAsFixed(0)}',
                  ),
                ),
              ],
            ),
          ),
          Expanded(
            child: Center(
              child: SizedBox(
                width: _w,
                height: _h,
                child: CustomPaint(
                  painter: _CirclesPainter(
                    circles: _circles,
                    targetRadius: targetR,
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _CirclesPainter extends CustomPainter {
  final List<Circle> circles;
  final double targetRadius;

  _CirclesPainter({required this.circles, required this.targetRadius});

  @override
  void paint(Canvas canvas, Size size) {
    final origin = Offset(size.width / 2, size.height / 2);
    canvas.translate(origin.dx, origin.dy);

    final enclosurePaint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;

    canvas.drawCircle(Offset.zero, targetRadius, enclosurePaint);

    final fill = Paint()
      ..style = PaintingStyle.fill
      ..color = const Color(0x22000000);

    final stroke = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1.5;

    for (final c in circles) {
      final center = Offset(c.x, c.y);
      canvas.drawCircle(center, c.r, fill);
      canvas.drawCircle(center, c.r, stroke);
    }
  }

  @override
  bool shouldRepaint(covariant _CirclesPainter oldDelegate) {
    return oldDelegate.targetRadius != targetRadius ||
        oldDelegate.circles.length != circles.length;
  }
}
1
likes
150
points
178
downloads
screenshot

Publisher

unverified uploader

Weekly Downloads

Circle packing algorithm for Flutter - pack circles of varying sizes into a target circular boundary.

Repository (GitHub)
View/report issues

Topics

#visualization #algorithm #circle-packing #data-visualization

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on circle_packing