Skip to content

Commit 02c5503

Browse files
committed
feat: bigger, richer constellation asterisms
Add 11 new figures (Cassiopeia, Sagittarius, Pegasus-Andromeda, Lyra, Aquila, Bootes, Taurus, Draco, Hercules, Canis Major, Perseus) and upgrade Cygnus to a fuller Northern Cross, taking the atlas from 6 to 17. Figures were authored over varying sub-ranges of 0..1, so many rendered as a small central patch. fit_to_grid now uniformly rescales each figure to fill the unit square (preserving proportions) before mapping to the grid, and the margin shrinks 5% to 4%, so every asterism covers far more of the frame.
1 parent f415bcf commit 02c5503

1 file changed

Lines changed: 332 additions & 12 deletions

File tree

src/simulation/constellations.rs

Lines changed: 332 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,16 @@ const SCORPIUS: Constellation = Constellation {
104104
const CYGNUS: Constellation = Constellation {
105105
name: "Cygnus",
106106
stars: &[
107-
(0.50, 0.08),
108-
(0.50, 0.48),
109-
(0.50, 0.92),
110-
(0.14, 0.38),
111-
(0.86, 0.54),
112-
(0.30, 0.44),
113-
(0.70, 0.50),
107+
(0.50, 0.00),
108+
(0.50, 0.32),
109+
(0.50, 0.66),
110+
(0.50, 1.00),
111+
(0.18, 0.40),
112+
(0.00, 0.30),
113+
(0.82, 0.52),
114+
(1.00, 0.60),
114115
],
115-
edges: &[(0, 1), (1, 2), (3, 5), (5, 1), (1, 6), (6, 4)],
116+
edges: &[(0, 1), (1, 2), (2, 3), (1, 4), (4, 5), (1, 6), (6, 7)],
116117
};
117118

118119
const LEO: Constellation = Constellation {
@@ -170,25 +171,344 @@ const GEMINI: Constellation = Constellation {
170171
],
171172
};
172173

174+
const CASSIOPEIA: Constellation = Constellation {
175+
name: "Cassiopeia",
176+
stars: &[
177+
(0.00, 0.20),
178+
(0.27, 0.55),
179+
(0.50, 0.00),
180+
(0.74, 0.48),
181+
(1.00, 0.30),
182+
],
183+
edges: &[(0, 1), (1, 2), (2, 3), (3, 4)],
184+
};
185+
186+
const SAGITTARIUS: Constellation = Constellation {
187+
name: "Sagittarius",
188+
stars: &[
189+
(0.00, 0.42),
190+
(0.20, 0.30),
191+
(0.18, 0.62),
192+
(0.40, 0.18),
193+
(0.46, 0.55),
194+
(0.62, 0.00),
195+
(0.66, 0.42),
196+
(0.92, 0.30),
197+
(1.00, 0.62),
198+
],
199+
edges: &[
200+
(0, 1),
201+
(1, 3),
202+
(3, 5),
203+
(5, 6),
204+
(6, 4),
205+
(4, 2),
206+
(2, 0),
207+
(1, 6),
208+
(6, 7),
209+
(7, 8),
210+
(8, 4),
211+
],
212+
};
213+
214+
const PEGASUS_ANDROMEDA: Constellation = Constellation {
215+
name: "Pegasus-Andromeda",
216+
stars: &[
217+
(0.30, 0.62),
218+
(0.30, 1.00),
219+
(0.66, 0.60),
220+
(0.64, 0.98),
221+
(0.50, 0.78),
222+
(0.20, 0.40),
223+
(0.08, 0.20),
224+
(0.00, 0.00),
225+
(0.78, 0.46),
226+
(0.92, 0.34),
227+
(0.46, 0.46),
228+
],
229+
edges: &[
230+
(0, 2),
231+
(2, 3),
232+
(3, 1),
233+
(1, 0),
234+
(0, 4),
235+
(0, 5),
236+
(5, 6),
237+
(6, 7),
238+
(2, 8),
239+
(8, 9),
240+
(2, 10),
241+
],
242+
};
243+
244+
const LYRA: Constellation = Constellation {
245+
name: "Lyra",
246+
stars: &[
247+
(0.15, 0.00),
248+
(0.00, 0.22),
249+
(0.40, 0.30),
250+
(0.30, 0.72),
251+
(0.70, 0.80),
252+
(0.85, 1.00),
253+
(0.62, 0.40),
254+
],
255+
edges: &[
256+
(0, 1),
257+
(0, 2),
258+
(2, 6),
259+
(6, 3),
260+
(3, 4),
261+
(4, 5),
262+
(5, 6),
263+
(1, 3),
264+
],
265+
};
266+
267+
const AQUILA: Constellation = Constellation {
268+
name: "Aquila",
269+
stars: &[
270+
(0.50, 0.00),
271+
(0.36, 0.22),
272+
(0.62, 0.20),
273+
(0.30, 0.48),
274+
(0.70, 0.50),
275+
(0.16, 0.78),
276+
(1.00, 0.60),
277+
(0.00, 1.00),
278+
],
279+
edges: &[(1, 0), (0, 2), (1, 3), (2, 4), (3, 5), (5, 7), (4, 6)],
280+
};
281+
282+
const BOOTES: Constellation = Constellation {
283+
name: "Bootes",
284+
stars: &[
285+
(0.42, 1.00),
286+
(0.18, 0.62),
287+
(0.66, 0.66),
288+
(0.10, 0.30),
289+
(0.74, 0.30),
290+
(0.40, 0.18),
291+
(0.50, 0.00),
292+
],
293+
edges: &[(0, 1), (0, 2), (1, 3), (2, 4), (3, 5), (4, 5), (5, 6)],
294+
};
295+
296+
const TAURUS: Constellation = Constellation {
297+
name: "Taurus",
298+
stars: &[
299+
(0.00, 0.00),
300+
(0.20, 0.30),
301+
(0.40, 0.52),
302+
(0.56, 0.60),
303+
(0.50, 0.78),
304+
(0.72, 0.66),
305+
(1.00, 1.00),
306+
(0.90, 0.20),
307+
],
308+
edges: &[
309+
(0, 1),
310+
(1, 2),
311+
(2, 3),
312+
(3, 4),
313+
(3, 5),
314+
(5, 7),
315+
(5, 6),
316+
(2, 5),
317+
],
318+
};
319+
320+
const DRACO: Constellation = Constellation {
321+
name: "Draco",
322+
stars: &[
323+
(0.92, 1.00),
324+
(0.78, 0.92),
325+
(0.86, 0.80),
326+
(0.70, 0.78),
327+
(0.58, 0.66),
328+
(0.40, 0.62),
329+
(0.30, 0.50),
330+
(0.42, 0.36),
331+
(0.30, 0.22),
332+
(0.46, 0.12),
333+
(0.20, 0.10),
334+
(0.00, 0.00),
335+
(0.16, 0.00),
336+
],
337+
edges: &[
338+
(0, 2),
339+
(2, 1),
340+
(1, 3),
341+
(3, 2),
342+
(3, 4),
343+
(4, 5),
344+
(5, 6),
345+
(6, 7),
346+
(7, 8),
347+
(8, 9),
348+
(8, 10),
349+
(10, 11),
350+
(10, 12),
351+
],
352+
};
353+
354+
const HERCULES: Constellation = Constellation {
355+
name: "Hercules",
356+
stars: &[
357+
(0.30, 0.42),
358+
(0.62, 0.36),
359+
(0.66, 0.66),
360+
(0.36, 0.70),
361+
(0.50, 0.00),
362+
(0.08, 0.30),
363+
(0.00, 0.10),
364+
(0.80, 0.24),
365+
(1.00, 0.30),
366+
(0.22, 0.92),
367+
(0.04, 1.00),
368+
(0.72, 0.96),
369+
(0.92, 1.00),
370+
],
371+
edges: &[
372+
(0, 1),
373+
(1, 2),
374+
(2, 3),
375+
(3, 0),
376+
(0, 5),
377+
(5, 6),
378+
(1, 4),
379+
(1, 7),
380+
(7, 8),
381+
(3, 9),
382+
(9, 10),
383+
(2, 11),
384+
(11, 12),
385+
],
386+
};
387+
388+
const CANIS_MAJOR: Constellation = Constellation {
389+
name: "Canis Major",
390+
stars: &[
391+
(0.30, 0.00),
392+
(0.10, 0.18),
393+
(0.46, 0.24),
394+
(0.00, 0.40),
395+
(0.40, 0.52),
396+
(0.52, 0.78),
397+
(0.78, 0.86),
398+
(1.00, 1.00),
399+
(0.66, 0.58),
400+
],
401+
edges: &[
402+
(0, 1),
403+
(0, 2),
404+
(1, 3),
405+
(2, 4),
406+
(4, 5),
407+
(5, 6),
408+
(6, 7),
409+
(4, 8),
410+
(8, 2),
411+
],
412+
};
413+
414+
const PERSEUS: Constellation = Constellation {
415+
name: "Perseus",
416+
stars: &[
417+
(0.50, 0.00),
418+
(0.40, 0.22),
419+
(0.46, 0.42),
420+
(0.30, 0.56),
421+
(0.16, 0.70),
422+
(0.00, 0.86),
423+
(0.10, 1.00),
424+
(0.60, 0.60),
425+
(0.72, 0.78),
426+
(0.66, 1.00),
427+
(0.86, 0.50),
428+
(1.00, 0.40),
429+
],
430+
edges: &[
431+
(0, 1),
432+
(1, 2),
433+
(2, 3),
434+
(3, 4),
435+
(4, 5),
436+
(4, 6),
437+
(2, 7),
438+
(7, 8),
439+
(8, 9),
440+
(2, 10),
441+
(10, 11),
442+
],
443+
};
444+
173445
/// All shipped figures, in display order.
174-
pub const ALL: &[Constellation] = &[ORION, URSA_MAJOR, SCORPIUS, CYGNUS, LEO, GEMINI];
446+
pub const ALL: &[Constellation] = &[
447+
ORION,
448+
URSA_MAJOR,
449+
SCORPIUS,
450+
CYGNUS,
451+
LEO,
452+
GEMINI,
453+
CASSIOPEIA,
454+
SAGITTARIUS,
455+
PEGASUS_ANDROMEDA,
456+
LYRA,
457+
AQUILA,
458+
BOOTES,
459+
TAURUS,
460+
DRACO,
461+
HERCULES,
462+
CANIS_MAJOR,
463+
PERSEUS,
464+
];
175465

176466
/// Pick one figure using the seeded simulation RNG (deterministic under `--seed`).
177467
pub fn pick(rng: &mut Rng) -> &'static Constellation {
178468
let i = rng.gen_range(0..ALL.len());
179469
&ALL[i]
180470
}
181471

182-
/// Map normalized 0..1 figure coords into grid pixels, centered with a 10%
472+
/// Uniformly rescale + center a figure so its bounding box fills the unit
473+
/// square in its longer dimension, preserving the figure's real proportions.
474+
///
475+
/// Figures are authored over varying sub-ranges of 0..1; without this an
476+
/// asterism that only spans, say, x∈0.30..0.64 would render as a small patch.
477+
/// Normalizing to the bbox makes every figure cover the available area.
478+
fn normalize_to_unit_square(stars: &[(f32, f32)]) -> Vec<(f32, f32)> {
479+
let (mut minx, mut maxx, mut miny, mut maxy) = (f32::MAX, f32::MIN, f32::MAX, f32::MIN);
480+
for &(x, y) in stars {
481+
minx = minx.min(x);
482+
maxx = maxx.max(x);
483+
miny = miny.min(y);
484+
maxy = maxy.max(y);
485+
}
486+
let fw = (maxx - minx).max(1e-6);
487+
let fh = (maxy - miny).max(1e-6);
488+
// Uniform scale by the larger extent keeps the figure undistorted; the
489+
// longer dimension spans exactly 0..1, the shorter is centered.
490+
let s = 1.0 / fw.max(fh);
491+
let (cx, cy) = ((minx + maxx) / 2.0, (miny + maxy) / 2.0);
492+
stars
493+
.iter()
494+
.map(|&(x, y)| (0.5 + (x - cx) * s, 0.5 + (y - cy) * s))
495+
.collect()
496+
}
497+
498+
/// Map a figure's normalized coords into grid pixels, centered with a small
183499
/// margin, scaled so the figure's **visual** aspect matches `aspect`. Halfblock
184500
/// packs 2 vertical px per cell, so a visual ratio W:H needs pixel ratio W:(H*2).
501+
///
502+
/// The figure is first uniformly rescaled to fill the unit square (see
503+
/// [`normalize_to_unit_square`]) so it covers as much of the frame as possible.
185504
pub fn fit_to_grid(
186505
stars: &[(f32, f32)],
187506
width: usize,
188507
height: usize,
189508
aspect: Aspect,
190509
) -> Vec<(f32, f32)> {
191-
const MARGIN: f32 = 0.05;
510+
const MARGIN: f32 = 0.04;
511+
let normalized = normalize_to_unit_square(stars);
192512
let gw = width as f32;
193513
let gh = height as f32;
194514
let avail_w = gw * (1.0 - 2.0 * MARGIN);
@@ -206,7 +526,7 @@ pub fn fit_to_grid(
206526
let off_x = (gw - box_w) / 2.0;
207527
let off_y = (gh - box_h) / 2.0;
208528

209-
stars
529+
normalized
210530
.iter()
211531
.map(|&(nx, ny)| (off_x + nx * box_w, off_y + ny * box_h))
212532
.collect()

0 commit comments

Comments
 (0)