| 1 | // De avatarhemel: het MODEL, niet de tekening.
|
|---|
| 2 | //
|
|---|
| 3 | // Deze wiskunde bestaat nu drie keer: Swift, Kotlin en hier. Dat is drie keer
|
|---|
| 4 | // kans op drift, en drift zie je niet aan een screenshot — je ziet het als
|
|---|
| 5 | // gezichten op een andere plek hangen dan in de app. Daarom staan hier
|
|---|
| 6 | // dezelfde eigenschappen bewaakt als in AvatarSkyTest.kt: vers hangt dichterbij
|
|---|
| 7 | // dan oud, dezelfde invoer geeft dezelfde uitkomst, en niemand overlapt.
|
|---|
| 8 | import { test } from 'node:test';
|
|---|
| 9 | import assert from 'node:assert/strict';
|
|---|
| 10 |
|
|---|
| 11 | const { orbitPlacement, nearestFace } = await import('../src/assets/js/ward-sky.js');
|
|---|
| 12 |
|
|---|
| 13 | const r = (s) => Math.hypot(s.x, s.y);
|
|---|
| 14 |
|
|---|
| 15 | test('vers hangt dichter bij het midden dan oud', () => {
|
|---|
| 16 | // De afstand tot het midden IS de leeftijd van het laatste bericht. Zou dat
|
|---|
| 17 | // omdraaien, dan leest de hele hemel verkeerd.
|
|---|
| 18 | const spots = orbitPlacement([0, 3600000, 86400000, 30 * 86400000]);
|
|---|
| 19 | assert.ok(r(spots[0]) < r(spots[3]), 'het verste gezicht was niet het oudste');
|
|---|
| 20 | });
|
|---|
| 21 |
|
|---|
| 22 | test('dezelfde invoer geeft dezelfde uitkomst', () => {
|
|---|
| 23 | // Zonder dit springt de hemel bij elke verversing door elkaar en herken je
|
|---|
| 24 | // niemand meer aan zijn plek.
|
|---|
| 25 | const ages = Array.from({ length: 40 }, (_, i) => i * 7200000);
|
|---|
| 26 | assert.deepEqual(orbitPlacement(ages), orbitPlacement(ages));
|
|---|
| 27 | });
|
|---|
| 28 |
|
|---|
| 29 | test('niemand overlapt, ook niet bij gelijke leeftijd', () => {
|
|---|
| 30 | // Twintig gesprekken van exact even oud: zonder de push-apart vallen die op
|
|---|
| 31 | // één punt samen en zie je één gezicht in plaats van twintig.
|
|---|
| 32 | const spots = orbitPlacement(Array(20).fill(3600000));
|
|---|
| 33 | for (let i = 0; i < spots.length; i++) {
|
|---|
| 34 | for (let j = i + 1; j < spots.length; j++) {
|
|---|
| 35 | const d = Math.hypot(spots[j].x - spots[i].x, spots[j].y - spots[i].y);
|
|---|
| 36 | assert.ok(d > 1.0, `gezicht ${i} en ${j} liggen op elkaar (${d.toFixed(3)})`);
|
|---|
| 37 | }
|
|---|
| 38 | }
|
|---|
| 39 | });
|
|---|
| 40 |
|
|---|
| 41 | test('leeg blijft leeg, en één gezicht staat niet in het midden', () => {
|
|---|
| 42 | assert.deepEqual(orbitPlacement([]), []);
|
|---|
| 43 | // Het midden is de deur naar iemand nieuw; daar hoort geen gezicht te staan.
|
|---|
| 44 | const [solo] = orbitPlacement([0]);
|
|---|
| 45 | assert.ok(r(solo) >= 1.4, 'het enige gezicht stond bovenop de (+)');
|
|---|
| 46 | });
|
|---|
| 47 |
|
|---|
| 48 | test('mikken kiest het dichtstbijzijnde gezicht, of niets', () => {
|
|---|
| 49 | const spots = orbitPlacement([0, 86400000, 30 * 86400000]);
|
|---|
| 50 | const view = { cx: 200, cy: 200, cell: 92, scale: 1, panX: 0, panY: 0 };
|
|---|
| 51 | // Precies op het eerste gezicht mikken.
|
|---|
| 52 | const p = { x: view.cx + spots[0].x * 92, y: view.cy + spots[0].y * 92 };
|
|---|
| 53 | assert.equal(nearestFace(p.x, p.y, spots, view), 0);
|
|---|
| 54 | // Ver ernaast is GEEN treffer: liever niets openen dan de verkeerde persoon.
|
|---|
| 55 | assert.equal(nearestFace(-500, -500, spots, view), null);
|
|---|
| 56 | });
|
|---|
| 57 |
|
|---|
| 58 | test('de treffer schaalt mee met de zoom', () => {
|
|---|
| 59 | // Uitgezoomd liggen de gezichten dichter bij elkaar; het mikpunt moet dan
|
|---|
| 60 | // meebewegen, anders open je bij het uitzoomen steeds de verkeerde.
|
|---|
| 61 | const spots = orbitPlacement([0, 3600000, 7200000]);
|
|---|
| 62 | const at = (scale) => {
|
|---|
| 63 | const v = { cx: 300, cy: 300, cell: 92, scale, panX: 0, panY: 0 };
|
|---|
| 64 | return nearestFace(v.cx + spots[1].x * 92 * scale, v.cy + spots[1].y * 92 * scale, spots, v);
|
|---|
| 65 | };
|
|---|
| 66 | assert.equal(at(1), 1);
|
|---|
| 67 | assert.equal(at(0.6), 1);
|
|---|
| 68 | assert.equal(at(1.5), 1);
|
|---|
| 69 | });
|
|---|