Name the neighbours that have a name, before the ones that only have a number
The neighbour ring exists to say where you are. Since the catalogue refresh it has been spending one of its four places in Sol on "Gaia DR3 5853498713190525696" -- a nineteen-digit survey id for the star printed beside it as Proxima Centauri, the same star twice -- and that duplicate row pushed Barnard's Star off the ring altogether. 91.9% of the refreshed catalogue is named that way. Named stars now come first, and survey designations fill in only where fewer than four named ones are in reach. The line between the two is the one the catalogue format already draws: a name is a designation when it is what the star's source would generate for it. Judged by the prefix rather than by rebuilding "prefix id" from the row, because the number after "Gaia DR3" is the survey's own id, which the 32-bit row id cannot hold -- a round trip through the id would have called every one of those stars named. The preference lives on the index as `nearestPreferring`: the preferred pass exhausts the search before the fill runs, so a named star is never outranked by a nearer unnamed one. That is the whole point of asking. The end-to-end spec names Barnard's Star again, on purpose. The four nearest named stars to the Sun are a fact about space, not about which catalogue was refreshed last, and without this change that is exactly the label that vanished -- checked by running the spec with the preference stashed: it fails on that line, and passes with it back. npm test 609/609, npx playwright test 16/16 under CI=true --workers=2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014fcUfL82nvyh9VebX1Fz6w
This commit is contained in:
@@ -74,6 +74,16 @@ describe('StarNeighbourhood', () => {
|
||||
expect(ids(index.nearest(4, 2, (point) => point.id % 2 === 0))).toEqual([2, 6]);
|
||||
});
|
||||
|
||||
it('puts the stars it is told to prefer first, and fills with the rest only when short', () => {
|
||||
const index = new StarNeighbourhood(line(10));
|
||||
const even = (point: StarPoint) => point.id % 2 === 0;
|
||||
|
||||
// Enough even stars: the odd ones next door, though nearer, do not get a look in.
|
||||
expect(ids(index.nearestPreferring(4, 2, even))).toEqual([2, 6]);
|
||||
// Not enough: every even star in reach, then the nearest of the others.
|
||||
expect(ids(index.nearestPreferring(4, 6, even))).toEqual([2, 6, 0, 8, 3, 5]);
|
||||
});
|
||||
|
||||
it('answers nothing for a star it has never heard of', () => {
|
||||
const index = new StarNeighbourhood(line(3));
|
||||
|
||||
|
||||
@@ -70,6 +70,20 @@ export class StarNeighbourhood {
|
||||
return index === undefined ? undefined : this.points[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `nearest`, but the stars `prefer` accepts come first, and the rest only fill what is
|
||||
* left. The preferred pass exhausts the search before the fill runs, so a preferred star is
|
||||
* never outranked by an ordinary one that happens to be closer — that is the point of asking.
|
||||
*/
|
||||
nearestPreferring(id: number, count: number, prefer: (point: StarPoint) => boolean): Neighbour[] {
|
||||
const preferred = this.nearest(id, count, prefer);
|
||||
if (preferred.length >= count) {
|
||||
return preferred;
|
||||
}
|
||||
const taken = new Set(preferred.map((neighbour) => neighbour.id));
|
||||
return preferred.concat(this.nearest(id, count - preferred.length, (point) => !taken.has(point.id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* The `count` stars nearest to `id`, nearest first, excluding the star itself.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { BYTES_PER_STAR_META, BYTES_PER_STAR_POSITION, decodeStarCatalog, encodeStarCatalog } from './star-catalog';
|
||||
import { BYTES_PER_STAR_META, BYTES_PER_STAR_POSITION, decodeStarCatalog, encodeStarCatalog, isDesignation } from './star-catalog';
|
||||
import { StarRecord } from './star.model';
|
||||
|
||||
const STARS: StarRecord[] = [
|
||||
@@ -131,4 +131,13 @@ describe('star catalogue provenance and derived names', () => {
|
||||
expect(named.index.names).toEqual(['Some Proper Name']);
|
||||
expect(decodeStarCatalog(named.index, named.positions, named.meta)[0].name).toBe('Some Proper Name');
|
||||
});
|
||||
|
||||
it('tells a name somebody gave from the designation a survey generates', () => {
|
||||
expect(isDesignation(decoded[0])).toBe(false);
|
||||
expect(isDesignation(decoded[1])).toBe(true);
|
||||
// The real catalogue names Gaia stars by the survey's nineteen-digit source id, which no
|
||||
// 32-bit row id can equal — so it is the prefix that decides, not a round trip through the id.
|
||||
expect(isDesignation({ ...MIXED[1], name: 'Gaia DR3 5853498713190525696' })).toBe(true);
|
||||
expect(isDesignation({ ...MIXED[1], name: 'Proxima Centauri' })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -161,6 +161,17 @@ function designationFor(prefix: string | undefined, id: number): string | undefi
|
||||
return prefix === undefined ? undefined : `${prefix} ${id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a star's name is only the designation its source generates, rather than anything
|
||||
* somebody called it. Judged by the prefix alone: the number after it is the survey's own id —
|
||||
* nineteen digits for Gaia — which the 32-bit row id `designationFor` prints cannot hold, so a
|
||||
* round-trip through the id would call every one of those stars named.
|
||||
*/
|
||||
export function isDesignation(star: StarRecord): boolean {
|
||||
const prefix = star.source === undefined ? 'HYG' : (DESIGNATION_PREFIXES[star.source] ?? star.source);
|
||||
return star.name.startsWith(`${prefix} `);
|
||||
}
|
||||
|
||||
/** Rebuilds the star records the app works with from the three loaded assets. */
|
||||
export function decodeStarCatalog(index: StarCatalogIndex, positions: Float32Array, meta: ArrayBuffer): StarRecord[] {
|
||||
const columns = metaColumns(meta, index.count);
|
||||
|
||||
Reference in New Issue
Block a user