content: import 'dart:collection';
  import 'dart:math' as math;
  
  import 'package:material_shapes/src/shapes/shapes.dart';
  import 'package:vector_math/vector_math_64.dart';
  
  /// Holds predefined Material Design shapes as [RoundedPolygon]s that can be
  /// used at various components as they are, or as part of a [Morph].
  ///
  /// Note that each [RoundedPolygon] in this class is normalized.
  ///
  /// https://developer.android.com/images/reference/androidx/compose/material3/shapes.png
  abstract final class MaterialShapes {
    static const _cornerRound15 = CornerRounding(radius: 0.15);
    static const _cornerRound20 = CornerRounding(radius: 0.2);
    static const _cornerRound30 = CornerRounding(radius: 0.3);
    static const _cornerRound50 = CornerRounding(radius: 0.5);
    static const _cornerRound100 = CornerRounding(radius: 1);
  
    static const _negative45Radians = -45 * math.pi / 180;
    static const _negative90Radians = -90 * math.pi / 180;
    static const _negative135Radians = -135 * math.pi / 180;
  
    /// A circle shape.
    static final circle = RoundedPolygon.circle(
      numVertices: 10,
      radius: 0.5,
      centerX: 0.5,
      centerY: 0.5,
    );
  
    /// A square shape.
    static final square = RoundedPolygon.rectangle(
      width: 1,
      height: 1,
      rounding: _cornerRound30,
      centerX: 0.5,
      centerY: 0.5,
    );
  
    /// A slanted square shape.
    static final slanted = _customPolygon(
      const [
        _PointNRound(
          Point(0.926, 0.970),
          CornerRounding(radius: 0.189, smoothing: 0.811),
        ),
        _PointNRound(
          Point(-0.021, 0.967),
          CornerRounding(radius: 0.187, smoothing: 0.057),
        ),
      ],
      2,
    ).normalized();
  
    /// An arch shape.
    static final arch = RoundedPolygon.fromVerticesNum(
      4,
      perVertexRounding: const [
        _cornerRound100,
        _cornerRound100,
        _cornerRound20,
        _cornerRound20,
      ],
    )
        .transformed(
          (Matrix4.identity()..rotateZ(_negative135Radians)).asPointTransformer(),
        )
        .normalized();
  
    /// A semi-circle shape.
    static final semiCircle = RoundedPolygon.rectangle(
      width: 1.6,
      height: 1,
      perVertexRounding: const [
        _cornerRound20,
        _cornerRound20,
        _cornerRound100,
        _cornerRound100,
      ],
    ).normalized();
  
    /// An oval shape.
    static final oval = RoundedPolygon.circle()
        .transformed(
          (Matrix4.identity()
                ..rotateZ(_negative45Radians)
                ..scale(1.0, 0.64))
              .asPointTransformer(),
        )
        .normalized();
  
    /// An pill shape.
    static final pill = _customPolygon(
      [
        const _PointNRound(
          Point(0.961, 0.039),
          CornerRounding(radius: 0.426),
        ),
        const _PointNRound(Point(1.001, 0.428)),
        const _PointNRound(
          Point(1, 0.609),
          CornerRounding(radius: 1),
        ),
      ],
      2,
      mirroring: true,
    ).normalized();
  
    /// A triangle shape.
    static final triangle =
        RoundedPolygon.fromVerticesNum(3, rounding: _cornerRound20)
            .transformed(
              (Matrix4.identity()..rotateZ(_negative90Radians))
                  .asPointTransformer(),
            )
            .normalized();
  
    /// An arrow shape.
    static final arrow = _customPolygon(
      [
        const _PointNRound(
          Point(0.5, 0.892),
          CornerRounding(radius: 0.313),
        ),
        const _PointNRound(
          Point(-0.216, 1.05),
          CornerRounding(radius: 0.207),
        ),
        const _PointNRound(
          Point(0.499, -0.16),
          CornerRounding(radius: 0.215, smoothing: 1),
        ),
        const _PointNRound(
          Point(1.225, 1.06),
          CornerRounding(radius: 0.211),
        ),
      ],
      1,
    ).normalized();
  
    /// A fan shape.
    static final fan = _customPolygon(
      [
        const _PointNRound(
          Point(1.004, 1),
          CornerRounding(radius: 0.148, smoothing: 0.417),
        ),
        const _PointNRound(
          Point(0, 1),
          CornerRounding(radius: 0.151),
        ),
        const _PointNRound(
          Point(0, -0.003),
          CornerRounding(radius: 0.148),
        ),
        const _PointNRound(
          Point(0.978, 0.02),
          CornerRounding(radius: 0.803),
        ),
      ],
      1,
    ).normalized();
  
    /// A diamond shape.
    static final diamond = _customPolygon(
      [
        const _PointNRound(
          Point(0.5, 1.096),
          CornerRounding(radius: 0.151, smoothing: 0.524),
        ),
        const _PointNRound(
          Point(0.04, 0.5),
          CornerRounding(radius: .159),
        ),
      ],
      2,
    ).normalized();
  
    /// A clam-shell shape.
    static final clamShell = _customPolygon(
      [
        const _PointNRound(
          Point(0.171, 0.841),
          CornerRounding(radius: 0.159),
        ),
        const _PointNRound(
          Point(-0.02, 0.5),
          CornerRounding(radius: 0.140),
        ),
        const _PointNRound(
          Point(0.17, 0.159),
          CornerRounding(radius: 0.159),
        ),
      ],
      2,
    ).normalized();
  
    /// A pentagon shape.
    static final pentagon = _customPolygon(
      [
        const _PointNRound(
          Point(0.5, -0.009),
          CornerRounding(radius: 0.172),
        ),
        const _PointNRound(
          Point(1.03, 0.365),
          CornerRounding(radius: 0.164),
        ),
        const _PointNRound(
          Point(0.828, 0.97),
          CornerRounding(radius: 0.169),
        ),
      ],
      1,
      mirroring: true,
    ).normalized();
  
    /// A gem shape.
    static final gem = _customPolygon(
      [
        const _PointNRound(
          Point(0.499, 1.023),
          CornerRounding(radius: 0.241, smoothing: 0.778),
        ),
        const _PointNRound(
          Point(-0.005, 0.792),
          CornerRounding(radius: 0.208),
        ),
        const _PointNRound(
          Point(0.073, 0.258),
          CornerRounding(radius: 0.228),
        ),
        const _PointNRound(
          Point(0.433, -0),
          CornerRounding(radius: 0.491),
        ),
      ],
      1,
      mirroring: true,
    ).normalized();
  
    /// A sunny shape.
    static final sunny = RoundedPolygon.star(
      numVerticesPerRadius: 8,
      innerRadius: 0.8,
      rounding: _cornerRound15,
    ).normalized();
  
    /// A very-sunny shape.
    static final verySunny = _customPolygon(
      [
        const _PointNRound(
          Point(0.5, 1.080),
          CornerRounding(radius: 0.085),
        ),
        const _PointNRound(
          Point(0.358, 0.843),
          CornerRounding(radius: 0.085),
        ),
      ],
      8,
    ).normalized();
  
    /// A 4-sided cookie shape.
    static final cookie4Sided = _customPolygon(
      [
        const _PointNRound(
          Point(1.237, 1.236),
          CornerRounding(radius: 0.258),
        ),
        const _PointNRound(
          Point(0.5, 0.918),
          CornerRounding(radius: 0.233),
        ),
      ],
      4,
    ).normalized();
  
    /// A 6-sided cookie shape.
    static final cookie6Sided = _customPolygon(
      [
        const _PointNRound(
          Point(0.723, 0.884),
          CornerRounding(radius: 0.394),
        ),
        const _PointNRound(
          Point(0.5, 1.099),
          CornerRounding(radius: 0.398),
        ),
      ],
      6,
    ).normalized();
  
    /// A 7-sided cookie shape.
    static final cookie7Sided = RoundedPolygon.star(
      numVerticesPerRadius: 7,
      innerRadius: 0.75,
      rounding: _cornerRound50,
    )
        .transformed(
          (Matrix4.identity()..rotateZ(_negative90Radians)).asPointTransformer(),
        )
        .normalized();
  
    /// A 9-sided cookie shape.
    static final cookie9Sided = RoundedPolygon.star(
      numVerticesPerRadius: 9,
      innerRadius: 0.8,
      rounding: _cornerRound50,
    )
        .transformed(
          (Matrix4.identity()..rotateZ(_negative90Radians)).asPointTransformer(),
        )
        .normalized();
  
    /// A 12-sided cookie shape.
    static final cookie12Sided = RoundedPolygon.star(
      numVerticesPerRadius: 12,
      innerRadius: 0.8,
      rounding: _cornerRound50,
    )
        .transformed(
          (Matrix4.identity()..rotateZ(_negative90Radians)).asPointTransformer(),
        )
        .normalized();
  
    /// A 4-leaf clover shape.
    static final clover4Leaf = _customPolygon(
      [
        const _PointNRound(Point(0.5, 0.074)),
        const _PointNRound(
          Point(0.725, -0.099),
          CornerRounding(radius: 0.476),
        ),
      ],
      4,
      mirroring: true,
    ).normalized();
  
    /// A 8-leaf clover shape.
    static final clover8Leaf = _customPolygon(
      [
        const _PointNRound(Point(0.5, 0.036)),
        const _PointNRound(
          Point(0.758, -0.101),
          CornerRounding(radius: 0.209),
        ),
      ],
      8,
    ).normalized();
  
    /// A burst shape.
    static final burst = _customPolygon(
      [
        const _PointNRound(
          Point(0.5, -0.006),
          CornerRounding(radius: 0.006),
        ),
        const _PointNRound(
          Point(0.592, 0.158),
          CornerRounding(radius: 0.006),
        ),
      ],
      12,
    ).normalized();
  
    /// A soft-burst shape.
    static final softBurst = _customPolygon(
      [
        const _PointNRound(
          Point(0.193, 0.277),
          CornerRounding(radius: 0.053),
        ),
        const _PointNRound(
          Point(0.176, 0.055),
          CornerRounding(radius: 0.053),
        ),
      ],
      10,
    ).normalized();
  
    /// A boom shape.
    static final boom = _customPolygon(
      [
        const _PointNRound(
          Point(0.457, 0.296),
          CornerRounding(radius: 0.007),
        ),
        const _PointNRound(
          Point(0.5, -0.051),
          CornerRounding(radius: 0.007),
        ),
      ],
      15,
    ).normalized();
  
    /// A soft-boom shape.
    static final softBoom = _customPolygon(
      [
        const _PointNRound(Point(0.733, 0.454)),
        const _PointNRound(
          Point(0.839, 0.437),
          CornerRounding(radius: 0.532),
        ),
        const _PointNRound(
          Point(0.949, 0.449),
          CornerRounding(radius: 0.439, smoothing: 1),
        ),
        const _PointNRound(
          Point(0.998, 0.478),
          CornerRounding(radius: 0.174),
        ),
      ],
      16,
      mirroring: true,
    ).normalized();
  
    /// A flower shape.
    static final flower = _customPolygon(
      [
        const _PointNRound(Point(0.370, 0.187)),
        const _PointNRound(
          Point(0.416, 0.049),
          CornerRounding(radius: 0.381),
        ),
        const _PointNRound(
          Point(0.479, 0.001),
          CornerRounding(radius: 0.095),
        ),
      ],
      8,
      mirroring: true,
    ).normalized();
  
    /// A puffy shape.
    static final puffy = _customPolygon(
      [
        const _PointNRound(Point(0.5, 0.053)),
        const _PointNRound(
          Point(0.545, -0.04),
          CornerRounding(radius: 0.405),
        ),
        const _PointNRound(
          Point(0.670, -0.035),
          CornerRounding(radius: 0.426),
        ),
        const _PointNRound(
          Point(0.717, 0.066),
          CornerRounding(radius: 0.574),
        ),
        const _PointNRound(Point(0.722, 0.128)),
        const _PointNRound(
          Point(0.777, 0.002),
          CornerRounding(radius: 0.36),
        ),
        const _PointNRound(
          Point(0.914, 0.149),
          CornerRounding(radius: 0.66),
        ),
        const _PointNRound(
          Point(0.926, 0.289),
          CornerRounding(radius: 0.66),
        ),
        const _PointNRound(Point(0.881, 0.346)),
        const _PointNRound(
          Point(0.940, 0.344),
          CornerRounding(radius: 0.126),
        ),
        const _PointNRound(
          Point(1.003, 0.437),
          CornerRounding(radius: 0.255),
        ),
      ],
      2,
      mirroring: true,
    )
        .transformed(
          (Matrix4.identity()..scale(1.0, 0.742)).asPointTransformer(),
        )
        .normalized();
  
    /// A puffy-diamond shape.
    static final puffyDiamond = _customPolygon(
      [
        const _PointNRound(
          Point(0.87, 0.13),
          CornerRounding(radius: 0.146),
        ),
        const _PointNRound(Point(0.818, 0.357)),
        const _PointNRound(
          Point(1, 0.332),
          CornerRounding(radius: 0.853),
        ),
      ],
      4,
      mirroring: true,
    ).normalized();
  
    /// A ghostish shape.
    static final ghostish = _customPolygon(
      [
        const _PointNRound(
          Point(0.5, 0),
          CornerRounding(radius: 1),
        ),
        const _PointNRound(
          Point(1, 0),
          CornerRounding(radius: 1),
        ),
        const _PointNRound(
          Point(1, 1.14),
          CornerRounding(radius: 0.254, smoothing: 0.106),
        ),
        const _PointNRound(
          Point(0.575, 0.906),
          CornerRounding(radius: 0.253),
        ),
      ],
      1,
      mirroring: true,
    ).normalized();
  
    /// A pixel-circle shape.
    static final pixelCircle = _customPolygon(
      [
        const _PointNRound(Point(0.5, 0)),
        const _PointNRound(Point(0.704, 0)),
        const _PointNRound(Point(0.704, 0.065)),
        const _PointNRound(Point(0.843, 0.065)),
        const _PointNRound(Point(0.843, 0.148)),
        const _PointNRound(Point(0.926, 0.148)),
        const _PointNRound(Point(0.926, 0.296)),
        const _PointNRound(Point(1, 0.296)),
      ],
      2,
      mirroring: true,
    ).normalized();
  
    /// A pixel-triangle shape.
    static final pixelTriangle = _customPolygon(
      [
        const _PointNRound(Point(0.11, 0.5)),
        const _PointNRound(Point(0.113, 0)),
        const _PointNRound(Point(0.287, 0)),
        const _PointNRound(Point(0.287, 0.087)),
        const _PointNRound(Point(0.421, 0.087)),
        const _PointNRound(Point(0.421, 0.17)),
        const _PointNRound(Point(0.56, 0.17)),
        const _PointNRound(Point(0.56, 0.265)),
        const _PointNRound(Point(0.674, 0.265)),
        const _PointNRound(Point(0.675, 0.344)),
        const _PointNRound(Point(0.789, 0.344)),
        const _PointNRound(Point(0.789, 0.439)),
        const _PointNRound(Point(0.888, 0.439)),
      ],
      1,
      mirroring: true,
    ).normalized();
  
    /// A bun shape.
    static final bun = _customPolygon(
      [
        const _PointNRound(Point(0.796, 0.5)),
        const _PointNRound(
          Point(0.853, 0.518),
          CornerRounding(radius: 1),
        ),
        const _PointNRound(
          Point(0.992, 0.631),
          CornerRounding(radius: 1),
        ),
        const _PointNRound(
          Point(0.968, 1),
          CornerRounding(radius: 1),
        ),
      ],
      2,
      mirroring: true,
    ).normalized();
  
    /// A heart shape.
    static final heart = _customPolygon(
      [
        const _PointNRound(
          Point(0.5, 0.268),
          CornerRounding(radius: 0.016),
        ),
        const _PointNRound(
          Point(0.792, -0.066),
          CornerRounding(radius: 0.958),
        ),
        const _PointNRound(
          Point(1.064, 0.276),
          CornerRounding(radius: 1),
        ),
        const _PointNRound(
          Point(0.501, 0.946),
          CornerRounding(radius: 0.129),
        ),
      ],
      1,
      mirroring: true,
    ).normalized();
  
    /// A list of all available shapes.
    static final all = UnmodifiableListView(
      <RoundedPolygon>[
        MaterialShapes.circle,
        MaterialShapes.square,
        MaterialShapes.slanted,
        MaterialShapes.arch,
        MaterialShapes.semiCircle,
        MaterialShapes.oval,
        MaterialShapes.pill,
        MaterialShapes.triangle,
        MaterialShapes.arrow,
        MaterialShapes.fan,
        MaterialShapes.diamond,
        MaterialShapes.clamShell,
        MaterialShapes.pentagon,
        MaterialShapes.gem,
        MaterialShapes.sunny,
        MaterialShapes.verySunny,
        MaterialShapes.cookie4Sided,
        MaterialShapes.cookie6Sided,
        MaterialShapes.cookie7Sided,
        MaterialShapes.cookie9Sided,
        MaterialShapes.cookie12Sided,
        MaterialShapes.clover4Leaf,
        MaterialShapes.clover8Leaf,
        MaterialShapes.burst,
        MaterialShapes.softBurst,
        MaterialShapes.boom,
        MaterialShapes.softBoom,
        MaterialShapes.flower,
        MaterialShapes.puffy,
        MaterialShapes.puffyDiamond,
        MaterialShapes.ghostish,
        MaterialShapes.pixelCircle,
        MaterialShapes.pixelTriangle,
        MaterialShapes.bun,
        MaterialShapes.heart,
      ],
    );
  
    static RoundedPolygon _customPolygon(
      List<_PointNRound> pnr,
      int reps, {
      Point center = const Point(0.5, 0.5),
      bool mirroring = false,
    }) {
      final actualPoints = _doRepeat(pnr, reps, center, mirroring);
  
      final vertices = List<double>.filled(actualPoints.length * 2, 0);
      final perVertexRounding = List<CornerRounding>.filled(
        actualPoints.length,
        CornerRounding.unrounded,
      );
  
      for (var i = 0; i < actualPoints.length; i++) {
        final ap = actualPoints[i];
        perVertexRounding[i] = ap.r;
  
        final j = i * 2;
        vertices[j] = ap.p.x;
        vertices[j + 1] = ap.p.y;
      }
  
      return RoundedPolygon.fromVertices(
        vertices,
        perVertexRounding: perVertexRounding,
        centerX: center.x,
        centerY: center.y,
      );
    }
  
    static List<_PointNRound> _doRepeat(
      List<_PointNRound> points,
      int reps,
      Point center,
      bool mirroring,
    ) {
      final result = <_PointNRound>[];
  
      if (mirroring) {
        final measures = List.generate(points.length, (i) {
          final point = points[i];
          final off = point.p - center;
          return (angle: off.angleRadians, distance: off.getDistance());
        });
        final actualReps = reps * 2;
        final sectionAngle = math.pi * 2 / actualReps;
  
        for (var r = 0; r < actualReps; r++) {
          for (var index = 0; index < points.length; index++) {
            final i = (r.isEven) ? index : points.length - 1 - index;
            if (i > 0 || r.isEven) {
              final a = sectionAngle * r +
                  ((r.isEven)
                      ? measures[i].angle
                      : sectionAngle - measures[i].angle + 2 * measures[0].angle);
  
              final finalPoint = Point(
                        math.cos(a),
                        math.sin(a),
                      ) *
                      measures[i].distance +
                  center;
  
              result.add(_PointNRound(finalPoint, points[i].r));
            }
          }
        }
      } else {
        final np = points.length;
        for (var i = 0; i < np * reps; i++) {
          final point = points[i % np].p.rotate(
                (i ~/ np) * 360 / reps,
                center: center,
              );
          result.add(_PointNRound(point, points[i % np].r));
        }
      }
  
      return result;
    }
  }
  
  class _PointNRound {
    const _PointNRound(
      this.p, [
      this.r = CornerRounding.unrounded,
    ]);
  
    final Point p;
  
    final CornerRounding r;
  }
  