Initial commit
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import '../contracts/danmaku.dart';
|
||||
|
||||
|
||||
const int kDefaultHeatmapMinComments = 50;
|
||||
|
||||
|
||||
const int kDefaultHeatmapBucketCount = 200;
|
||||
|
||||
|
||||
class DanmakuHeatmapData {
|
||||
final List<double> 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: <double>[],
|
||||
bucketCount: 0,
|
||||
totalComments: 0,
|
||||
maxCount: 0,
|
||||
visible: false,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
DanmakuHeatmapData computeHeatmap({
|
||||
required List<DanmakuComment> 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<int>.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 <double>[],
|
||||
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 <double>[],
|
||||
bucketCount: bucketCount,
|
||||
totalComments: total,
|
||||
maxCount: 0,
|
||||
visible: false,
|
||||
);
|
||||
}
|
||||
|
||||
final buckets = List<double>.generate(
|
||||
bucketCount,
|
||||
(i) => counts[i] / maxCount,
|
||||
growable: false,
|
||||
);
|
||||
|
||||
return DanmakuHeatmapData(
|
||||
buckets: buckets,
|
||||
bucketCount: bucketCount,
|
||||
totalComments: total,
|
||||
maxCount: maxCount,
|
||||
visible: true,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
List<double> downsampleHeatmap(List<double> buckets, int targetCount) {
|
||||
if (buckets.isEmpty || targetCount <= 0) return const <double>[];
|
||||
if (targetCount >= buckets.length) return buckets;
|
||||
final out = List<double>.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;
|
||||
}
|
||||
Reference in New Issue
Block a user