SpriteSet MakeOuterRing(int s, int e, Vector2 position, float patternRadius, float scale)
        {
            var   bitmap        = GetMapsetBitmap(Sprites.Triangle);
            float triangleWidth = bitmap.Width;

            var       hexagon   = Patterns.Circle2D(position, patternRadius, (float)-Math.PI / 3, 5 * (float)Math.PI / 6);
            SpriteSet triangles = layer.CreateSpriteSet(Sprites.Triangle, _ => OsbOrigin.Centre, hexagon, 6);

            triangles.Fade(s, e, 1.0, 1.0);
            triangles.Rotate(i => - Math.PI / 3 + i * Math.PI / 3);
            triangles.Scale(patternRadius / triangleWidth * scale);
            return(triangles);
        }
        void Section4_2()
        {
            // triangle pattern
            int     s                 = 63468;
            int     e                 = 63939;
            float   patternRadius     = 300;
            float   scale             = 0.6f;
            float   relativeRingScale = 0.5f;
            Vector2 patternPosition   = Vectors.Centre + Vectors.polar(-Math.PI / 6, patternRadius);

            SpriteSet outer = MakeOuterRing(s, e, patternPosition, patternRadius, scale);
            SpriteSet inner = MakeInnerRing(s, e, patternPosition, patternRadius * relativeRingScale, scale);

            SpriteSet extension = layer.CreateSpriteSet();

            for (int i = 0; i < 6; i++)
            {
                var ring = Patterns.Circle2D(
                    patternPosition,
                    patternRadius * 3,
                    (float)-Math.PI / 3,
                    5 * (float)Math.PI / 6
                    );
                extension += MakeOuterRing(s, e, ring(i), patternRadius, scale);
            }
            for (int i = 0; i < 6; i++)
            {
                var ring = Patterns.Circle2D(
                    patternPosition,
                    patternRadius * (float)Math.Sqrt(3),
                    (float)-Math.PI / 3,
                    4 * (float)Math.PI / 6
                    );
                extension += MakeInnerRing(s, e, ring(i), patternRadius * relativeRingScale, scale);
            }

            SpriteSet trianglePattern = inner + outer + extension;

            float zoomScale = 0.9f;
            float cutScale  = 0.9f;

            //trianglePattern.MoveRelative(s, Vector2.Zero);
            trianglePattern.Scale(OsbEasing.None, s, s + 3 * Timing.beat(s) / 2, cutScale, cutScale * zoomScale, Vectors.Centre);
            //trianglePattern.MoveRelative(OsbEasing.None, s, s + 3 * Timing.beat(s) / 2, Vector2.Zero, Vectors.down(patternRadius));
        }
        SpriteSet MakeInnerRing(int s, int e, Vector2 position, float patternRadius, float scale)
        {
            var   bitmap        = GetMapsetBitmap(Sprites.Triangle);
            var   bitmap2       = GetMapsetBitmap(Sprites.Circle);
            float triangleWidth = bitmap.Width;
            float circleWidth   = bitmap2.Width;

            var       hexagon2 = Patterns.Circle2D(position, patternRadius, (float)-Math.PI / 3, 5 * (float)Math.PI / 6);
            SpriteSet circles  = layer.CreateSpriteSet(Sprites.Circle, _ => OsbOrigin.Centre, hexagon2, 6);

            circles.Fade(s, e, 1.0, 1.0);
            circles.Scale(patternRadius / circleWidth * scale * 0.8f);

            float dist = 0.36f;
            Func <int, Vector2> hexagon3       = (int i) => Vector2.Lerp(hexagon2(i), hexagon2(i + 1), i < 6 ? dist : 1 - dist);
            SpriteSet           smallTriangles = layer.CreateSpriteSet(Sprites.Triangle, _ => OsbOrigin.Centre, hexagon3, 12);

            smallTriangles.Fade(s, e, 1.0, 1.0);
            smallTriangles.Scale(patternRadius / triangleWidth * scale * 0.3f);
            smallTriangles.Rotate(i => hexagon3(i).rotationTo(hexagon3(i + 6)), 0, 6);
            smallTriangles.Rotate(i => hexagon3(i).rotationTo(hexagon3(i - 6)), 6, 6);

            return(circles + smallTriangles);
        }