@@ -104,15 +104,16 @@ const SCORPIUS: Constellation = Constellation {
104104const 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
118119const 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`).
177467pub 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.
185504pub 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