@johnmorrisdotca/dougu-sports 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +53 -24
  3. package/dist/emblems/ball/ball.baseball.js +2 -1
  4. package/dist/emblems/ball/ball.contrast.d.ts +4 -0
  5. package/dist/emblems/ball/ball.contrast.js +22 -0
  6. package/dist/emblems/ball/ball.core.d.ts +42 -0
  7. package/dist/emblems/ball/ball.core.js +34 -1
  8. package/dist/emblems/ball/ball.engine.d.ts +70 -4
  9. package/dist/emblems/ball/ball.engine.js +70 -20
  10. package/dist/emblems/ball/ball.family.seamed.d.ts +4 -0
  11. package/dist/emblems/ball/ball.family.seamed.js +42 -7
  12. package/dist/emblems/ball/ball.image.d.ts +8 -0
  13. package/dist/emblems/ball/ball.image.js +14 -5
  14. package/dist/emblems/ball/ball.index.js +2 -4
  15. package/dist/emblems/ball/ball.letters.d.ts +3 -0
  16. package/dist/emblems/ball/ball.letters.js +51 -0
  17. package/dist/emblems/ball/ball.print.d.ts +81 -0
  18. package/dist/emblems/ball/ball.print.js +268 -0
  19. package/dist/emblems/ball/ball.printable.d.ts +6 -0
  20. package/dist/emblems/ball/ball.printable.js +13 -0
  21. package/dist/emblems/ball/ball.region.d.ts +12 -0
  22. package/dist/emblems/ball/ball.region.js +94 -0
  23. package/dist/emblems/ball/ball.script.d.ts +15 -0
  24. package/dist/emblems/ball/ball.script.js +107 -0
  25. package/dist/emblems/ball/ball.soccer.js +7 -3
  26. package/dist/emblems/ball/ball.soccer.panels.d.ts +2 -0
  27. package/dist/emblems/ball/ball.soccer.panels.js +6 -3
  28. package/dist/emblems/ball/ball.soccer.size.d.ts +2 -0
  29. package/dist/emblems/ball/ball.soccer.size.js +8 -0
  30. package/dist/emblems/ball/ball.softball.js +2 -1
  31. package/dist/emblems/ball/ball.warp.d.ts +48 -0
  32. package/dist/emblems/ball/ball.warp.js +441 -0
  33. package/dist/emblems/emblem.core.d.ts +13 -0
  34. package/dist/emblems-entry.d.ts +1 -0
  35. package/dist/index.d.ts +4 -4
  36. package/dist/index.js +3 -4
  37. package/dist/version.d.ts +1 -1
  38. package/dist/version.js +1 -1
  39. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,24 @@ All notable changes to this project are written here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.0] - 2026-10-15
10
+
11
+ ### Added
12
+
13
+ - **Custom balls.** The baseball, the softball and the classic soccer balls (`ball:baseball`, `ball:softball`, `ball:soccer`, `ball:soccer-orange`, `ball:soccer-yellow` and `ball:soccer-colour`) take six options, drawn on the sphere so that they curve with it, are foreshortened toward the edge, go round the limb and turn with the ball (`renderEmblem`, `emblemFrame`, `ballFrame` and `bindTurn` take them):
14
+ - `paint`: a colour, or `{ body, second, split, angle, stitch }`. A solid ball takes the colour, and the stitches change to a thread that shows against it (red, cream, navy or near-black). Two colours make a two-tone ball, split along the figure-eight seam (each leather piece one colour, each stitch leg the thread that shows on its own side) or in halves. On a soccer ball `body` is the hexagons and `second` the pentagons.
15
+ - `decal`: a logo of your own as an SVG string, a `data:` image URI or `{ svg, href, size, turn }`. An SVG is flattened and wrapped exactly (fills cut at the limb, strokes thinning as they turn away); a PNG, JPEG, GIF or WebP is laid on in small affine triangles. Made safe with an allow-list; an address on the network is refused.
16
+ - `stamp`: bold condensed block lettering between small stars (`UNOFFICIAL` over `DIGITAL BASEBALL` by default, the ball's own word on each ball), your own lines, ink and an optional made-up signature line with a caption.
17
+ - `script`: an original cursive script (waypoints drawn for this package, a thick down stroke and a hairline up stroke, a to z and our own `Baseball` and `Soccer`), where a maker's mark sits; with a small circled letter under it, or your own mark.
18
+ - `mark`: a letter in a circle, or an SVG of your own drawn in its place (the slot for a mark this package must not carry).
19
+ - `spots`: where the logo and the lettering go. The logo has the middle and the lettering the far side by default.
20
+ - **`printed(ball)`** (main entry) and the types `BallPaint`, `BallDecal`, `BallStamp`, `BallScript`, `BallMark`, `BallSpots` and `Spot`. The balls' own modules carry no printing code and ignore the options; the registry, `renderEmblem` and the main entry's constants are the printed ones, and `printed(BALL_BASEBALL)` gives a ball taken from its own module the printing (about 19 kB gzipped, against 8.6 kB for the plain baseball).
21
+ - **`docs/custom-balls.md`** and its Japanese page, the gallery's note about the options, and a "Custom ball" section in the demo: colour, two-tone, stamp lines, script word, mark and a logo picked from a file (read in the browser into a data URI), with the call that draws it.
22
+
23
+ ### Changed
24
+
25
+ - The ball engine can fill the disc of a ball and a region of it, and a ball's draw hook takes its paint and print options. A plain soccer ball bundled alone is 7.3 kB gzipped (was 6.5), so the size test's limit for a ball is 7.6 kB.
26
+
9
27
  ## [0.8.0] - 2026-10-14
10
28
 
11
29
  ### Added
package/README.md CHANGED
@@ -85,6 +85,7 @@ Put the string in a page (`element.innerHTML = svg`), write it to a file, or sen
85
85
  - **Size, tint, accent and theme** as options: a tint recolours the main part (a medal's metal, a crest's colour, a ball's patches), an accent the second colour (a ribbon, a crest's band, the pennants), a theme sets the ink for light, dark or either.
86
86
  - **Turning**: `emblemFrame` and `ballFrame` return the emblem at an angle, in plain SVG: every emblem turns, a ball rolls, a cup keeps its outline and moves its light, and everything flat (gear, a bat, a crown, a sport's icon) flips like a coin; `bindTurn` lets a drag, the arrow keys or an auto-turn do the turning in a page; see [docs/turning.md](./docs/turning.md).
87
87
  - **Engraving**: text cut into a medal, a plaque, the belt, a ring, a banner, a pennant and the plinth of a cup, from `label`, `year` and `rank` or one `engrave` string, in the ink that reads at 4.5 to 1 on the metal, and it turns with the emblem; see [Engrave an emblem](#engrave-an-emblem).
88
+ - **Custom balls**: a baseball, a softball and the classic soccer balls take `paint` (a solid colour, or two: along the seam or in halves, with stitches that still show), `decal` (your own SVG or image as a logo, wrapped onto the sphere so that it turns with the ball), `stamp` (condensed bold text between small stars, with an optional made-up signature), `script` (an original cursive word) and `mark` (a letter in a circle, or an SVG of your own); see [docs/custom-balls.md](./docs/custom-balls.md). The package carries no logo of anyone's: a logo or a mark is the one you pass.
88
89
  - **Labels in two languages**: the `<title>` is English or Japanese (`locale`), your own words (`title: "…"`), or left out for an emblem that sits beside text that says the same (`title: false`).
89
90
  - **As an image, a sprite, a podium or a case**: `emblemDataUri` for an `<img>`, a CSS background or a canvas, `emblemSprite` to draw one emblem fifty times cheaply, `sportEquipment` for what a sport is played with, and `podium` and `awardCase` for results and trophy shelves; see [docs/compose.md](./docs/compose.md).
90
91
  - **One module per item**, so a bundler takes only what is imported; see [Use it in your project](#use-it-in-your-project).
@@ -149,7 +150,7 @@ console.log(TROPHY_HOCKEY_CUP.id); // trophy:hockey-cup
149
150
  console.log(TROPHY_HOCKEY_CUP.svg({ size: 24 }).includes('width="24"')); // true
150
151
  ```
151
152
 
152
- A bundler that imports this module takes the cup and the renderer for a body of revolution, and no other emblem: the package says `"sideEffects": false` and each constant is marked pure. `test/size.test.ts` bundles one item with esbuild and holds it to a limit for its kind (a ball 6.8 kB, a medal or a crest 4.6 kB, a cup 7.1 kB gzipped, most of that the renderer); `pnpm test:package` does the same from the packed tarball.
153
+ A bundler that imports this module takes the cup and the renderer for a body of revolution, and no other emblem: the package says `"sideEffects": false` and each constant is marked pure. `test/size.test.ts` bundles one item with esbuild and holds it to a limit for its kind (a ball 7.6 kB, a medal or a crest 4.6 kB, a cup 7.1 kB gzipped, most of that the renderer); `pnpm test:package` does the same from the packed tarball.
153
154
 
154
155
  ### In a page
155
156
 
@@ -551,39 +552,67 @@ Every emblem is drawn for this package, and nothing is copied from another proje
551
552
  ### The family
552
553
 
553
554
  <!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
554
- Dougu Sports is one of twenty-eight packages, each made for the same site, each at
555
- [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
555
+ Dougu Sports is one of thirty-two packages, each made for the same site, each at
556
+ [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca) or [github.com/spxis](https://github.com/spxis). The code of every one is MIT.
556
557
 
557
- - [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
558
- - [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ): a turning cube for the browser, 2×2 to 7×7, with record solves to replay. [Demo](https://johnmorrisdotca.github.io/kyuubu/).
559
- - [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ): a colour-card shedding game for two to eight, with the house rules people play. [Demo](https://johnmorrisdotca.github.io/hitotsu/).
560
- - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
561
- - [Tane](https://github.com/johnmorrisdotca/tane) (種): seeded random numbers and daily seeds, the same in every browser and on every server. [Demo](https://johnmorrisdotca.github.io/tane/).
558
+ **Games — board**
559
+
560
+ - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
561
+ - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
562
562
  - [Narabe](https://github.com/johnmorrisdotca/narabe) (並べ): one rules engine for abstract board games, from gomoku and Reversi to Go and checkers. [Demo](https://johnmorrisdotca.github.io/narabe/).
563
+ - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
563
564
  - [Tenka](https://github.com/johnmorrisdotca/tenka) (天下): world conquest for two to six, on a map of the real world. [Demo](https://johnmorrisdotca.github.io/tenka/).
564
- - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
565
- - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
565
+
566
+ **Games — cards & tiles**
567
+
568
+ - [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ): a colour-card shedding game for two to eight, with the house rules people play. [Demo](https://johnmorrisdotca.github.io/hitotsu/).
566
569
  - [Jarajara](https://github.com/johnmorrisdotca/jarajara) (ジャラジャラ): mahjong tiles drawn as SVG, stacked layouts, and the matching solitaire Awase. [Demo](https://johnmorrisdotca.github.io/jarajara/).
567
- - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
568
- - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
569
- - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
570
- - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
570
+ - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
571
+ - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
572
+
573
+ **Games — puzzles**
574
+
575
+ - [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
576
+ - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
577
+ - [Karakuri](https://github.com/johnmorrisdotca/karakuri) (からくり): eight hyper-casual puzzle games, some of them physics: draw a shield, pull pins, cut ropes, slide blocks, pour tubes. [Demo](https://johnmorrisdotca.github.io/karakuri/).
571
578
  - [Kazu](https://github.com/johnmorrisdotca/kazu) (数): grid number puzzles: Sudoku and its variants, Futoshiki and Skyscrapers. [Demo](https://johnmorrisdotca.github.io/kazu/).
579
+ - [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ): a turning cube for the browser, 2×2 to 7×7, with record solves to replay. [Demo](https://johnmorrisdotca.github.io/kyuubu/).
572
580
  - [Meikyuu](https://github.com/johnmorrisdotca/meikyuu) (迷宮): mazes on squares, hexagons, triangles and circles, made from a seed and drawn through with a finger or the mouse. [Demo](https://johnmorrisdotca.github.io/meikyuu/).
581
+ - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
582
+ - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
583
+ - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
584
+
585
+ **Games — dice & random**
586
+
587
+ - [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
588
+ - [Tane](https://github.com/johnmorrisdotca/tane) (種): seeded random numbers and daily seeds, the same in every browser and on every server. [Demo](https://johnmorrisdotca.github.io/tane/).
589
+
590
+ **Language**
591
+
592
+ - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
573
593
  - [Hikidashi](https://github.com/johnmorrisdotca/hikidashi) (引き出し): a drawer of small Japanese text tools: era dates, kanji numerals, readings and sentence difficulty. [Demo](https://johnmorrisdotca.github.io/hikidashi/).
594
+ - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
595
+
596
+ **Geography & data**
597
+
598
+ - [address-plus](https://github.com/johnmorrisdotca/address-plus) (アドレスプラス): parse and format addresses in the United States, Canada, Japan, Australia, France, Germany and the United Kingdom. [Demo](https://johnmorrisdotca.github.io/address-plus/).
574
599
  - [Chizu](https://github.com/johnmorrisdotca/chizu) (地図): maps of the world and of countries' regions, in English and Japanese, with a quiz and callouts. [Demo](https://johnmorrisdotca.github.io/chizu/).
575
- - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
576
- - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
577
- - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
578
- - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
579
- - [Karakuri](https://github.com/johnmorrisdotca/karakuri) (からくり): eight hyper-casual puzzle games, some of them physics: draw a shield, pull pins, cut ropes, slide blocks, pour tubes. [Demo](https://johnmorrisdotca.github.io/karakuri/).
580
- - [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
581
- - [Kyogijo Stadiums](https://github.com/johnmorrisdotca/kyogijo-stadiums) (競技場): sports fields, rinks, courts and ballparks drawn to scale as SVG, with each team and the score on them. [Demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/).
582
- - [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets) (対戦): a sport-agnostic tournament bracket, drawn as SVG, from a list of ties. [Demo](https://johnmorrisdotca.github.io/taisen-brackets/).
600
+ - [Hata](https://github.com/johnmorrisdotca/hata) (旗): country and regional flags as SVG, at their true proportions and keyed by ISO code. [Demo](https://johnmorrisdotca.github.io/hata/).
601
+ - [Kuni](https://github.com/johnmorrisdotca/kuni) (国): countries and their regions, with ISO codes and names in English and Japanese. [Demo](https://johnmorrisdotca.github.io/kuni/).
602
+
603
+ **Sports**
604
+
583
605
  - [Dougu Sports](https://github.com/johnmorrisdotca/dougu-sports) (道具): sports emblems as SVG: trophies, medals, crests, and the ball and gear of every sport, with optional 3D turning. [Demo](https://johnmorrisdotca.github.io/dougu-sports/).
606
+ - [Kyogijo Stadiums](https://github.com/johnmorrisdotca/kyogijo-stadiums) (競技場): sports fields, rinks, courts and ballparks drawn to scale as SVG, with each team and the score on them. [Demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/).
584
607
  - [Senshu Jerseys](https://github.com/johnmorrisdotca/senshu-jerseys) (選手): sports jerseys, sweaters, kits, caps, helmets and retired-number banners as SVG, front and back, with optional 2.5D turning. [Demo](https://johnmorrisdotca.github.io/senshu-jerseys/).
608
+ - [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets) (対戦): a sport-agnostic tournament bracket, drawn as SVG, from a list of ties. [Demo](https://johnmorrisdotca.github.io/taisen-brackets/).
609
+
610
+ **Developer tools**
611
+
612
+ - [REST in Pieces](https://github.com/spxis/rest-in-pieces) (レスト・イン・ピーシーズ): seeded test data and REST APIs for frontend development, with latency and error drills. [Demo](https://spxis.github.io/rest-in-pieces/).
613
+
585
614
 
586
- **This package is Dougu Sports.** The demos of all twenty-eight share one header and footer, so each links the rest.
615
+ **This package is Dougu Sports.** The demos of all thirty-two share one header and footer, so each links the rest.
587
616
  <!-- family:end -->
588
617
 
589
618
  ## Development
@@ -605,7 +634,7 @@ Bug reports and ideas go in the [issues](https://github.com/johnmorrisdotca/doug
605
634
 
606
635
  ## Changes
607
636
 
608
- See [CHANGELOG.md](./CHANGELOG.md). 0.8.0 makes `bindTurn` light: it takes an emblem object and then needs no registry, so a turnable cup is about 9.4 kB gzipped instead of about 81 kB, and an id loads the registry on demand; 0.7.3 turns the build, docs and picture scripts, the browser tests and the demo's source from JavaScript into TypeScript, with no change to the package; 0.7.2 makes every docs page single-language (a page in Japanese beside each page that had Japanese words in it, and a test that holds them apart), with no change to the pictures; 0.7.1 puts real countries (Canada, Japan) in the crest and puck examples in place of made-up teams, with no change to the code; 0.7.0 gives the national volleyballs layouts that tell them apart (Japan a crimson band, Poland white above and red below, the USA a navy field), strengthens the 2010-style grooved soccer ball, and adds six pictures of how the judo belt is tied, `gear:obi-step-1` to `gear:obi-step-6` (269 emblems: [docs/obi-steps.md](./docs/obi-steps.md)); 0.6.0 gives the judo suit and the karate suit their sleeves (a judogi has no short ones: they reach the wrist) and adds the judogi seen from behind with the IJF-style back number, a family name and a country code on a patch, with a `backnumber` option (269 emblems: [docs/judogi-back.md](./docs/judogi-back.md)); 0.5.1 redraws the judo suit, the belt and the football pictograms and adds the blue and folded suits and the belts of every grade (261 emblems: [docs/japanese-sports.md](./docs/japanese-sports.md)); 0.5.0 adds fifty balls: vintage leather soccer balls, era-style and shop-wall soccer balls, baseballs of other threads and volleyballs in national colours, with a `colors` option (244 emblems: [docs/vintage-balls.md](./docs/vintage-balls.md)); 0.4.0 adds eight printed pucks and a `face` option (194 emblems: [docs/pucks.md](./docs/pucks.md)); 0.3.0 adds nineteen soccer balls (26, 18, 14, 12 and 6 panels, winter orange and high-visibility yellow, futsal and beach soccer: 186 emblems); 0.2.1 draws every ball and implement to the rulebook's proportions; 0.2.0 is 167 emblems, every one drawn here, with the Japanese sports, engraving, a turn for every emblem and the compose helpers; 0.1.0 brought the first full set of 148; 0.0.1 was the first preview.
637
+ See [CHANGELOG.md](./CHANGELOG.md). 0.9.0 lets the baseball, the softball and the classic soccer balls be painted and printed: solid and two-tone colours, a logo of your own wrapped onto the ball, stamped text, an original cursive word and a small mark, all turning with the ball ([docs/custom-balls.md](./docs/custom-balls.md)); 0.8.0 makes `bindTurn` light: it takes an emblem object and then needs no registry, so a turnable cup is about 9.4 kB gzipped instead of about 81 kB, and an id loads the registry on demand; 0.7.3 turns the build, docs and picture scripts, the browser tests and the demo's source from JavaScript into TypeScript, with no change to the package; 0.7.2 makes every docs page single-language (a page in Japanese beside each page that had Japanese words in it, and a test that holds them apart), with no change to the pictures; 0.7.1 puts real countries (Canada, Japan) in the crest and puck examples in place of made-up teams, with no change to the code; 0.7.0 gives the national volleyballs layouts that tell them apart (Japan a crimson band, Poland white above and red below, the USA a navy field), strengthens the 2010-style grooved soccer ball, and adds six pictures of how the judo belt is tied, `gear:obi-step-1` to `gear:obi-step-6` (269 emblems: [docs/obi-steps.md](./docs/obi-steps.md)); 0.6.0 gives the judo suit and the karate suit their sleeves (a judogi has no short ones: they reach the wrist) and adds the judogi seen from behind with the IJF-style back number, a family name and a country code on a patch, with a `backnumber` option (269 emblems: [docs/judogi-back.md](./docs/judogi-back.md)); 0.5.1 redraws the judo suit, the belt and the football pictograms and adds the blue and folded suits and the belts of every grade (261 emblems: [docs/japanese-sports.md](./docs/japanese-sports.md)); 0.5.0 adds fifty balls: vintage leather soccer balls, era-style and shop-wall soccer balls, baseballs of other threads and volleyballs in national colours, with a `colors` option (244 emblems: [docs/vintage-balls.md](./docs/vintage-balls.md)); 0.4.0 adds eight printed pucks and a `face` option (194 emblems: [docs/pucks.md](./docs/pucks.md)); 0.3.0 adds nineteen soccer balls (26, 18, 14, 12 and 6 panels, winter orange and high-visibility yellow, futsal and beach soccer: 186 emblems); 0.2.1 draws every ball and implement to the rulebook's proportions; 0.2.0 is 167 emblems, every one drawn here, with the Japanese sports, engraving, a turn for every emblem and the compose helpers; 0.1.0 brought the first full set of 148; 0.0.1 was the first preview.
609
638
 
610
639
  ## Credits
611
640
 
@@ -1,5 +1,5 @@
1
1
  // Baseball: a white leather ball with 108 red double stitches in V's along the figure-eight seam.
2
- import { seamedBall } from "./ball.family.seamed.js";
2
+ import { SEAMED_HOME, seamedBall } from "./ball.family.seamed.js";
3
3
  export const BALL_BASEBALL = /* @__PURE__ */ seamedBall({
4
4
  id: "ball:baseball",
5
5
  en: "Baseball",
@@ -9,6 +9,7 @@ export const BALL_BASEBALL = /* @__PURE__ */ seamedBall({
9
9
  b: 0.4,
10
10
  view: { yaw: 18, pitch: 28, roll: -10 },
11
11
  mat: { gloss: 0.3, shade: "#4a3a2a", grain: { f: 1.4, a: 0.12 } },
12
+ home: SEAMED_HOME("baseball"),
12
13
  groove: "#8a7a62",
13
14
  stitch: { colour: "#c4262e", count: 108, half: 0.075, width: 0.85, lean: 0.032 },
14
15
  });
@@ -0,0 +1,4 @@
1
+ /** The contrast of two colours, 1 to 21. */
2
+ export declare function contrast(a: string, b: string): number;
3
+ /** The stitch colour for a ball: the wanted one if it is a colour, else the ball's own thread, cream, navy or near-black, whichever shows on every colour the ball is. */
4
+ export declare function stitchFor(grounds: readonly string[], wanted: string | undefined, fallback: string): string;
@@ -0,0 +1,22 @@
1
+ // Contrast of colours, for choosing a thread or an ink that shows on the ball. Imports only the core's colour reader.
2
+ import { hexOf, readColour } from "./ball.core.js";
3
+ const luminance = (hex) => {
4
+ const rgb = readColour(hex) ?? [128, 128, 128];
5
+ const f = (c) => {
6
+ const v = c / 255;
7
+ return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
8
+ };
9
+ return 0.2126 * f(rgb[0]) + 0.7152 * f(rgb[1]) + 0.0722 * f(rgb[2]);
10
+ };
11
+ /** The contrast of two colours, 1 to 21. */
12
+ export function contrast(a, b) {
13
+ const x = luminance(a), y = luminance(b);
14
+ return (Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05);
15
+ }
16
+ /** The stitch colour for a ball: the wanted one if it is a colour, else the ball's own thread, cream, navy or near-black, whichever shows on every colour the ball is. */
17
+ export function stitchFor(grounds, wanted, fallback) {
18
+ const w = wanted === undefined ? undefined : readColour(wanted);
19
+ if (w !== undefined)
20
+ return hexOf(w);
21
+ return [fallback, "#f4f0e6", "#1b2a5c", "#101418"].find((c) => grounds.every((g) => contrast(c, g) >= 2.2)) ?? fallback;
22
+ }
@@ -1,4 +1,6 @@
1
1
  import type { FlagRenderer, PuckFace } from "./ball.face.ts";
2
+ import type { Surface } from "./ball.engine.ts";
3
+ import type { BallDecal, BallMark, BallPaint, BallScript, BallSpots, BallStamp } from "./ball.print.ts";
2
4
  /** `metal` (also written `colour`): full colour, with shading. `line`: one outline in `currentColor`. */
3
5
  export type PieceStyle = "metal" | "colour" | "color" | "line";
4
6
  /** `inherit` (the default): the page's text colour. `auto`: dark ink on a light device and pale ink on a dark one. `light` and `dark` force one. */
@@ -27,7 +29,23 @@ export interface PieceOptions {
27
29
  flags?: FlagRenderer;
28
30
  /** A judogi seen from behind: the back number on it (see `Backnumber`). The back views of the judogi (`gear:judogi-back`, `gear:judogi-blue-back`) take it; anything else ignores it. */
29
31
  backnumber?: Backnumber;
32
+ /** A baseball, a softball and the classic soccer balls: the colour of the ball, as one colour or two (see `BallPaint`). Any other ball takes the body colour as a tint. */
33
+ paint?: string | BallPaint;
34
+ /** A baseball, a softball and the classic soccer balls: a logo printed on the side of the ball, wrapped to the sphere (see `BallDecal`). An SVG string, a `data:` image URI or an object. */
35
+ decal?: string | BallDecal;
36
+ /** The same balls: stamped text like a ball's stamp, `true` for the default (see `BallStamp`). */
37
+ stamp?: boolean | string | readonly string[] | BallStamp;
38
+ /** The same balls: a cursive script word where a maker's mark sits, `true` for the ball's own word, or your text (see `BallScript`). */
39
+ script?: boolean | string | BallScript;
40
+ /** The same balls: a small mark under the script, `true` for the circled letter, an SVG string of your own, or an object (see `BallMark`). */
41
+ mark?: boolean | string | BallMark;
42
+ /** The same balls: where on the ball the logo and the lettering go (see `BallSpots`). */
43
+ spots?: BallSpots;
44
+ /** @internal The function that draws the print of a ball (`printLayers`). `renderEmblem`, the registry and `printed()` set it; a ball taken from its own module has none and ignores `decal`, `stamp`, `script` and `mark`. */
45
+ printer?: PrintRenderer;
30
46
  }
47
+ /** What draws the print of a ball on its surface: the options, the ball's own word, and the colours the print lies on. */
48
+ export type PrintRenderer = (s: Surface, p: PrintOptions | undefined, word: string, grounds: readonly string[]) => string;
31
49
  /**
32
50
  * The back number sewn on the back of a judogi (IJF Sport and Organisation Rules, C1.8): the competitor's family name and the three letters of the National Olympic Committee, on a white patch under a coloured band.
33
51
  * Only `gear:judogi-back` and `gear:judogi-blue-back` take it; anything else ignores it.
@@ -112,7 +130,31 @@ export interface ResolvedOptions {
112
130
  flags: FlagRenderer | undefined;
113
131
  /** A string that tells two faces apart, for the ids of a drawing. */
114
132
  faceKey: string;
133
+ /** The colours of a printed or painted ball, read and made safe; undefined when none was asked for. */
134
+ paint: ResolvedPaint | undefined;
135
+ /** What a ball is printed with, as the caller gave it (made safe where it is drawn, by `resolvePrint`); undefined when nothing was asked for. */
136
+ print: PrintOptions | undefined;
137
+ /** What draws the print, when the caller has it (see `PieceOptions.printer`). */
138
+ printer: PrintRenderer | undefined;
115
139
  }
140
+ /** The colours of a painted ball, as hex colours. */
141
+ export interface ResolvedPaint {
142
+ body: string | undefined;
143
+ second: string | undefined;
144
+ split: "seam" | "half";
145
+ angle: number;
146
+ stitch: string | undefined;
147
+ }
148
+ /** The printing options of a ball, as given. */
149
+ export interface PrintOptions {
150
+ decal?: PieceOptions["decal"];
151
+ stamp?: PieceOptions["stamp"];
152
+ script?: PieceOptions["script"];
153
+ mark?: PieceOptions["mark"];
154
+ spots?: PieceOptions["spots"];
155
+ }
156
+ /** True when a ball is asked to carry a logo, lettering or a mark. */
157
+ export declare const hasPrint: (p: PrintOptions | undefined) => boolean;
116
158
  export declare function resolveOptions(o: PieceOptions | undefined): ResolvedOptions;
117
159
  /** The ink of a drawing when a theme sets it, and the page it is made to read on. */
118
160
  export declare const PIECE_INK: {
@@ -42,6 +42,39 @@ export function hashOf(text) {
42
42
  h = Math.imul(h ^ text.charCodeAt(i), 16777619);
43
43
  return (h >>> 0).toString(36);
44
44
  }
45
+ /** True when a ball is asked to carry a logo, lettering or a mark. */
46
+ export const hasPrint = (p) => p !== undefined && ((p.decal !== undefined && p.decal !== false) || [p.stamp, p.script, p.mark].some((v) => v !== undefined && v !== false));
47
+ function paintOf(value) {
48
+ const o = typeof value === "string" ? { body: value } : typeof value === "object" && value !== null ? value : undefined;
49
+ if (o === undefined)
50
+ return undefined;
51
+ const pick = (v) => {
52
+ const rgb = typeof v === "string" ? readColour(v) : undefined;
53
+ return rgb === undefined ? undefined : hexOf(rgb);
54
+ };
55
+ const body = pick(o.body), second = pick(o.second), stitch = pick(o.stitch);
56
+ if (body === undefined && second === undefined && stitch === undefined)
57
+ return undefined;
58
+ const angle = typeof o.angle === "number" && Number.isFinite(o.angle) ? Math.max(-360, Math.min(360, o.angle)) : 0;
59
+ return { body, second, split: o.split === "half" ? "half" : "seam", angle, stitch };
60
+ }
61
+ /** The printing options that were given, copied (a hostile object is never held), or undefined when there are none. */
62
+ function printOf(o) {
63
+ if (o === undefined)
64
+ return undefined;
65
+ const p = {};
66
+ if (o.decal !== undefined)
67
+ p.decal = o.decal;
68
+ if (o.stamp !== undefined)
69
+ p.stamp = o.stamp;
70
+ if (o.script !== undefined)
71
+ p.script = o.script;
72
+ if (o.mark !== undefined)
73
+ p.mark = o.mark;
74
+ if (o.spots !== undefined)
75
+ p.spots = o.spots;
76
+ return Object.keys(p).length === 0 ? undefined : p;
77
+ }
45
78
  /** What tells one face from another: its plain data (a renderer is a function, and only its name goes in). */
46
79
  function faceKeyOf(face) {
47
80
  if (typeof face !== "object" || face === null)
@@ -68,7 +101,7 @@ export function resolveOptions(o) {
68
101
  const theme = o?.theme === "auto" || o?.theme === "light" || o?.theme === "dark" ? o.theme : "inherit";
69
102
  const tint = readColour(o?.tint);
70
103
  const accent = readColour(o?.accent);
71
- return { style: o?.style === "line" ? "line" : "colour", size, theme, tint: tint === undefined ? undefined : hexOf(tint), accent: accent === undefined ? undefined : hexOf(accent), colors: coloursOf(o?.colors), grain: typeof o?.grain === "boolean" ? o.grain : undefined, face: typeof o?.face === "object" && o.face !== null ? o.face : undefined, flags: typeof o?.flags === "function" ? o.flags : undefined, faceKey: faceKeyOf(o?.face) + (typeof o?.flags === "function" ? `|fn:${o.flags.name}` : "") };
104
+ return { style: o?.style === "line" ? "line" : "colour", size, theme, tint: tint === undefined ? undefined : hexOf(tint), accent: accent === undefined ? undefined : hexOf(accent), colors: coloursOf(o?.colors), grain: typeof o?.grain === "boolean" ? o.grain : undefined, face: typeof o?.face === "object" && o.face !== null ? o.face : undefined, flags: typeof o?.flags === "function" ? o.flags : undefined, faceKey: faceKeyOf(o?.face) + (typeof o?.flags === "function" ? `|fn:${o.flags.name}` : ""), paint: paintOf(o?.paint), print: printOf(o), printer: typeof o?.printer === "function" ? o.printer : undefined };
72
105
  }
73
106
  /** The ink of a drawing when a theme sets it, and the page it is made to read on. */
74
107
  export const PIECE_INK = { light: "#20262d", dark: "#eef1f3" };
@@ -1,5 +1,5 @@
1
- import { type Piece, type PieceSize, type ResolvedOptions } from "./ball.core.ts";
2
- import { type V3 } from "./ball.math.ts";
1
+ import { type Piece, type PieceSize, type PrintOptions, type PrintRenderer, type ResolvedOptions, type ResolvedPaint } from "./ball.core.ts";
2
+ import { type M3, type V3 } from "./ball.math.ts";
3
3
  /** How a ball takes the light. */
4
4
  export interface Material {
5
5
  /** The colour of the shadowed side; by default the base colour made darker. */
@@ -32,12 +32,14 @@ export interface Surface {
32
32
  ink?: boolean;
33
33
  opacity?: number;
34
34
  n?: number;
35
+ rule?: "evenodd";
35
36
  }): string;
36
37
  /** Several closed polygons of one fill, drawn as one path (a smaller document than a path for each). */
37
38
  panels(rings: ReadonlyArray<readonly V3[]>, fill: string, o?: {
38
39
  ink?: boolean;
39
40
  opacity?: number;
40
41
  n?: number;
42
+ rule?: "evenodd";
41
43
  }): string;
42
44
  /** A curve on the surface: only what faces you. `closed` joins its ends; `deco` marks a line that is not part of the ball's form (a highlight), which the line style leaves out. */
43
45
  stroke(pts: readonly V3[], colour: string, width: number, o?: StrokeOptions): string;
@@ -59,8 +61,16 @@ export interface Surface {
59
61
  facing(u: V3): number;
60
62
  /** A point of space, in units of the ball's radius from its centre (not on the surface): its screen position and depth (positive is toward you). */
61
63
  space(p: V3): [number, number, number];
64
+ /** The whole disc of the ball filled with a colour (a sphere's silhouette). */
65
+ fill(colour: string): string;
66
+ /** The ball's view, for a module that draws what the engine does not (`ball.region.ts`). Opaque to everything else. */
67
+ readonly view: Geometry;
68
+ /** Where a ball's printing sits: the middle of its label area in the ball's own frame, and the right-hand and upward directions there. Only a ball that is printed has one. */
69
+ readonly home: HomeFrame | undefined;
62
70
  }
63
71
  export interface StrokeOptions {
72
+ /** Digits kept after the point in the path (1 by default; lettering asks for 2). */
73
+ digits?: number;
64
74
  closed?: boolean;
65
75
  ink?: boolean;
66
76
  deco?: boolean;
@@ -88,8 +98,23 @@ export interface BallSpec {
88
98
  /** The colour the whole ball is filled with before its panels are laid on, where it is not `base` (a soccer ball is white, its pentagons are `base`). */
89
99
  ground?: string;
90
100
  mat: Material;
91
- /** Lays the panels and seams. `base` is the main colour, or the tint; `accent` is the second colour or undefined. */
92
- draw: (s: Surface, base: string, accent: string | undefined, colors?: readonly [string, string, string]) => string;
101
+ /** Lays the panels and seams. `base` is the main colour, or the tint; `accent` is the second colour or undefined. `custom` is what a caller asked to paint and print (a ball that takes it reads it; the rest ignore it). */
102
+ draw: (s: Surface, base: string, accent: string | undefined, colors?: readonly [string, string, string], custom?: Custom) => string;
103
+ /**
104
+ * Where a ball carries a logo and lettering: the middle of the label area (`c`) and the way up there (`up`), as directions in the ball's own frame, how far it reaches from the middle (`reach`, radians),
105
+ * the view that shows it (`view`, used instead of `view` when the caller prints something) and the ball's own word for the script and the stamp (`word`).
106
+ */
107
+ home?: {
108
+ c: V3;
109
+ up: V3;
110
+ reach: number;
111
+ view: {
112
+ yaw: number;
113
+ pitch: number;
114
+ roll: number;
115
+ };
116
+ word: string;
117
+ };
93
118
  /** Where the ball's centre is in the 100 by 100 box (50, 50 by default): a hammer's head sits in a corner to leave room for the wire. */
94
119
  centre?: readonly [number, number];
95
120
  /** Where the ball is in space, in units of its radius from the middle of the whole (0, 0, 0 by default), turned with it: a hammer's head is at one end of the hammer, which turns about its middle. */
@@ -102,6 +127,47 @@ export interface BallSpec {
102
127
  front: string;
103
128
  };
104
129
  }
130
+ /** The label area of a printed ball: its middle, and the right-hand and upward directions there, and how far it reaches. */
131
+ export interface HomeFrame {
132
+ c: V3;
133
+ ex: V3;
134
+ up: V3;
135
+ reach: number;
136
+ }
137
+ /** What a caller asked a ball to be painted and printed with. */
138
+ export interface Custom {
139
+ paint: ResolvedPaint | undefined;
140
+ print: PrintOptions | undefined;
141
+ /** Draws the print, when the caller has it; without it a ball draws no print. */
142
+ printer: PrintRenderer | undefined;
143
+ }
144
+ export declare const EPS = 0.000001;
145
+ export interface Geometry {
146
+ /** The centre of the ball on the screen, and the middle of the whole (where a point of `space` is measured from). */
147
+ cx: number;
148
+ cy: number;
149
+ ox: number;
150
+ oy: number;
151
+ axes: V3;
152
+ rot: M3;
153
+ k: number;
154
+ /** The unit sphere's point is visible when its dot with `w` is above 0. */
155
+ w: V3;
156
+ wl: number;
157
+ /** The third row of the rotation: the direction of the eye in the ball's own frame. */
158
+ rz: V3;
159
+ /** The exponent of a pointed ball's profile, or undefined for an ellipsoid. */
160
+ point: number | undefined;
161
+ }
162
+ /** How squarely a point of the sphere faces the eye, before it is scaled: above 0 is the side you see. For an ellipsoid it is the dot of the point with `w`; for a pointed ball the surface's normal is worked out. */
163
+ export declare const visOf: (g: Geometry, u: V3) => number;
164
+ export declare const project: (g: Geometry, u: V3) => [number, number];
165
+ export interface Run {
166
+ pts: Array<[number, number, number]>;
167
+ }
168
+ /** The parts of a curve that face you, each as screen points with their facing. The curve is cut exactly at the limb. */
169
+ export declare function visibleRuns(g: Geometry, pts: readonly V3[], closed: boolean): Run[];
170
+ export declare const pathOf: (pts: Array<[number, number, number]>, digits?: number) => string;
105
171
  /** Draws a ball: the document for a spec, the options and a turn in radians. */
106
172
  export declare function drawBall(title: string, key: string, spec: BallSpec, opts: ResolvedOptions, turn: {
107
173
  yaw: number;