- Neues Layout lib/features/call_ui/inflated_layout.dart: gewichtetes Voronoi-/Potenzdiagramm im Anrufbereich, Zellen fuellen das Fenster bis an die Raender, an Nachbarn plattgedrueckt, weich gerundete Ecken; Lloyd-Relaxation + sanfter Zug ins Raster (aufgeraeumt), Flaechen per Gewicht nachgefuehrt (Sprecher 1,7-fach), Neue pumpen sich auf, Gehende schrumpfen weg, je mehr Personen desto kleiner - Darstellung nur als Kontur mit weichem Innenleuchten, Profilbild mit Kontur + Name in der Mitte, Sprecher gruen pulsierend, Kamera-Bild fuellt die Form - Tests: test/inflated_layout_test.dart (7), Vorschau-/Animationsbilder integration_test/call_bubbles_demo_test.dart; alte Kugel-Physik entfernt - Version 0.1.43 Co-Authored-By: Claude Opus 5.5 <[email protected]>
108 lines
3.9 KiB
Dart
108 lines
3.9 KiB
Dart
import 'dart:ui';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:pyramid/features/call_ui/inflated_layout.dart';
|
|
|
|
/// [seconds] lang simulieren (60 Schritte pro Sekunde).
|
|
bool _run(InflatedLayout l, double seconds) {
|
|
var moving = true;
|
|
for (var i = 0; i < (seconds * 60).round(); i++) {
|
|
moving = l.step(1 / 60);
|
|
}
|
|
return moving;
|
|
}
|
|
|
|
List<String> _ids(int n) => [for (var i = 0; i < n; i++) 'p$i'];
|
|
|
|
void main() {
|
|
const phone = Size(390, 620);
|
|
const desktop = Size(1200, 700);
|
|
|
|
test('eine Person füllt den ganzen Bereich bis an die Ränder', () {
|
|
final l = InflatedLayout()..sync(['solo'], {}, phone);
|
|
_run(l, 1);
|
|
final r = l.cellBounds('solo');
|
|
expect(r.left, closeTo(l.margin, 0.5));
|
|
expect(r.top, closeTo(l.margin, 0.5));
|
|
expect(r.right, closeTo(phone.width - l.margin, 0.5));
|
|
expect(r.bottom, closeTo(phone.height - l.margin, 0.5));
|
|
// Gerundete Ecken: der Umriss enthält die Ecke des Rechtecks nicht mehr.
|
|
final path = l.outline('solo');
|
|
expect(path.contains(r.topLeft + const Offset(2, 2)), isFalse);
|
|
expect(path.contains(r.center), isTrue);
|
|
});
|
|
|
|
test('vier Personen: gleich große Zellen, zusammen fast die ganze Fläche', () {
|
|
final l = InflatedLayout()..sync(_ids(4), {}, desktop);
|
|
final moving = _run(l, 6);
|
|
expect(moving, isFalse, reason: 'kommt nicht zur Ruhe');
|
|
final areas = l.cells.values.map((c) => c.area).toList();
|
|
final avg = areas.reduce((a, b) => a + b) / areas.length;
|
|
for (final a in areas) {
|
|
expect(a, closeTo(avg, avg * 0.08));
|
|
}
|
|
final total = areas.reduce((a, b) => a + b);
|
|
expect(total, greaterThan(l.bounds.width * l.bounds.height * 0.9));
|
|
});
|
|
|
|
test('wer spricht, bekommt deutlich mehr Fläche', () {
|
|
final l = InflatedLayout()..sync(_ids(3), {}, phone);
|
|
_run(l, 4);
|
|
l.sync(_ids(3), {'p1'}, phone);
|
|
_run(l, 5);
|
|
final speaker = l.cells['p1']!.area;
|
|
final other = l.cells['p0']!.area;
|
|
expect(speaker / other,
|
|
closeTo(InflatedLayout.speakingShare, InflatedLayout.speakingShare * 0.15));
|
|
});
|
|
|
|
test('je mehr Personen, desto kleiner die Zellen', () {
|
|
double avgArea(int n) {
|
|
final l = InflatedLayout()..sync(_ids(n), {}, phone);
|
|
_run(l, 5);
|
|
return l.cells.values.fold<double>(0, (s, c) => s + c.area) / n;
|
|
}
|
|
|
|
final a2 = avgArea(2), a5 = avgArea(5), a9 = avgArea(9);
|
|
expect(a5, lessThan(a2));
|
|
expect(a9, lessThan(a5));
|
|
});
|
|
|
|
test('Neue pumpen sich auf, Gehende verschwinden', () {
|
|
final l = InflatedLayout()..sync(_ids(2), {}, phone);
|
|
_run(l, 3);
|
|
l.sync(_ids(3), {}, phone);
|
|
expect(l.cells['p2']!.area, lessThan(l.cells['p0']!.area));
|
|
_run(l, 5);
|
|
expect(l.cells['p2']!.area, closeTo(l.cells['p0']!.area, l.cells['p0']!.area * 0.1));
|
|
l.sync(['p0', 'p2'], {}, phone);
|
|
_run(l, 3);
|
|
expect(l.cells.keys.toSet(), {'p0', 'p2'});
|
|
});
|
|
|
|
test('Zellen überlappen nicht und jeder Mittelpunkt liegt in seiner Zelle', () {
|
|
final l = InflatedLayout()..sync(_ids(7), {'p3'}, desktop);
|
|
_run(l, 6);
|
|
final paths = {for (final id in l.cells.keys) id: l.outline(id)};
|
|
for (final c in l.cells.values) {
|
|
expect(paths[c.id]!.contains(c.centroid), isTrue, reason: '${c.id} Schwerpunkt');
|
|
for (final other in l.cells.values) {
|
|
if (other.id == c.id) continue;
|
|
expect(paths[other.id]!.contains(c.centroid), isFalse,
|
|
reason: '${c.id} liegt in ${other.id}');
|
|
}
|
|
}
|
|
});
|
|
|
|
test('Drehen (hochkant → quer): zwei Personen nebeneinander', () {
|
|
final l = InflatedLayout()..sync(_ids(2), {}, phone);
|
|
_run(l, 3);
|
|
final a = l.cells['p0']!.centroid, b = l.cells['p1']!.centroid;
|
|
expect((a.dy - b.dy).abs(), greaterThan((a.dx - b.dx).abs()), reason: 'hochkant: übereinander');
|
|
l.sync(_ids(2), {}, desktop);
|
|
_run(l, 3);
|
|
final c = l.cells['p0']!.centroid, d = l.cells['p1']!.centroid;
|
|
expect((c.dx - d.dx).abs(), greaterThan((c.dy - d.dy).abs()), reason: 'quer: nebeneinander');
|
|
});
|
|
}
|