import 'dart:math' as math; import 'package:flutter/material.dart'; import '../../utils/Colors.dart'; /// Paints the landscape behind [SceneArt]. Everything is derived from the /// palette and the seed, so the same routine always gets the same picture. class ScenePainter extends CustomPainter { final List palette; final int seed; /// 1 → the sun sits in its place; 0 → it is down behind the ridges. final double sunLift; ScenePainter(this.palette, this.seed, {this.sunLift = 1}); @override void paint(Canvas canvas, Size size) { final double w = size.width; final double h = size.height; final math.Random random = math.Random(seed); final Rect bounds = Offset.zero & size; // Sky. canvas.drawRect( bounds, Paint() ..shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [palette[0], palette[1]], stops: const [0.0, 0.62], ).createShader(bounds), ); // Sun, with a wide soft glow. final double radius = math.min(w, h) * (0.13 + random.nextDouble() * 0.05); final Offset sun = Offset( w * (0.18 + random.nextDouble() * 0.64), h * (0.26 + random.nextDouble() * 0.12) + h * 0.5 * (1 - sunLift), ); canvas.drawCircle( sun, radius * 2.6, Paint() ..shader = RadialGradient( colors: [ palette[2].withValues(alpha: 0.55), palette[2].withValues(alpha: 0.0), ], ).createShader(Rect.fromCircle(center: sun, radius: radius * 2.6)), ); canvas.drawCircle(sun, radius, Paint()..color = palette[2]); // A haze band along the horizon so the far ridge sits back. final Rect haze = Rect.fromLTWH(0, h * 0.42, w, h * 0.22); canvas.drawRect( haze, Paint() ..shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ colorWhite.withValues(alpha: 0.0), colorWhite.withValues(alpha: 0.35), colorWhite.withValues(alpha: 0.0), ], ).createShader(haze), ); // Three ridges, far to near. final List baselines = [0.56, 0.68, 0.82]; final List amplitudes = [0.07, 0.06, 0.05]; for (int layer = 0; layer < 3; layer++) { final double base = h * baselines[layer]; final double amp = h * amplitudes[layer]; final double f1 = 0.8 + random.nextDouble() * 1.4; final double f2 = 2.0 + random.nextDouble() * 2.5; final double p1 = random.nextDouble() * math.pi * 2; final double p2 = random.nextDouble() * math.pi * 2; final Path ridge = Path()..moveTo(0, h); const int steps = 36; for (int i = 0; i <= steps; i++) { final double x = w * i / steps; final double t = i / steps; final double y = base - amp * (math.sin(t * f1 * math.pi * 2 + p1) * 0.65 + math.sin(t * f2 * math.pi * 2 + p2) * 0.35); ridge.lineTo(x, y); } ridge ..lineTo(w, h) ..close(); final Color tone = palette[3 + layer]; canvas.drawPath( ridge, Paint() ..shader = LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [tone, Color.lerp(tone, colorBlack, 0.18)!], ).createShader(Rect.fromLTWH(0, base - amp, w, h - base + amp)), ); } } @override bool shouldRepaint(covariant ScenePainter oldDelegate) { return oldDelegate.seed != seed || oldDelegate.palette != palette || oldDelegate.sunLift != sunLift; } }