import '../contracts/danmaku.dart'; const int kDefaultHeatmapMinComments = 50; const int kDefaultHeatmapBucketCount = 200; class DanmakuHeatmapData { final List buckets; final int bucketCount; final int totalComments; final int maxCount; final bool visible; const DanmakuHeatmapData({ required this.buckets, required this.bucketCount, required this.totalComments, required this.maxCount, required this.visible, }); static const empty = DanmakuHeatmapData( buckets: [], bucketCount: 0, totalComments: 0, maxCount: 0, visible: false, ); } DanmakuHeatmapData computeHeatmap({ required List comments, required double durationSec, int bucketCount = kDefaultHeatmapBucketCount, int minComments = kDefaultHeatmapMinComments, }) { if (bucketCount <= 0) return DanmakuHeatmapData.empty; if (durationSec <= 0) return DanmakuHeatmapData.empty; final counts = List.filled(bucketCount, 0); int total = 0; for (final c in comments) { final t = c.time; if (t < 0 || t > durationSec) continue; var idx = (t / durationSec * bucketCount).floor(); if (idx >= bucketCount) idx = bucketCount - 1; if (idx < 0) idx = 0; counts[idx]++; total++; } if (total < minComments) { return DanmakuHeatmapData( buckets: const [], bucketCount: bucketCount, totalComments: total, maxCount: 0, visible: false, ); } var maxCount = 0; for (final v in counts) { if (v > maxCount) maxCount = v; } if (maxCount <= 0) { return DanmakuHeatmapData( buckets: const [], bucketCount: bucketCount, totalComments: total, maxCount: 0, visible: false, ); } final buckets = List.generate( bucketCount, (i) => counts[i] / maxCount, growable: false, ); return DanmakuHeatmapData( buckets: buckets, bucketCount: bucketCount, totalComments: total, maxCount: maxCount, visible: true, ); } List downsampleHeatmap(List buckets, int targetCount) { if (buckets.isEmpty || targetCount <= 0) return const []; if (targetCount >= buckets.length) return buckets; final out = List.filled(targetCount, 0.0); final src = buckets.length; for (var i = 0; i < targetCount; i++) { final start = (i * src / targetCount).floor(); final end = ((i + 1) * src / targetCount).floor().clamp(start + 1, src); var m = 0.0; for (var j = start; j < end; j++) { if (buckets[j] > m) m = buckets[j]; } out[i] = m; } return out; }