@johnmorrisdotca/dougu-sports 0.16.1 → 0.25.3

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 (46) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/README.md +9 -9
  3. package/dist/emblems/ball/ball.baseball.js +1 -0
  4. package/dist/emblems/ball/ball.engine.d.ts +3 -0
  5. package/dist/emblems/ball/ball.engine.js +13 -2
  6. package/dist/emblems/ball/ball.family.dotted.d.ts +4 -0
  7. package/dist/emblems/ball/ball.family.dotted.js +2 -1
  8. package/dist/emblems/ball/ball.family.seamed.d.ts +2 -0
  9. package/dist/emblems/ball/ball.family.seamed.js +1 -0
  10. package/dist/emblems/ball/ball.golf.js +6 -4
  11. package/dist/emblems/ball/ball.object.d.ts +2 -0
  12. package/dist/emblems/ball/ball.object.js +1 -1
  13. package/dist/emblems/ball/ball.puck.js +1 -1
  14. package/dist/emblems/ball/ball.stack.d.ts +7 -0
  15. package/dist/emblems/ball/ball.stack.js +41 -3
  16. package/dist/emblems/emblem.descriptions.js +4 -0
  17. package/dist/emblems/emblem.equipment.js +1 -1
  18. package/dist/emblems/emblem.flip.d.ts +5 -1
  19. package/dist/emblems/emblem.flip.js +30 -13
  20. package/dist/emblems/emblem.ids.d.ts +4 -4
  21. package/dist/emblems/emblem.ids.js +4 -1
  22. package/dist/emblems/emblem.lathe.d.ts +28 -2
  23. package/dist/emblems/emblem.lathe.js +5 -0
  24. package/dist/emblems/emblem.medal.js +2 -1
  25. package/dist/emblems/emblem.named.js +30 -0
  26. package/dist/emblems/emblem.registry.js +6 -0
  27. package/dist/emblems/emblem.shapes.d.ts +5 -1
  28. package/dist/emblems/emblem.shapes.js +14 -12
  29. package/dist/emblems/gear/gear.baseballcap.d.ts +1 -0
  30. package/dist/emblems/gear/gear.baseballcap.js +29 -0
  31. package/dist/emblems/gear/gear.index.js +2 -1
  32. package/dist/emblems/lathe.extras.d.ts +3 -0
  33. package/dist/emblems/lathe.extras.js +59 -0
  34. package/dist/emblems/trophy/bill-russell-trophy.d.ts +2 -0
  35. package/dist/emblems/trophy/bill-russell-trophy.js +24 -0
  36. package/dist/emblems/trophy/hank-aaron-award.d.ts +2 -0
  37. package/dist/emblems/trophy/hank-aaron-award.js +22 -0
  38. package/dist/emblems/trophy/relief-man-award.d.ts +2 -0
  39. package/dist/emblems/trophy/relief-man-award.js +28 -0
  40. package/dist/index.d.ts +4 -0
  41. package/dist/index.js +4 -0
  42. package/dist/turn-control.d.ts +2 -1
  43. package/dist/turn-control.js +6 -5
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.js +1 -1
  46. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,128 @@ All notable changes to this project are written here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.25.3] - 2026-10-28
10
+
11
+ ### Fixed
12
+
13
+ - **A drag up (and Arrow Up) in `bindTurn` lifts the FRONT of the emblem**, as if its near edge were held and raised, and a drag down lowers it. It did the opposite (a drag up tipped the front down and showed a cup from above). The `pitch` option and `angles.pitch` keep their meaning and sign (positive is the tilt toward the eye, seen more from above); only what the hand and the arrow keys do to it is reversed, so a drag up now makes `pitch` go down, and a program that read `angles.pitch` to mirror a drag sees the opposite sign. Left and right are unchanged. In the demo the Tilt slider is the opposite sign of `pitch` (its top is a drag up), and the address and the code line still say `pitch`, so `?pitch=` from before means the same thing. Tests: `test/turn-control.test.ts`, `e2e/turn.demo.ts`.
14
+
15
+ ## [0.25.2] - 2026-10-28
16
+
17
+ ### Changed
18
+
19
+ - The lathe's extra decorations, `mesh` (the etched net of many diamonds, which was `net` with a `rows` number), `patch` and `panel`, are drawn by `src/emblems/lathe.extras.ts`, which a model asks for with `extras: LATHE_EXTRAS` (the Bill Russell Trophy and the Relief Man Award do). A cup that uses none of them no longer carries their code: the hockey cup alone bundles to 7213 bytes gzipped again (it was 7450 with them in the renderer, over the 7.1 kB limit, which `pnpm check` caught only in `scripts/check-package.ts`), and `test/size.test.ts` now holds that it leaves them out. The pictures do not change.
20
+
21
+ ## [0.25.1] - 2026-10-28
22
+
23
+ ### Fixed
24
+
25
+ - The Japanese name and description of `trophy:bill-russell-trophy` and the description of `trophy:hank-aaron-award` read as the awards are named in Japanese (the name `ビル・ラッセル・トロフィー`, `NBAファイナル` for the finals, `最優秀打者` for the best hitter); reviewed.
26
+
27
+ ## [0.25.0] - 2026-10-28
28
+
29
+ Two of baseball's awards, redrawn from photographs of the real ones. The earlier Hank Aaron Award, a silver batter, was a different object: the real award is a block of mahogany cut as home plate.
30
+
31
+ ### Added
32
+
33
+ - **`trophy:hank-aaron-award`**: the Hank Aaron Award, a mahogany block in the shape of home plate with a grey metal face, a bat laid across it and two ribbons over the bat, in the flat plaque style of the Cy Young and MLB MVP awards, with a back and a coin-flip turn. Measured on a Wikimedia Commons photograph (CC BY 2.0; looked at, never copied). No lettering, league mark or engraving. 330 emblems, 74 trophies, 50 named with the Relief Man Award.
34
+ - **`trophy:relief-man-award`**: the Relief Man Award (the fireman trophy given from 1976 to 2012), a silver fire helmet with a gilt shield on a short chrome column, over a walnut block with a brass nameplate and a stepped walnut base, built on the lathe like the Stanley Cup. Measured on an auction photograph of a 1981 trophy (looked at, never copied). Approximate: the helmet is drawn round. The shield, the plate and every mark and word are left blank, the plate taking the engraving option. 330 emblems, 74 trophies, 50 named.
35
+ - The lathe's decorations `patch` (a tone laid on a round body between two angles and two heights, a shield on a helmet) and `panel` (a tone laid on one flat face of a body with flat sides, a nameplate on a block).
36
+
37
+ ## [0.24.0] - 2026-10-28
38
+
39
+ A baseball cap, redrawn from two photographs of plain caps.
40
+
41
+ ### Added
42
+
43
+ - **`gear:baseball-cap`**: a baseball cap in three-quarter view, with a dome crown, button, front seam and three eyelets, and a curved brim with its edge and rows of stitching, in the same flat layered style as the package's helmet and glove, with a coin-flip turn. Measured on two public-domain and CC0 photographs of plain caps (looked at, never copied). No team mark, lettering or brand. It is in the gear list and with baseball's bat and glove. 328 emblems, 71 pieces of gear.
44
+
45
+ ## [0.23.0] - 2026-10-28
46
+
47
+ The NBA's Finals MVP trophy, redrawn from a photograph of the real one.
48
+
49
+ ### Added
50
+
51
+ - **`trophy:bill-russell-trophy`**: the Bill Russell NBA Finals Most Valuable Player Trophy, a gold basketball seated on the flared rim of a net-patterned goblet that narrows in a long concave curve to a slim waist, over a round foot of two tiers. Measured on a press photograph of the 2022 design standing beside the championship trophy (looked at, never copied; no freely licensed photograph exists), and built with the same lathe as the Stanley Cup and the Larry O'Brien Trophy. No lettering. 327 emblems, 72 trophies, 48 named.
52
+ - The lathe's `net` decoration takes a fifth number, `rows`: strands that wind a column to the side at each row, one way and the other, make a net of many diamonds down a body (a trophy's etched net) in a pale line. Without it `net` draws one row of diamonds as before.
53
+
54
+ ## [0.22.0] - 2026-10-28
55
+
56
+ The award trophies and the cap of 0.17.0 to 0.21.0, which were never published, are taken out. Drawn from written descriptions, they did not match the real objects and were far below the Stanley Cup and the Larry O'Brien Trophy. A site that asks for these awards draws the plain cup, which is honest; each comes back only when it can be drawn from a photograph of the real thing and passes beside the Stanley Cup.
57
+
58
+ ### Removed
59
+
60
+ - The trophies `jackie-robinson-award`, `mlb-manager-award`, `relief-man-award`, `hank-aaron-award`, `michael-jordan-trophy`, `bill-russell-trophy`, `olajuwon-trophy`, `chamberlain-trophy`, `havlicek-trophy`, `auerbach-trophy`, `mikan-trophy`, `rozelle-trophy`, `halas-trophy` and `gregory-trophy`, and `gear:baseball-cap`. None was ever on npm. 326 emblems again: 71 trophies, 70 pieces of gear.
61
+
62
+ ### Changed
63
+
64
+ - `docs/trophy-audit.md` says, for each of these awards, what the real object is and why it has no emblem yet.
65
+
66
+ ## [0.21.0] - 2026-10-23
67
+
68
+ A baseball cap. The Canadian and writers' football awards stay the plain cup: no photograph or measured description of those objects was found, so nothing new was drawn for them.
69
+
70
+ ### Added
71
+
72
+ - **`gear:baseball-cap`**: a baseball cap in three-quarter view (crown, button, panel seams, eyelets and a brim with an edge), in the gear list and with baseball's bat and glove. 341 emblems, 71 pieces of gear.
73
+
74
+ ### Fixed
75
+
76
+ - **`trophy:gregory-trophy`**: from behind, the three skaters stay on the trophy. The back had kept only the metal, so it was thinner than the front.
77
+
78
+ ## [0.20.0] - 2026-10-23
79
+
80
+ The National Hockey League's general-manager award that a site still drew as a plain cup.
81
+
82
+ ### Added
83
+
84
+ - **`trophy:gregory-trophy`**: the Jim Gregory General Manager of the Year Award, a silver bowl over three bronze skaters on a tall silver block. Drawn for this package from a Hockey Hall of Fame collection photograph that was looked at and never copied; Wikimedia Commons had no freely licensed photograph (searched 2026-10-23), so it is marked approximate. No shield, lettering or nameplates. 340 emblems, 85 trophies, 61 named.
85
+
86
+ ## [0.19.0] - 2026-10-23
87
+
88
+ Two National Football League awards that a site still drew as a plain cup.
89
+
90
+ ### Added
91
+
92
+ - **`trophy:rozelle-trophy`** and **`trophy:halas-trophy`**: the Pete Rozelle Trophy (the championship-game most valuable player; a silver football on a short disc) and the George Halas Trophy (the National Football Conference champion; a hollow silver football on a short three-sided stand, the counterpart of the Lamar Hunt Trophy). Each is this package's own drawing from a published description of the object; Wikimedia Commons had no freely licensed photograph (searched 2026-10-23), so they are marked approximate. No lettering, logo or conference mark. The Associated Press awards have no famous trophy and keep the generic plaque. 339 emblems, 84 trophies, 60 named.
93
+
94
+ ## [0.18.0] - 2026-10-23
95
+
96
+ Seven National Basketball Association awards that a site still drew as a plain cup.
97
+
98
+ ### Added
99
+
100
+ - **`trophy:michael-jordan-trophy`**, **`trophy:bill-russell-trophy`**, **`trophy:olajuwon-trophy`**, **`trophy:chamberlain-trophy`**, **`trophy:havlicek-trophy`**, **`trophy:auerbach-trophy`** and **`trophy:mikan-trophy`**: the Michael Jordan Trophy (regular-season most valuable player), the Bill Russell Trophy (championship-series most valuable player), the Hakeem Olajuwon, Wilt Chamberlain, John Havlicek and George Mikan trophies (defensive player, rookie, sixth man and most improved) and the Red Auerbach Trophy (coach of the year). Each is this package's own drawing from a published description of the object; Wikimedia Commons had no freely licensed photograph (searched 2026-10-23), so they are marked approximate. No lettering, logo or sponsor name. The MVP trophy's id is the current name, not the old commissioner's cup. 337 emblems, 82 trophies, 58 named.
101
+
102
+ ## [0.17.0] - 2026-10-23
103
+
104
+ Four Major League Baseball awards that a site still drew as a plain cup.
105
+
106
+ ### Added
107
+
108
+ - **`trophy:jackie-robinson-award`**, **`trophy:mlb-manager-award`**, **`trophy:relief-man-award`** and **`trophy:hank-aaron-award`**: the Jackie Robinson Award (Rookie of the Year of each league), the Manager of the Year Award, the Relief Man Award (the fireman trophy given 1976 to 2012) and the Hank Aaron Award. Each is this package's own drawing from a public description of the object; Wikimedia Commons had no freely licensed photograph (searched 2026-10-23), so they are marked approximate. No lettering, logo or sponsor name (the Relief Man's id is not the old sponsor's). 330 emblems, 75 trophies, 51 named.
109
+
110
+ ## [0.16.2] - 2026-10-21
111
+
112
+ Every piece that flips or turns has a body at every angle: a solid edge, a back of its own, and what hangs from it still there.
113
+
114
+ ### Fixed
115
+
116
+ - **Medals, crests, banners, belts, plaques, pennants, gear and the pictograms are slabs, not sheets** (`flipSvg`, and `defineFlat`, which now turns through it): a solid edge, in the face's own colours darkened, runs behind the squeezed face and stays as wide on the screen as the piece is thick, never under about 2.4 pixels whatever the size, so edge on (a quarter turn, either way) a medal is a band and no longer a hairline. The edge is eight slices of the face made opaque again (a thin part such as a stick or a strap keeps its edge when the squeeze would have thinned it away) and the slab is centred, so it does not slide as it turns; it lies on the other side once the back is toward you.
117
+ - **A medal seen from behind keeps its ribbon** (it lost it), a championship banner its pole and a crest pennant its staff, on the other side, as a real turn mirrors them. The plain parts of a piece with no back of its own (a plaque's board, a belt's strap) are drawn behind it too, so the back is as big as the front.
118
+ - **The hockey puck is a straight cylinder** (`drawStack`): the wall ends in the curve of the lower rim, a crisp edge, and no longer in the soft edge of the bottom cap; its edge is knurled with diamonds all round the wall (it covered only part of it), and the knurl slides round the wall as the puck spins. The curling stone's walls end the same way. Seen from the side the puck is one third as thick as it is wide, at 0, 30, 60 and 90 degrees of tilt.
119
+ - **The golf ball reads as a golf ball at every size and tint**: about 330 dimples (it had 190, faint), each a bowl with a shadow on its upper left wall and a bright edge on the lower right, white by default. **A golf ball and a baseball ignore `tint`** (their colour is fixed by the rules of their sports; a baseball takes `paint`); every other ball takes it. `BallSpec.fixed`, `DottedOptions.fixed` and `SeamedOptions.fixed` say so.
120
+ - **The demo's Auto-turn button** (Stop) is an ordinary button beside Reverse again; a class name it shared with the playground's section had stretched it across the row.
121
+
122
+ ### Changed
123
+
124
+ - **README pictures**: the balls row's centre ball, the one that rolls, is the outdoor pickleball with its see-through holes (it was the rugby ball); the flips row and the pickleball picture are redrawn (`pnpm animations`), still half speed and only the centre one turning.
125
+
126
+ ### Tests
127
+
128
+ - `test/thickness.test.ts`: the edge's width in pixels for every flat piece at 24, 64 and 256 pixels, its sides, the back of every flat piece, the puck's wall; `test/golf.test.ts`; `e2e/silhouette.demo.ts` counts real pixels in Chromium (edge on every flat piece is a band at least two pixels wide, from behind it is as solid as its front, the puck is one third as thick as wide at 0, 30, 60 and 90 degrees); an e2e test of the Auto-turn button.
129
+ - Size limits: a medal, a crest or a plaque bundled alone is held to 5.8 kB gzipped (was 4.6), the cost of the one turn they now share.
130
+
9
131
  ## [0.16.1] - 2026-10-21
10
132
 
11
133
  The turning pictures show one thing moving: in a row, only the centre one turns.
package/README.md CHANGED
@@ -61,7 +61,7 @@ The ball and the gear of every sport, trophies, medals, crests and the icon of e
61
61
 
62
62
  ## The best of it
63
63
 
64
- - **47 famous trophies**, each a close likeness measured on a photograph and carrying no lettering, logo or mark: the Stanley Cup, the FIFA World Cup, the Lombardi Trophy, the Larry O'Brien trophy, the Grey Cup and the rest ([famous trophies](./docs/famous-trophies.md)).
64
+ - **50 famous trophies**, each a close likeness measured on a photograph and carrying no lettering, logo or mark: the Stanley Cup, the FIFA World Cup, the Lombardi Trophy, the Larry O'Brien trophy, the Grey Cup and the rest ([famous trophies](./docs/famous-trophies.md)).
65
65
 
66
66
  <p align="center">
67
67
  <picture>
@@ -75,7 +75,7 @@ The ball and the gear of every sport, trophies, medals, crests and the icon of e
75
75
  <p align="center">
76
76
  <picture>
77
77
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/dougu-sports/main/docs/images/balls-turn-dark.webp">
78
- <img alt="Seven balls under floodlights: a soccer ball, a basketball, a baseball, a rugby ball, a volleyball, a tennis ball and a golf ball. Six stand still and only the rugby ball in the centre rolls, so that its stripes and panels travel across it." src="https://raw.githubusercontent.com/johnmorrisdotca/dougu-sports/main/docs/images/balls-turn-light.webp" width="760">
78
+ <img alt="Seven balls under floodlights: a soccer ball, a basketball, a baseball, an outdoor pickleball, a volleyball, a tennis ball and a golf ball. Six stand still and only the pickleball in the centre rolls, its holes showing the dark inside and the floodlights through as they line up." src="https://raw.githubusercontent.com/johnmorrisdotca/dougu-sports/main/docs/images/balls-turn-light.webp" width="760">
79
79
  </picture>
80
80
  </p>
81
81
 
@@ -102,15 +102,15 @@ The ball and the gear of every sport, trophies, medals, crests and the icon of e
102
102
  <p align="center">
103
103
  <picture>
104
104
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/dougu-sports/main/docs/images/flips-turn-dark.webp">
105
- <img alt="A crest, a hockey skate, a silver medal, a gold medal, a championship banner, a baseball glove and a football helmet on royal purple: six stand still and only the gold medal in the centre flips like a coin, showing its face, then its edge, then its plain back." src="https://raw.githubusercontent.com/johnmorrisdotca/dougu-sports/main/docs/images/flips-turn-light.webp" width="760">
105
+ <img alt="A crest, a hockey skate, a silver medal, a gold medal, a championship banner, a baseball glove and a football helmet on royal purple: six stand still and only the gold medal in the centre flips like a coin, showing its face, then its solid edge, then its back, with the ribbon still hanging from it." src="https://raw.githubusercontent.com/johnmorrisdotca/dougu-sports/main/docs/images/flips-turn-light.webp" width="760">
106
106
  </picture>
107
107
  </p>
108
108
 
109
- - **70 pieces of gear**, among them the Japanese sports and budo ([Japanese sports](./docs/japanese-sports.md)).
109
+ - **71 pieces of gear**, among them the Japanese sports and budo ([Japanese sports](./docs/japanese-sports.md)).
110
110
 
111
111
  - **On a phone**, and **turned by hand**: drag, arrow keys or the automatic turn ([turning](./docs/turning.md)).
112
112
 
113
- > **Status: preview.** 326 emblems: 71 trophies, 6 awards, 6 medals, 4 crests, a pictogram for each of the 42 sports, 118 balls and implements drawn in 3D, 8 printed pucks ([docs/pucks.md](./docs/pucks.md)), 70 pieces of gear (among them the Japanese sports and budo, [docs/japanese-sports.md](./docs/japanese-sports.md)) and a championship pennant; [docs/layout.md](./docs/layout.md) says where each goes. All of it is drawn for this package: no art is copied from another project.
113
+ > **Status: preview.** 330 emblems: 74 trophies, 6 awards, 6 medals, 4 crests, a pictogram for each of the 42 sports, 118 balls and implements drawn in 3D, 8 printed pucks ([docs/pucks.md](./docs/pucks.md)), 71 pieces of gear (among them the Japanese sports and budo, [docs/japanese-sports.md](./docs/japanese-sports.md)) and a championship pennant; [docs/layout.md](./docs/layout.md) says where each goes. All of it is drawn for this package: no art is copied from another project.
114
114
 
115
115
  ## In 30 seconds
116
116
 
@@ -140,8 +140,8 @@ Put the string in a page (`element.innerHTML = svg`), write it to a file, or sen
140
140
 
141
141
  - **Equipment first (道具 is the word for it)**: 118 balls and implements drawn in 3D, from the 32-panel soccer ball to the 26, 18, 14, 12 and 6 panel constructions, vintage leather balls, era-style and shop-wall soccer balls, baseballs of other threads, volleyballs in national colours ([docs/vintage-balls.md](./docs/vintage-balls.md)), futsal, beach soccer and the pucks and stones, each with its official size, 8 printed pucks (a flag, a leaf, stars and stripes, a player's number, a made-up signature, your own picture: [docs/pucks.md](./docs/pucks.md)), and 42 pieces of gear (sticks, rackets, helmets, skates, nets, and the Japanese budo kit) drawn to match; see [docs/gallery.md](./docs/gallery.md).
142
142
  - **Three styles** for every emblem: `metal` (polished metal: a gradient, a highlight and a soft rim of reflected light, edged in a darker tone of its own metal, with no heavy outline), `line` (one clean outline in the page's text colour, made to read at 14 pixels) and `color` (flat fills); see [Theming](#theming).
143
- - **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.
144
- - **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).
143
+ - **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; not the body of a golf ball or a baseball, whose colour the sport fixes), an accent the second colour (a ribbon, a crest's band, the pennants), a theme sets the ink for light, dark or either.
144
+ - **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 (a medal, a crest, a banner, gear, a bat, a crown, a sport's icon) flips like a coin with a solid edge, a back of its own and its ribbon or pole seen from behind; `bindTurn` lets a drag, the arrow keys or an auto-turn do the turning in a page; see [docs/turning.md](./docs/turning.md).
145
145
  - **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 [engraving](./docs/engraving.md).
146
146
  - **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.
147
147
  - **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`).
@@ -545,7 +545,7 @@ The metals are `METALS` (gold `#d9a620`, silver `#aeb6c0`, bronze `#b8733a`); ea
545
545
  ## Limits
546
546
 
547
547
  - A turn is drawn in plain SVG, not in 3D: a cup keeps its silhouette and moves its light, handles and seams; a medal is squeezed; a ball is projected from its own geometry, panel by panel, and has no grain while it turns. Gear, icons and pictograms flip like a coin, with the back as a mirror of the front and a thin rim; a pennant's back is its own drawing; one turn (`src/emblems/emblem.flip.ts`) does it for all of them.
548
- - A tint is one colour, the main one, and an accent a second. The pieces of a trophy that are another metal (the gilt of a plate, the green bands of a globe, the black foot of a cup) keep their own colour.
548
+ - A tint is one colour, the main one, and an accent a second. A golf ball and a baseball are white by the rules of their sports, so they ignore `tint` (a baseball takes `paint`, as the custom balls do); every other ball takes it. The pieces of a trophy that are another metal (the gilt of a plate, the green bands of a globe, the black foot of a cup) keep their own colour.
549
549
  - The trophies are generic: each is drawn for this package in a simplified form that a reader knows at a glance, and none is traced from a photograph or carries a name, a mark or an engraving.
550
550
  - A trophy in the metal style is about 3 to 4.5 kB of markup, a medal or a crest about 1 to 2 kB, a line drawing about 1 to 2.5 kB; a ball in 3D is 5 to 13 kB, and a piece of gear is 1 to 4 kB, drawn in layers.
551
551
  - The size is a number of pixels up to 4096; the drawing is a vector at any size.
@@ -676,7 +676,7 @@ Bug reports and ideas go in the [issues](https://github.com/johnmorrisdotca/doug
676
676
 
677
677
  ## Changes
678
678
 
679
- See [CHANGELOG.md](./CHANGELOG.md). 0.16.0 adds a slotted plastic practice baseball (`ball:plastic-baseball-slotted`, 326 emblems: [docs/slotted-ball.md](./docs/slotted-ball.md)); 0.15.0 gives the pickleballs real holes, cut through the shell and drawn with the inside of the ball behind them, so the page shows through where two holes line up ([docs/pickleball.md](./docs/pickleball.md)); 0.14.0 lets the auto-turn go either way (`direction: "clockwise"` or `"counterclockwise"`, `setDirection()`); 0.13.0 adds a pictogram for each of four combat sports, MMA (a cage), boxing (a glove), wrestling (two wrestlers on the mat) and professional wrestling (a ring with ropes), in the three styles (326 emblems); 0.12.0 adds forty-seven named trophies, each a close likeness measured on a photograph (321 emblems); 0.11.0 gives the era soccer balls neutral ids (the old ones stay as aliases), new looks and a second 1980s ball (274 emblems); 0.10.0 adds a standalone ribbon and the nets and posts of sepak takraw, netball and teqball from their rulebooks' sizes (273 emblems); 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.
679
+ See [CHANGELOG.md](./CHANGELOG.md). 0.25.0 adds two of baseball's awards, drawn from photographs of the real ones: the Hank Aaron Award and the Relief Man Award (`trophy:hank-aaron-award`, `trophy:relief-man-award`, 330 emblems); 0.24.0 adds a baseball cap (`gear:baseball-cap`, 328 emblems); 0.23.0 adds the Bill Russell Finals MVP Trophy, drawn from a photograph of the real one (`trophy:bill-russell-trophy`, 327 emblems); 0.22.0 takes out the award cups and the cap of the unpublished 0.17.0 to 0.21.0, which did not match the real objects; 0.16.0 adds a slotted plastic practice baseball (`ball:plastic-baseball-slotted`, 326 emblems: [docs/slotted-ball.md](./docs/slotted-ball.md)); 0.15.0 gives the pickleballs real holes, cut through the shell and drawn with the inside of the ball behind them, so the page shows through where two holes line up ([docs/pickleball.md](./docs/pickleball.md)); 0.14.0 lets the auto-turn go either way (`direction: "clockwise"` or `"counterclockwise"`, `setDirection()`); 0.13.0 adds a pictogram for each of four combat sports, MMA (a cage), boxing (a glove), wrestling (two wrestlers on the mat) and professional wrestling (a ring with ropes), in the three styles (326 emblems); 0.12.0 adds forty-seven named trophies, each a close likeness measured on a photograph (321 emblems); 0.11.0 gives the era soccer balls neutral ids (the old ones stay as aliases), new looks and a second 1980s ball (274 emblems); 0.10.0 adds a standalone ribbon and the nets and posts of sepak takraw, netball and teqball from their rulebooks' sizes (273 emblems); 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.
680
680
 
681
681
  ## Credits
682
682
 
@@ -6,6 +6,7 @@ export const BALL_BASEBALL = /* @__PURE__ */ seamedBall({
6
6
  ja: "野球のボール",
7
7
  size: { extentMm: 73.8, diameterMm: 73.8, circumferenceMm: 231.77, massG: [141.75, 148.83], rule: "Official Baseball Rules, Rule 3.01 The Ball", note: "Diameter worked out from the middle of the circumference range." },
8
8
  base: "#f4f0e6",
9
+ fixed: true,
9
10
  b: 0.4,
10
11
  view: { yaw: 18, pitch: 28, roll: -10 },
11
12
  mat: { gloss: 0.3, shade: "#4a3a2a", grain: { f: 1.4, a: 0.12 } },
@@ -57,6 +57,7 @@ export interface Surface {
57
57
  opacity?: number;
58
58
  ink?: boolean;
59
59
  light?: boolean;
60
+ dimple?: string;
60
61
  }): string;
61
62
  /** The facing of a point: 1 straight at you, 0 on the limb. */
62
63
  facing(u: V3): number;
@@ -96,6 +97,8 @@ export interface BallSpec {
96
97
  };
97
98
  /** The main colour of the ball (what `tint` replaces). */
98
99
  base: string;
100
+ /** The ball's real colour is fixed (a golf ball, a baseball): `tint` does not recolour it. The ball's own `paint` still does, where it has one. */
101
+ fixed?: boolean;
99
102
  /** 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`). */
100
103
  ground?: string;
101
104
  mat: Material;
@@ -269,6 +269,16 @@ function surfaceOf(g, line, minWidth, home) {
269
269
  if (d.length === 0)
270
270
  return "";
271
271
  const path = d.join("");
272
+ if (o?.dimple !== undefined && !line) {
273
+ // A dimple is a shallow bowl lit from the upper left: its upper left wall is in shadow, its lower right wall catches the light. Three layers for all of them: the whole mark in the shadow's colour, the same marks moved a little
274
+ // toward the lower right and filled with the ball's own colour (which leaves a crisp dark crescent on the upper left), and a bright edge along the lower right.
275
+ const r = s.radius * sr;
276
+ const dx = num(r * 0.2, 2), dy = num(r * 0.26, 2);
277
+ const w = num(Math.max(0.3, Math.min(r * 0.2, 0.9)), 2);
278
+ const key = `dm${hashOf(path)}`;
279
+ const at = `transform="translate(${dx} ${dy})"`;
280
+ return `<defs><path id="${key}" d="${path}"/></defs><use href="#${key}" fill="${fill}" opacity="${o.opacity ?? 0.55}"/><use href="#${key}" fill="${o.dimple}" ${at}/><use href="#${key}" fill="none" stroke="#fff" stroke-width="${w}" opacity=".7" ${at}/>`;
281
+ }
272
282
  const hi = o?.light === false || line ? "" : `<path d="${path}" fill="none" stroke="#fff" stroke-width="${num(Math.max(0.35, minWidth * 0.3), 2)}" opacity=".3" transform="translate(.25 .35)"/>`;
273
283
  return `${hi}<path d="${path}" fill="${line ? INK : fill}"${o?.opacity === undefined ? "" : ` opacity="${o.opacity}"`}/>`;
274
284
  },
@@ -333,12 +343,13 @@ export function drawBall(title, key, spec, opts, turn, turning) {
333
343
  // A ball with a second colour of its own (a soccer ball's panels on a white ground) paints the body as the ground and the second colour as the panels; one with a single colour takes the body as its main colour.
334
344
  const paint = opts.paint;
335
345
  const two = spec.ground !== undefined;
336
- const base = opts.tint ?? (two ? paint?.second : paint?.body) ?? spec.base;
346
+ const tint = spec.fixed === true ? undefined : opts.tint;
347
+ const base = tint ?? (two ? paint?.second : paint?.body) ?? spec.base;
337
348
  const ground = two ? paint?.body ?? spec.ground : undefined;
338
349
  const surface = surfaceOf(g, line, minWidth, homeOf(spec));
339
350
  const layers = spec.draw(surface, base, opts.accent, opts.colors, { paint: opts.paint, print: opts.print, printer: opts.printer });
340
351
  const sil = silhouette(g);
341
- const id = `k${hashOf(`${key}|${opts.style}|${opts.tint ?? ""}|${opts.accent ?? ""}${opts.colors === undefined ? "" : `|${opts.colors.join("")}`}|${sil.tag}${sil.d}${paint === undefined && (opts.print === undefined || opts.printer === undefined) ? "" : `|${JSON.stringify([paint, opts.printer === undefined ? undefined : opts.print])}`}`)}`;
352
+ const id = `k${hashOf(`${key}|${opts.style}|${tint ?? ""}|${opts.accent ?? ""}${opts.colors === undefined ? "" : `|${opts.colors.join("")}`}|${sil.tag}${sil.d}${paint === undefined && (opts.print === undefined || opts.printer === undefined) ? "" : `|${JSON.stringify([paint, opts.printer === undefined ? undefined : opts.print])}`}`)}`;
342
353
  // A long outline (the hull of a pointed ball) is written once, in the definitions, and used wherever the silhouette is.
343
354
  const long = sil.d.length > 400;
344
355
  const outline = long ? `<path id="${id}s" d="${sil.d}"/>` : "";
@@ -22,6 +22,10 @@ export interface DottedOptions {
22
22
  light?: boolean;
23
23
  /** Real holes through the shell, not marks on it (`holeLayers` from ball.holes.ts, taken by the pickleball only): the near ones are cut out and show the inside of the ball, and where two line up the page shows through. The line style still draws marks. */
24
24
  hollow?: Drill;
25
+ /** The ball's real colour is fixed, so `tint` does not recolour it (see `BallSpec.fixed`). */
26
+ fixed?: boolean;
27
+ /** Marks drawn as shallow bowls (dimples): a shadow on the upper left wall, a bright edge on the lower right, the ball's own colour inside. */
28
+ dimples?: boolean;
25
29
  /** Turns the spread, so a ball does not show its spiral's pole straight on. */
26
30
  phase?: number;
27
31
  }
@@ -10,12 +10,13 @@ export function dottedBall(o) {
10
10
  const spec = {
11
11
  view: o.view ?? { yaw: 20, pitch: 25, roll: 0 },
12
12
  base: o.base,
13
+ fixed: o.fixed,
13
14
  mat: o.mat,
14
15
  draw: (s, base) => {
15
16
  const mark = o.mark ?? mix(base, -0.55);
16
17
  if (o.hollow !== undefined && !s.line)
17
18
  return "";
18
- return s.dots(centres, o.radius, mark, { opacity: o.depth ?? 0.55, ink: true, light: o.light ?? true });
19
+ return s.dots(centres, o.radius, mark, { opacity: o.depth ?? 0.55, ink: true, light: o.light ?? true, ...(o.dimples === true ? { dimple: mix(base, -0.04) } : {}) });
19
20
  },
20
21
  holes: o.hollow === undefined ? undefined : (s, id, base, shape, minWidth) => o.hollow(s, id, { centres, radius: o.radius }, base, shape, minWidth),
21
22
  };
@@ -9,6 +9,8 @@ export interface SeamedOptions {
9
9
  base: string;
10
10
  /** The seam's parameter b. */
11
11
  b: number;
12
+ /** The ball's real colour is fixed, so `tint` does not recolour it (see `BallSpec.fixed`). */
13
+ fixed?: boolean;
12
14
  view?: BallSpec["view"];
13
15
  mat: BallSpec["mat"];
14
16
  /** A strip along the seam: its colour and width. */
@@ -19,6 +19,7 @@ export function seamedBall(o) {
19
19
  const spec = {
20
20
  view: o.view ?? { yaw: 0, pitch: 0, roll: 0 },
21
21
  base: o.base,
22
+ fixed: o.fixed,
22
23
  mat: o.mat,
23
24
  home: o.home,
24
25
  draw: (s, base, _accent, _colors, custom) => {
@@ -6,10 +6,12 @@ export const BALL_GOLF = /* @__PURE__ */ dottedBall({
6
6
  ja: "ゴルフボール",
7
7
  size: { extentMm: 42.67, diameterMm: 42.67, massG: [0, 45.93], rule: "Rules of Equipment (R&A / USGA), Part 4 Conformance of Balls, 4.2 Weight and 4.3 Size", note: "A minimum diameter and a maximum mass: the rules set no maximum size and no minimum weight." },
8
8
  base: "#f7f7f5",
9
- count: 190,
10
- radius: 0.066,
11
- depth: 0.2,
9
+ fixed: true,
10
+ count: 330,
11
+ radius: 0.074,
12
+ depth: 0.62,
13
+ dimples: true,
12
14
  light: false,
13
- mark: "#5a6070",
15
+ mark: "#7d8594",
14
16
  mat: { gloss: 0.85, shade: "#2a3350", grain: undefined },
15
17
  });
@@ -13,6 +13,8 @@ export interface ObjectContext {
13
13
  turning: boolean;
14
14
  /** An id prefix that no other drawing of this piece in the page shares. */
15
15
  id: string;
16
+ /** The turn about the object's own axis, in degrees (its resting view's and the turn's together): what a knurled edge slides by. */
17
+ spin: number;
16
18
  /** The thinnest line that shows at this size, in box units. */
17
19
  minWidth: number;
18
20
  /** A point of the object in its own space to the box: x, y and depth (positive toward you); one unit is 46 box units. */
@@ -14,7 +14,7 @@ export function defineObject(def) {
14
14
  const q = apply(rot, [(p[0] - sh[0]) * k, (p[1] - sh[1]) * k, (p[2] - sh[2]) * k]);
15
15
  return [50 + 46 * q[0], 50 - 46 * q[1], q[2]];
16
16
  };
17
- const { defs, body } = def.draw({ rot, opts, turning, id, minWidth: 80 / opts.size, at });
17
+ const { defs, body } = def.draw({ rot, opts, turning, id, spin: def.view.yaw + finite(o?.yaw), minWidth: 80 / opts.size, at });
18
18
  return wrapPiece({ title: escape(def.en), size: opts.size, theme: opts.theme, defs, body });
19
19
  };
20
20
  return { id: def.id, en: def.en, ja: def.ja, source: "generated", kind: "ball", model: { ...def.model, view: def.view }, size: def.size, svg: labelled(def.en, def.ja, (o) => render(o, false)), frame: labelled(def.en, def.ja, (o) => render(o, true)) };
@@ -14,7 +14,7 @@ export const puckSpec = (extra) => ({
14
14
  gloss: 0.5,
15
15
  shade: "#000",
16
16
  grain: { f: 1.6, a: 0.25 },
17
- grooves: { count: 30, slices: [0], colour: "#000", opacity: 0.5 },
17
+ knurl: { count: 44, slices: [0], colour: "#000", opacity: 0.7 },
18
18
  topRing: { r: 0.9, colour: "#4a4d56" },
19
19
  ...extra,
20
20
  });
@@ -25,6 +25,13 @@ export interface StackSpec {
25
25
  colour: string;
26
26
  opacity: number;
27
27
  };
28
+ /** A knurled band: diamonds cut across the whole wall of the slices named, `count` of them round the slice, in lines of `colour`. It follows the wall all round and to its crisp lower edge. */
29
+ knurl?: {
30
+ count: number;
31
+ slices: readonly number[];
32
+ colour: string;
33
+ opacity: number;
34
+ };
28
35
  /** A ring on the top face: radius as a fraction, colour. */
29
36
  topRing?: {
30
37
  r: number;
@@ -45,7 +45,6 @@ export function drawStack(c, spec) {
45
45
  const [x0, y0] = at(s.y0), [x1, y1] = at(s.y1);
46
46
  const rb = 46 * radius(s, 0), rt = 46 * radius(s, 1);
47
47
  const pts = [[x0 + perp[0] * rb, y0 + perp[1] * rb], [x1 + perp[0] * rt, y1 + perp[1] * rt], [x1 - perp[0] * rt, y1 - perp[1] * rt], [x0 - perp[0] * rb, y0 - perp[1] * rb]];
48
- const d4 = `M${pts.map((p) => `${num(p[0], 2)} ${num(p[1], 2)}`).join("L")}Z`;
49
48
  if (line) {
50
49
  if (s.outline !== true)
51
50
  return "";
@@ -57,9 +56,18 @@ export function drawStack(c, spec) {
57
56
  const near = topVisible ? -1 : 1;
58
57
  return `<path d="${lines}${rim(s.y0, radius(s, 0), near)}${i === slices.length - 1 ? "" : rim(s.y1, radius(s, 1), near)}" fill="none"/>`;
59
58
  }
60
- let out = `<path d="${d4}" fill="url(#${gid})"/>`;
59
+ // The wall is the band between the two ends and the half of the far end's rim that bulges toward you, so it ends in a crisp curved edge like a real cylinder's, not in the soft edge of its cap.
60
+ const bulge = (y, r) => {
61
+ const [cx, cy] = at(y);
62
+ const rad = (perpDeg * Math.PI) / 180;
63
+ const ex = cx - Math.cos(rad) * 46 * r, ey = cy - Math.sin(rad) * 46 * r;
64
+ return `A${num(46 * r, 2)} ${num(Math.max(46 * r * Math.abs(nz), 0.02), 2)} ${num(perpDeg, 1)} 0 ${topVisible ? 1 : 0} ${num(ex, 2)} ${num(ey, 2)}`;
65
+ };
66
+ const p = (k) => `${num(pts[k][0], 2)} ${num(pts[k][1], 2)}`;
67
+ const wall = topVisible ? `M${p(1)}L${p(0)}${bulge(s.y0, radius(s, 0))}L${p(2)}Z` : `M${p(0)}L${p(1)}${bulge(s.y1, radius(s, 1))}L${p(3)}Z`;
68
+ let out = `<path d="${wall}" fill="url(#${gid})"/>`;
61
69
  const gr = spec.grooves;
62
- if (!line && gr !== undefined && gr.slices.includes(i)) {
70
+ if (gr !== undefined && gr.slices.includes(i)) {
63
71
  let marks = "";
64
72
  for (let k = 0; k < gr.count; k++) {
65
73
  const t = ((k + 0.5) / gr.count) * Math.PI;
@@ -71,6 +79,36 @@ export function drawStack(c, spec) {
71
79
  }
72
80
  out += `<path d="${marks}" stroke="${gr.colour}" stroke-width="${num(Math.max(0.5, c.minWidth * 0.5), 2)}" opacity="${gr.opacity}" fill="none"/>`;
73
81
  }
82
+ const kn = spec.knurl;
83
+ if (kn !== undefined && kn.slices.includes(i)) {
84
+ // Each line of the knurl runs up the wall as it goes round it, one family each way, and is cut where it would leave the half of the wall you can see.
85
+ const toward = topVisible ? -1 : 1;
86
+ const point = (theta, v) => {
87
+ const rr = rb + (rt - rb) * v;
88
+ const cx = x0 + (x1 - x0) * v, cy = y0 + (y1 - y0) * v;
89
+ return `${num(cx + perp[0] * rr * Math.cos(theta) + toward * d[0] * rr * Math.abs(nz) * Math.sin(theta), 1)} ${num(cy + perp[1] * rr * Math.cos(theta) + toward * d[1] * rr * Math.abs(nz) * Math.sin(theta), 1)}`;
90
+ };
91
+ const pitch = (2 * Math.PI) / kn.count;
92
+ // A turn about the axis slides the knurl round the wall (a positive turn carries its front to the right).
93
+ const slide = ((((-c.spin * Math.PI) / 180) % pitch) + pitch) % pitch;
94
+ let marks = "";
95
+ for (const way of [1, -1]) {
96
+ for (let k = -2; k <= kn.count / 2 + 2; k++) {
97
+ let pen = false;
98
+ for (let j = 0; j <= 4; j++) {
99
+ const v = j / 4;
100
+ const theta = k * pitch + slide + way * 2 * pitch * v;
101
+ if (theta < 0 || theta > Math.PI) {
102
+ pen = false;
103
+ continue;
104
+ }
105
+ marks += `${pen ? "L" : "M"}${point(theta, v)}`;
106
+ pen = true;
107
+ }
108
+ }
109
+ }
110
+ out += `<path d="${marks}" stroke="${kn.colour}" stroke-width="${num(Math.max(0.5, c.minWidth * 0.55), 2)}" opacity="${kn.opacity}" fill="none" stroke-linecap="butt"/>`;
111
+ }
74
112
  return out;
75
113
  };
76
114
  const cap = (y, r, colour, i, topFace) => {
@@ -69,6 +69,7 @@ export const EMBLEM_DESCRIPTIONS = {
69
69
  "gear:table-tennis-paddle": { en: "Table-tennis paddle and ball", ja: "卓球のラケットとボール" },
70
70
  "gear:badminton-racket": { en: "Badminton racket and shuttlecock", ja: "バドミントンのラケットとシャトルコック" },
71
71
  "gear:football-helmet": { en: "Football helmet", ja: "アメリカンフットボールのヘルメット" },
72
+ "gear:baseball-cap": { en: "A baseball cap in three-quarter view: a dome crown with a button, a seam and three eyelets, and a curved brim with its edge and rows of stitching.", ja: "野球帽を斜め前から描いています。頭頂のボタン、縫い目、3つのはと目のあるドーム形のクラウンと、縁と何列もの縫い目のある湾曲したつばが見えます。" },
72
73
  "gear:hurley": { en: "Hurley", ja: "ハーリー(ハーリングのスティック)" },
73
74
  "trophy:sumo-cup": { en: "A tall silver cup on a tall stepped stand, with an ornate phoenix-crested handle on each side", ja: "段のある高い台に載った、背の高い銀の杯。両側の取っ手に鳳凰の飾りが付く" },
74
75
  "trophy:paterson-cup": { en: "The Paterson Cup: a wide silver bowl with a straight engraved band at the rim, two scrolled ear handles, a short stem and a dark wooden base. Given to the champion of the Pacific Coast Hockey Association from 1912", ja: "パターソン・カップ。縁にまっすぐな彫刻の帯がある幅広の銀のボウルで、渦巻き形の耳状の取っ手が2つ付き、短い脚と濃い色の木の台座があります。1912年から、パシフィック・コースト・ホッケー協会の優勝チームに贈られました。" },
@@ -96,6 +97,9 @@ export const EMBLEM_DESCRIPTIONS = {
96
97
  "trophy:commissioners-trophy": { en: "The Commissioner's Trophy: thirty slender poles with gilt pennants in a ring whose tops are higher at the back than at the front, over a wide round silver base.", ja: "コミッショナーズ・トロフィーは、30本の細い支柱が、それぞれ金色のペナントを付けて輪になり、輪の上端は後ろが高く手前が低く、幅広い丸い銀色の台の上に立つ姿を描いています。ワールドシリーズの優勝チームに贈られます。" },
97
98
  "trophy:grey-cup": { en: "The Grey Cup: a two-handled silver cup with a swirled stem on a tall flared cone of twelve panels crossed by dark bands.", ja: "グレイカップは、持ち手が2つで渦巻き模様の脚をもつ銀色のカップが、暗い帯の入った12面の背の高い広がる円錐形の台の上に載った姿を描いています。カナディアン・フットボール・リーグの優勝決定戦に勝ったチームに贈られます。" },
98
99
  "trophy:cy-young-award": { en: "The Cy Young Award: a dark plaque with a curved silver band, a V and a hand holding a baseball over a nameplate.", ja: "サイ・ヤング賞は、暗い色の飾り板に、曲線を描く銀色の帯とV字、銘板の上で野球のボールを握る手をあしらった姿を描いています。各リーグの最優秀投手に贈られます。" },
100
+ "trophy:relief-man-award": { en: "The Relief Man Award: a silver fire helmet with a gilt shield, on a short chrome column over a walnut block with a brass nameplate and a stepped walnut base.", ja: "リリーフマン賞は、金色の盾を付けた銀色の消防ヘルメットが、短いクロームの支柱の上に載り、真鍮の銘板を付けたクルミ材の角柱と、段のあるクルミ材の台の上に立つ姿を描いています。1976年から2012年まで、各リーグの最優秀救援投手に贈られました。" },
101
+ "trophy:hank-aaron-award": { en: "The Hank Aaron Award: a mahogany block cut in the shape of home plate, with a grey metal face, a bat laid across it from the upper left and two ribbons over the bat.", ja: "ハンク・アーロン賞は、ホームベースの形に削り出したマホガニーの台に、灰色の金属の面、左上がりに置かれたバット、その上に重なる2本のリボンをあしらった姿を描いています。各リーグの最優秀打者に贈られます。" },
102
+ "trophy:bill-russell-trophy": { en: "The Bill Russell Trophy: a gold basketball seated on the flared rim of a net-patterned goblet that narrows to a slim waist, over a round foot of two tiers.", ja: "ビル・ラッセル・トロフィーは、金色のバスケットボールが、ネット模様のゴブレットの広がった縁に載り、ゴブレットは細い脚へとすぼまり、二段の丸い台の上に立つ姿を描いています。NBAファイナルの最優秀選手に贈られます。" },
99
103
  "trophy:mlb-mvp-award": { en: "The Most Valuable Player Award: a dark octagonal plaque with a silver ring and diamond, a gilt medallion, a nameplate and crossed bats.", ja: "MVP賞は、銀色の輪とダイヤモンド、金色のメダリオン、銘板、交差した2本のバットを配した、暗い色の八角形の飾り板を描いています。各リーグの最優秀選手に贈られます。" },
100
104
  "trophy:walter-cup": { en: "The Walter Cup: a wide shallow silver bowl on a stem between two tall stick-shaped blades, over a heavy round base.", ja: "ウォルター・カップは、脚付きの幅広く浅い銀色のボウルが、スティック形の背の高い2枚のブレードの間に立ち、重厚な丸い台の上に載った姿を描いています。プロフェッショナル・ウィメンズ・ホッケー・リーグ(PWHL)の優勝チームに贈られます。" },
101
105
  "trophy:wnba-championship-trophy": { en: "The WNBA Championship Trophy: a silver basketball held on two slanting tubes over a low slab.", ja: "WNBA優勝トロフィーは、銀色のバスケットボールを、斜めに伸びる2本の管が支え、低い板状の台に載った姿を描いています。WNBAファイナルの優勝チームに贈られます。" },
@@ -5,7 +5,7 @@ const TABLE = {
5
5
  ifaf: ["ball:football-ifaf", ["gear:football-helmet", "gear:goalposts"], "trophy:cup"],
6
6
  nhl: ["ball:puck", ["gear:hockey-stick", "gear:hockey-skate", "gear:goalie-mask", "gear:goal-net"], "trophy:hockey-cup"],
7
7
  nba: ["ball:basketball", ["gear:basketball-hoop"], "trophy:basketball"],
8
- mlb: ["ball:baseball", ["gear:baseball-bat", "gear:baseball-glove"], "trophy:baseball"],
8
+ mlb: ["ball:baseball", ["gear:baseball-bat", "gear:baseball-glove", "gear:baseball-cap"], "trophy:baseball"],
9
9
  soccer: ["ball:soccer", ["gear:goal-net"], "trophy:soccer-globe", GOLD],
10
10
  curling: ["ball:curling-stone", ["gear:curling-broom"], "trophy:bowl", GOLD],
11
11
  tennis: ["ball:tennis", ["gear:tennis-racket"], "trophy:tennis-cup", GOLD],
@@ -6,5 +6,9 @@ export interface FlipOptions {
6
6
  back?: string;
7
7
  thick?: number;
8
8
  }
9
- /** A document turned by `yaw` and `pitch` degrees from its still drawing. With no turn it is the document itself. */
9
+ /**
10
+ * A document turned by `yaw` and `pitch` degrees from its still drawing. With no turn it is the document itself.
11
+ * The piece is a slab: its face is squeezed by the cosine of the turn, and between the face and its other side runs a solid edge (the thickness) that stays the same width on the screen while the face
12
+ * narrows, so that edge on it is a band and never a hairline. The edge is the face's own colours darkened, and a thin part (a stick, a strap) keeps its edge because the squeezed face is made opaque again before it is moved.
13
+ */
10
14
  export declare function flipSvg(svg: string, o: FlipOptions): string;
@@ -11,7 +11,15 @@ function split(svg) {
11
11
  const box = (/viewBox="([^"]*)"/.exec(open)?.[1] ?? "0 0 64 64").split(/\s+/).map(Number);
12
12
  return { open, head, rest: after.slice(head.length), box: [box[0] ?? 0, box[1] ?? 0, box[2] || 64, box[3] || 64] };
13
13
  }
14
- /** A document turned by `yaw` and `pitch` degrees from its still drawing. With no turn it is the document itself. */
14
+ /** The slices an edge is built from: each is the silhouette moved a little further along the thickness, so that together they are a solid band. */
15
+ const SLICES = 8;
16
+ /** The least thickness of the edge on the screen, in pixels, so that a piece edge on never thins to a hairline however small it is drawn. */
17
+ const LEAST_PX = 2.4;
18
+ /**
19
+ * A document turned by `yaw` and `pitch` degrees from its still drawing. With no turn it is the document itself.
20
+ * The piece is a slab: its face is squeezed by the cosine of the turn, and between the face and its other side runs a solid edge (the thickness) that stays the same width on the screen while the face
21
+ * narrows, so that edge on it is a band and never a hairline. The edge is the face's own colours darkened, and a thin part (a stick, a strap) keeps its edge because the squeezed face is made opaque again before it is moved.
22
+ */
15
23
  export function flipSvg(svg, o) {
16
24
  if (o.yaw === 0 && o.pitch === 0)
17
25
  return svg;
@@ -24,23 +32,32 @@ export function flipSvg(svg, o) {
24
32
  const [x0, y0, w, h] = front.box;
25
33
  const sx = Math.max(Math.abs(across), 0.02);
26
34
  const sy = Math.max(Math.abs(tilt), 0.02);
27
- const thick = w * (o.thick ?? 0.035);
28
- // A turn to a positive yaw carries the front toward the right, as it does for a ball and a cup (and for a drag to the right), so the rim is seen on the left, on the edge that comes toward the eye.
29
- const dx = -thick * Math.sin(o.yaw * RAD);
30
- const dy = -thick * Math.sin(o.pitch * RAD) * 0.6;
35
+ const pixels = Number(/\swidth="([\d.]+)"/.exec(front.open)?.[1]) || 64;
36
+ const thick = Math.min(Math.max(w * (o.thick ?? 0.035), (LEAST_PX * w) / pixels), w * 0.1);
37
+ // The edge runs from the face toward the far side. For a face that looks to the eye it is behind it (a positive yaw carries the front toward the right, as it does for a ball and a cup, so the edge is on the
38
+ // left); past a quarter turn the face seen is the back and the edge lies toward the front, on the other side. The whole slab is centred on the piece, so it does not slide as it turns.
39
+ const side = showBack ? -1 : 1;
40
+ const dx = -thick * Math.sin(o.yaw * RAD) * side;
41
+ const dy = -thick * Math.sin(o.pitch * RAD) * Math.abs(across) * 0.6;
31
42
  const mirror = showBack && own === undefined;
32
43
  const cx = x0 + w / 2;
33
44
  const cy = y0 + h / 2;
34
- // The turned face, then the filter that adds the rim behind it (and dims a mirrored back).
45
+ const flat = Math.abs(dx) + Math.abs(dy) < 0.05;
35
46
  // The id names everything the definition depends on (its region comes from the box), so two drawings in one page that share an id share one filter.
36
- const id = `dgf${o.line === true ? "l" : "c"}${mirror ? "b" : "f"}${Math.round(dx * 100)}_${Math.round(dy * 100)}_${[x0, y0, w, h].map((v) => Math.round(v)).join("_")}`;
37
- const flood = o.line === true ? '<feFlood flood-color="currentColor" flood-opacity=".5"/>' : '<feFlood flood-color="#000" flood-opacity=".42"/>';
38
- const steps = [1, 2, 3].map((k) => (Math.abs(dx) + Math.abs(dy) < 0.05 ? "" : `<feOffset in="s" dx="${n2((dx * k) / 3)}" dy="${n2((dy * k) / 3)}" result="o${k}"/>`)).join("");
39
- const nodes = Math.abs(dx) + Math.abs(dy) < 0.05 ? "" : '<feMergeNode in="o3"/><feMergeNode in="o2"/><feMergeNode in="o1"/>';
40
- const dim = mirror && o.line !== true ? '<feColorMatrix in="SourceGraphic" values=".8 0 0 0 0 0 .8 0 0 0 0 0 .8 0 0 0 0 0 1 0" result="d"/>' : "";
47
+ const lift = Math.round(Math.min(1 / sx, 24) * 10) / 10;
48
+ const dark = mirror && o.line !== true ? 0.8 : 1;
49
+ const id = `dgf${o.line === true ? "l" : "c"}${mirror ? "b" : "f"}${Math.round(dx * 100)}_${Math.round(dy * 100)}_${lift}_${[x0, y0, w, h].map((v) => Math.round(v)).join("_")}`;
50
+ const steps = flat ? "" : Array.from({ length: SLICES }, (_, k) => `<feOffset in="s" dx="${n2((dx * (k + 1)) / SLICES)}" dy="${n2((dy * (k + 1)) / SLICES)}" result="o${k + 1}"/>`).join("");
51
+ const nodes = flat ? "" : Array.from({ length: SLICES }, (_, k) => `<feMergeNode in="o${SLICES - k}"/>`).join("");
52
+ // The edge: the face's colours, darkened and made opaque again (the squeeze spread them thin); in the line style a flat dim ink.
53
+ const edge = o.line === true
54
+ ? `<feColorMatrix in="SourceGraphic" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ${lift} 0" result="a"/><feFlood flood-color="currentColor"/><feComposite in2="a" operator="in" result="b"/><feComponentTransfer in="b" result="s"><feFuncA type="linear" slope=".5"/></feComponentTransfer>`
55
+ : `<feColorMatrix in="SourceGraphic" values=".58 0 0 0 0 0 .58 0 0 0 0 0 .58 0 0 0 0 0 ${lift} 0" result="s"/>`;
56
+ const dim = mirror && o.line !== true ? `<feColorMatrix in="SourceGraphic" values="${dark} 0 0 0 0 0 ${dark} 0 0 0 0 0 ${dark} 0 0 0 0 0 1 0" result="d"/>` : "";
41
57
  const filter = `<filter id="${id}" x="${n2(x0 - w * 0.15)}" y="${n2(y0 - h * 0.15)}" width="${n2(w * 1.3)}" height="${n2(h * 1.3)}" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">` +
42
- `${flood}<feComposite in2="SourceAlpha" operator="in" result="s"/>${steps}${dim}<feMerge>${nodes}<feMergeNode in="${dim === "" ? "SourceGraphic" : "d"}"/></feMerge></filter>`;
58
+ `${flat ? "" : edge + steps}${dim}<feMerge>${nodes}<feMergeNode in="${dim === "" ? "SourceGraphic" : "d"}"/></feMerge></filter>`;
43
59
  const flip = mirror ? -1 : 1;
44
- const group = `<g filter="url(#${id})"><g transform="translate(${n2(cx)} ${n2(cy)}) scale(${n2(sx * flip)} ${n2(sy)}) translate(${n2(-cx)} ${n2(-cy)})">${face.rest}</g></g>`;
60
+ const shift = flat ? "" : `translate(${n2(-dx / 2)} ${n2(-dy / 2)}) `;
61
+ const group = `<g filter="url(#${id})"><g transform="${shift}translate(${n2(cx)} ${n2(cy)}) scale(${n2(sx * flip)} ${n2(sy)}) translate(${n2(-cx)} ${n2(-cy)})">${face.rest}</g></g>`;
45
62
  return `${front.open}${front.head}<defs>${filter}</defs>${group}</svg>`;
46
63
  }