@johnmorrisdotca/dougu-sports 0.4.0 → 0.5.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.
- package/CHANGELOG.md +29 -1
- package/NOTICE.md +6 -4
- package/README.md +6 -5
- package/dist/emblems/ball/ball.baseball.vintage.d.ts +10 -0
- package/dist/emblems/ball/ball.baseball.vintage.js +70 -0
- package/dist/emblems/ball/ball.core.d.ts +8 -1
- package/dist/emblems/ball/ball.core.js +11 -1
- package/dist/emblems/ball/ball.decal.d.ts +56 -0
- package/dist/emblems/ball/ball.decal.js +146 -0
- package/dist/emblems/ball/ball.engine.d.ts +7 -1
- package/dist/emblems/ball/ball.engine.js +10 -2
- package/dist/emblems/ball/ball.face.d.ts +14 -9
- package/dist/emblems/ball/ball.face.js +6 -5
- package/dist/emblems/ball/ball.family.panelled.d.ts +5 -0
- package/dist/emblems/ball/ball.family.panelled.js +12 -5
- package/dist/emblems/ball/ball.family.seamed.d.ts +8 -1
- package/dist/emblems/ball/ball.family.seamed.js +13 -3
- package/dist/emblems/ball/ball.index.js +6 -1
- package/dist/emblems/ball/ball.puck.print.js +1 -1
- package/dist/emblems/ball/ball.soccer.era.d.ts +23 -0
- package/dist/emblems/ball/ball.soccer.era.js +302 -0
- package/dist/emblems/ball/ball.soccer.store.d.ts +19 -0
- package/dist/emblems/ball/ball.soccer.store.js +176 -0
- package/dist/emblems/ball/ball.soccer.vintage.d.ts +11 -0
- package/dist/emblems/ball/ball.soccer.vintage.js +135 -0
- package/dist/emblems/ball/ball.tilings.d.ts +26 -0
- package/dist/emblems/ball/ball.tilings.js +87 -0
- package/dist/emblems/ball/ball.volleyball.flag.d.ts +12 -0
- package/dist/emblems/ball/ball.volleyball.flag.js +36 -0
- package/dist/emblems/emblem.core.d.ts +6 -2
- package/dist/emblems/emblem.descriptions.js +50 -0
- package/dist/emblems/emblem.ids.d.ts +2 -2
- package/dist/emblems/emblem.ids.js +3 -2
- package/dist/emblems-entry.d.ts +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,32 @@ All notable changes to this project are written here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.0] - 2026-10-14
|
|
10
|
+
|
|
11
|
+
Fifty balls in true 3D: vintage leather soccer balls, soccer balls in the style of an era, patterns of a shop wall, baseballs of other threads and volleyballs in national colours. 244 emblems.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Five vintage soccer balls** ([docs/vintage-balls.md](docs/vintage-balls.md), [日本語](docs/vintage-balls.ja.md)), in worn tan, brown and chestnut leather with scuffs and stitch lines, each on the construction of its period: `ball:soccer-1870-hand-stitched` (eight long panels, a button at each end, cross-stitched), `ball:soccer-1900-laced` (eight panels, a round cap at each end and a criss-crossed lace across a slit), `ball:soccer-1930-18-panel` (eighteen strips, six panels of three), `ball:soccer-1950-12-panel` (twelve pentagons in four tones) and `ball:soccer-1960-32-panel-leather`. The four older balls carry the 27 to 28 inch circumference of the 1872 Laws.
|
|
16
|
+
- **Eleven soccer balls in the style of an era**, with graphics of our own on the construction of the time and no real design, mark or name: `ball:soccer-1970-black-white`, `-1978-triad` (three curved triangles in a pinwheel on each hexagon), `-1986-decorated-triad`, `-1998-coloured-triad`, `-2002-triangle-sweep`, `-2006-propeller` (14 panels), `-2010-grooved` (8 panels), `-2014-ribbon` (6 panels), `-2018-pixel`, `-2022-speed-shell` (20 panels) and `ball:soccer-training-high-vis`.
|
|
17
|
+
- **Eighteen shop-wall soccer balls**, six generic patterns in three colourways each: stars (`-blue`, `-red`, `-night`), hexagon outlines (`-cyan`, `-magenta`, `-lime`), flames (`-fire`, `-ice`, `-toxic`), chevrons (`-red`, `-green`, `-blue`), a two-tone swirl (`-blue`, `-red`, `-teal`) and a metallic glossy finish (`-gold`, `-silver`, `-cobalt`), as `ball:soccer-stars-blue` and so on.
|
|
18
|
+
- **Five baseballs and softballs**: `ball:baseball-lemon-peel` (the early four-seam ball), `ball:baseball-red-blue` and `ball:baseball-black-red` (the figure-8 seam stitched in two colours, the laces of the two leagues in a 1930s player's account), `ball:baseball-vintage-leather` and `ball:softball-11-optic` (the 27.9 cm optic yellow ball of the WBSC Slow Pitch rules, 166.5 to 173.6 g). The red figure-8 stitch is the existing `ball:baseball`.
|
|
19
|
+
- **Eleven volleyballs in national colours**: `ball:volleyball-brazil`, `-japan`, `-canada`, `-usa`, `-italy`, `-poland`, `-france`, `-serbia`, `-netherlands` and `-germany`, the 18-panel indoor ball with its panels in three colours of the flag (colours only, no flag drawn), and `ball:volleyball-colours`. A new **`colors` option** (`renderEmblem`, `emblemFrame`, `ballFrame`) takes any three colours for any of them.
|
|
20
|
+
- **New tilings and tools**: `octahedron8` (an octahedron whose edges are bent into S curves), `icosahedron`, `gorePoint` and `colourCentres` in `ball.tilings.ts`; `ball.decal.ts` (shapes laid on the sphere with `frameAt`, `tongue` leaves and ribbons, rows of stitches, a lace, scuffs); `paint18` and `seams18` in the panelled family; `panels` on the engine's surface (several polygons of one fill as one path); `wear` and a second thread colour in the seamed balls.
|
|
21
|
+
- Tests (`test/vintage.test.ts`) for the new ids, names and descriptions, the sizes, the `colors` option, the new tilings and the decal tools. The contact sheets in `docs/pieces`, the README's ball picture (now with a row of vintage balls) and both gallery pages are redrawn and rewritten.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- The `ball:soccer` family is now 55 balls in the proportions test (the 21 of 0.3.0, the five vintage, eleven era-style and eighteen patterned), and the balls hold their size budget (14 kB in colour, 13 kB as a line drawing; 8.5 kB on average).
|
|
26
|
+
|
|
27
|
+
## [0.4.1] - 2026-10-14
|
|
28
|
+
|
|
29
|
+
The printed pucks take the same flag renderer as taisen-brackets.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- **The puck's flag renderer is the one taisen-brackets takes.** `flags` is now an option of the call (`renderEmblem`, `emblemFrame`, `ballFrame`), beside `face`, and no longer a field of `face`; it is called `flags(code, { shape: "round" | "square" | "rect", size, theme: "inherit" | "light" | "dark" })` (the puck asks for `"rect"`), may return `null` or `undefined` for a code it has no flag for, and is synchronous, so one adapter over hata serves every package. New types `FlagRequest` and `FlagShape`. `FlagRenderer` changed accordingly; the 0.4.0 form (`face.flags`, shapes `4:3`/`1:1`/`round`) is gone.
|
|
34
|
+
|
|
9
35
|
## [0.4.0] - 2026-10-14
|
|
10
36
|
|
|
11
37
|
Printed pucks: eight souvenir pucks with a print on the top face that turns with the puck, and a `face` option that takes any picture, emblem or flag code. 194 emblems.
|
|
@@ -132,7 +158,9 @@ First preview: the name, the API and two sample emblems. The full set of trophie
|
|
|
132
158
|
- Tests for the drawing, the ids, the turns and the bundle size of one item; a package check that installs the packed tarball and bundles one item from it.
|
|
133
159
|
- The demo (a playground with a turn slider, and a gallery page), `docs/layout.md`, `docs/turning.md`, `docs/gallery.md`, the family header and footer, the CI, Pages and Release workflows, and `LICENSES/` and `NOTICE.md` ready for third-party art.
|
|
134
160
|
|
|
135
|
-
[Unreleased]: https://github.com/johnmorrisdotca/dougu-sports/compare/v0.
|
|
161
|
+
[Unreleased]: https://github.com/johnmorrisdotca/dougu-sports/compare/v0.5.0...HEAD
|
|
162
|
+
[0.5.0]: https://github.com/johnmorrisdotca/dougu-sports/compare/v0.4.1...v0.5.0
|
|
163
|
+
[0.4.1]: https://github.com/johnmorrisdotca/dougu-sports/compare/v0.4.0...v0.4.1
|
|
136
164
|
[0.4.0]: https://github.com/johnmorrisdotca/dougu-sports/compare/v0.3.2...v0.4.0
|
|
137
165
|
[0.3.2]: https://github.com/johnmorrisdotca/dougu-sports/compare/v0.3.1...v0.3.2
|
|
138
166
|
[0.3.1]: https://github.com/johnmorrisdotca/dougu-sports/compare/v0.3.0...v0.3.1
|
package/NOTICE.md
CHANGED
|
@@ -7,7 +7,7 @@ No emblem carries the name, mark or logo of a league, a team, a federation or an
|
|
|
7
7
|
## How the art is made
|
|
8
8
|
|
|
9
9
|
- The trophies, medals, crests, awards and sport pictograms are drawn in SVG by hand in a simplified, generic form. No real trophy, medal, logo, mark or engraving was copied or traced, and no photograph was traced (photographs were looked at, never copied).
|
|
10
|
-
- The balls (`src/emblems/ball/`) are drawn by this package's own code: a rotation, a projection and our own geometry (a seam curve, the truncated icosahedron, a layout of curved panels, a fitted basketball channel).
|
|
10
|
+
- The balls (`src/emblems/ball/`) are drawn by this package's own code: a rotation, a projection and our own geometry (a seam curve, the truncated icosahedron, an octahedron and an icosahedron, a layout of curved panels, a fitted basketball channel). The vintage, era-style and patterned balls ([docs/vintage-balls.md](docs/vintage-balls.md)) are our own graphics laid on those constructions (shapes placed on the sphere, rows of stitches, a lace, scuffs): the shop-wall patterns and the era styles were suggested by photographs the package's author supplied, which were looked at and not copied or traced, and no real ball's pattern, mark or name is used. The volleyballs take only three colours from a country's flag, and draw no flag.
|
|
11
11
|
- The printed pucks (`puck:*`, `src/emblems/ball/ball.puck.print.ts`) are our own drawings: the maple leaf, the flags of the Canadian and United States patterns and the stars are drawn from simple geometry, the lettering is a stroke alphabet adapted from the block letters of the author's own senshu-jerseys (same author, same MIT licence, no font file), and the signature is made by a seeded pen, so it is no real person's. Photographs of souvenir pucks were looked at for what such a puck shows (a flag, a name, a number, a signature, a ring of words) and nothing was copied or traced. No team's or league's logo or wordmark, no maker's mark and no real player's name is used.
|
|
12
12
|
- The gear (`src/emblems/gear/`) is drawn by us from a list of parts (a path, a colour, how it reads as a line); the bow, the arrow and the other tapering shapes are made by a small function of ours (`swell`), and the rest are paths written for this package.
|
|
13
13
|
- The Japanese words for the Japanese items are the ones the sports' governing bodies use in their own rules and equipment lists (kendo, judo, kyudo and karate federations and the sumo association); the bodies' names and marks are not used in any id, title or drawing.
|
|
@@ -22,19 +22,21 @@ Each ball's `size.rule` names the rule; the table gives the document and where i
|
|
|
22
22
|
|
|
23
23
|
| Balls | Rulebook and rule | Read at |
|
|
24
24
|
| --- | --- | --- |
|
|
25
|
-
| `ball:soccer`, `ball:soccer-colour`, `ball:soccer-26`, `ball:soccer-18`, `ball:soccer-14`, `ball:soccer-12`, `ball:soccer-6`, `ball:soccer-orange`, `ball:soccer-26-orange`, `ball:soccer-18-orange`, `ball:soccer-14-orange`, `ball:soccer-12-orange`, `ball:soccer-6-orange`, `ball:soccer-yellow`, `ball:soccer-26-yellow`, `ball:soccer-18-yellow`, `ball:soccer-14-yellow`, `ball:soccer-12-yellow`, `ball:soccer-6-yellow` | IFAB Laws of the Game, Law 2 The Ball, section 2.1 Qualities and measurements | https://www.theifab.com/laws/latest/the-ball/ |
|
|
25
|
+
| `ball:soccer`, `ball:soccer-colour`, `ball:soccer-26`, `ball:soccer-18`, `ball:soccer-14`, `ball:soccer-12`, `ball:soccer-6`, `ball:soccer-orange`, `ball:soccer-26-orange`, `ball:soccer-18-orange`, `ball:soccer-14-orange`, `ball:soccer-12-orange`, `ball:soccer-6-orange`, `ball:soccer-yellow`, `ball:soccer-26-yellow`, `ball:soccer-18-yellow`, `ball:soccer-14-yellow`, `ball:soccer-12-yellow`, `ball:soccer-6-yellow`, `ball:soccer-1960-32-panel-leather`, `ball:soccer-1970-black-white`, `ball:soccer-1978-triad`, `ball:soccer-1986-decorated-triad`, `ball:soccer-1998-coloured-triad`, `ball:soccer-2002-triangle-sweep`, `ball:soccer-2006-propeller`, `ball:soccer-2010-grooved`, `ball:soccer-2014-ribbon`, `ball:soccer-2018-pixel`, `ball:soccer-2022-speed-shell`, `ball:soccer-training-high-vis`, `ball:soccer-stars-blue`, `ball:soccer-stars-red`, `ball:soccer-stars-night`, `ball:soccer-hexagons-cyan`, `ball:soccer-hexagons-magenta`, `ball:soccer-hexagons-lime`, `ball:soccer-flames-fire`, `ball:soccer-flames-ice`, `ball:soccer-flames-toxic`, `ball:soccer-chevrons-red`, `ball:soccer-chevrons-green`, `ball:soccer-chevrons-blue`, `ball:soccer-swirl-blue`, `ball:soccer-swirl-red`, `ball:soccer-swirl-teal`, `ball:soccer-metallic-gold`, `ball:soccer-metallic-silver`, `ball:soccer-metallic-cobalt` | IFAB Laws of the Game, Law 2 The Ball, section 2.1 Qualities and measurements | https://www.theifab.com/laws/latest/the-ball/ |
|
|
26
26
|
| `ball:futsal` | Futsal Laws of the Game 2025/26, Law 2 The Ball, section 1 Qualities and measurements (circumference 62-64 cm, 400-440 g, 0.6-0.9 atm, bounce 50-65 cm from 2 m) | https://digitalhub.fifa.com/m/20d52e6779b53321/original/FUTSAL-Laws-of-the-Game-2025-2026.pdf |
|
|
27
27
|
| `ball:beach-soccer` | Beach Soccer Laws of the Game 2024-25, Law 2 The Ball, section 1 Qualities and measurements (circumference 68-70 cm, 400-440 g, 0.4-0.6 atm); the 2021/22 edition has the same figures | https://www.the-aiff.com/media/uploads/2024/11/Beach-Soccer-Laws-of-the-Game-2024-25.pdf |
|
|
28
|
+
| `ball:soccer-1870-hand-stitched`, `ball:soccer-1900-laced`, `ball:soccer-1930-18-panel`, `ball:soccer-1950-12-panel` | Laws of the Game of the Football Association, revised 1872: a sphere 27 to 28 inches round (no mass given: the early figures could not be confirmed in a primary text); read in Wikipedia's account of the 1872 revision (secondary) | https://en.wikipedia.org/wiki/Ball_(association_football) |
|
|
28
29
|
| `ball:teqball` | FITEQ Official Rules and Regulations of Teqball 2026, Rule 3 The Official Ball, 3.1.1-3.1.6 | https://cdn.buttercms.com/s6Mb1gRkRJuFP38kpMTh |
|
|
29
30
|
| `ball:basketball` | FIBA Basketball Equipment 2026, Rule 2 Basketballs, 2.4 and Table 1 (size 7 = men's competitions) | https://assets.fiba.basketball/image/upload/documents-corporate-fiba-official-rules-2026-equipment-v1-0.pdf |
|
|
30
31
|
| `ball:basketball-6` | FIBA Basketball Equipment 2026, Rule 2 Basketballs, 2.4 and Table 1 (size 6 = women's competitions) | https://assets.fiba.basketball/image/upload/documents-corporate-fiba-official-rules-2026-equipment-v1-0.pdf |
|
|
31
32
|
| `ball:basketball-3x3` | FIBA 3x3 Official Equipment & Software Appendix to the 3x3 Official Rules (Dec 2024 update), Table 1 and section 5.7 (as quoted by balls.com) (not read in the primary text: see the ball's note) | https://fiba3x3.com/docs/equipment-and-software-appendix-to-the-3x3-official-rules.pdf |
|
|
32
|
-
| `ball:baseball` | Official Baseball Rules, Rule 3.01 The Ball | https://mktg.mlbstatic.com/mlb/official-information/2026-official-baseball-rules.pdf |
|
|
33
|
+
| `ball:baseball`, `ball:baseball-lemon-peel`, `ball:baseball-red-blue`, `ball:baseball-black-red`, `ball:baseball-vintage-leather` | Official Baseball Rules, Rule 3.01 The Ball (the lemon-peel ball is drawn at today's size: no rule fixed the ball in the 1850s and 1860s) | https://mktg.mlbstatic.com/mlb/official-information/2026-official-baseball-rules.pdf |
|
|
34
|
+
| `ball:softball-11-optic` | WBSC Official Rules of Softball Slow Pitch 2026-2029, Appendix 3 (Ball Standards), B.2 and the 'Ball standards' table: the 27.9 cm (11 in) ball, circumference 27.6 to 28.3 cm, 166.5 to 173.6 g, white or yellow optic, red stitch (read in the full text, 2026-10-14) | https://static.wbsc.org/uploads/federations/0/cms/documents/5f45c5e4-54ed-3659-8116-4d7cb5051f69.pdf |
|
|
33
35
|
| `ball:softball` | WBSC Official Rules of Softball Slow Pitch 2026-2029, Appendix 3 (Ball Specifications), B. Dimensions and Specifications and 'Ball standards' table; Fast Pitch 2026-2029 Appendix 3 gives the same 12 in ball | https://static.wbsc.org/uploads/federations/0/cms/documents/5f45c5e4-54ed-3659-8116-4d7cb5051f69.pdf |
|
|
34
36
|
| `ball:tennis` | ITF Rules of Tennis, Rule 3 The Ball and Appendix I The Ball (a)-(c) | https://www.itftennis.com/media/7221/2026-rules-of-tennis-english.pdf |
|
|
35
37
|
| `ball:padel` | FIP Rules of Padel, 'The Ball' article (as extracted in FIP Game Ball Certification Process, section B) and Appendix 'Technical specifications of the padel ball' | https://www.padelfip.com/wp-content/uploads/2026/04/Game-Ball-Certification-Process-3-1.pdf |
|
|
36
38
|
| `ball:sliotar` | GAA Official Guide Part 2, Rules of Specification, Rule 4.4(i) | https://www.galwaygaa.ie/wp-content/uploads/2026/02/Official-Guide-Part-2.pdf |
|
|
37
|
-
| `ball:volleyball`, `ball:volleyball-8` | FIVB Official Volleyball Rules 2025-2028, Rule 3 Balls, 3.1 Standards | https://www.fivb.com/wp-content/uploads/2025/01/FIVB-Volleyball_Rules2025_2028-EN-v05.pdf |
|
|
39
|
+
| `ball:volleyball`, `ball:volleyball-8`, `ball:volleyball-colours`, `ball:volleyball-brazil`, `ball:volleyball-japan`, `ball:volleyball-canada`, `ball:volleyball-usa`, `ball:volleyball-italy`, `ball:volleyball-poland`, `ball:volleyball-france`, `ball:volleyball-serbia`, `ball:volleyball-netherlands`, `ball:volleyball-germany` | FIVB Official Volleyball Rules 2025-2028, Rule 3 Balls, 3.1 Standards | https://www.fivb.com/wp-content/uploads/2025/01/FIVB-Volleyball_Rules2025_2028-EN-v05.pdf |
|
|
38
40
|
| `ball:beach-volleyball` | FIVB Official Beach Volleyball Rules 2025-2028, Rule 3 Balls, 3.1 Standards | https://www.fivb.com/wp-content/uploads/2025/02/FIVB-BeachVolleyball_Rules2025_2028-EN-v01.pdf |
|
|
39
41
|
| `ball:cricket-red`, `ball:cricket-white`, `ball:cricket-pink` | Laws of Cricket, Law 4 The Ball, 4.1 Weight and size (Size 1, Size 2, Size 3) | https://www.lords.org/getmedia/1d908298-5c44-468d-b6a7-e1414a1296e0/Laws-of-Cricket-2017-Code-4th-Edition-(2026)_3.pdf |
|
|
40
42
|
| `ball:football` | NFL Rulebook, Rule 2 The Ball, Section 1 (Dimensions) | https://operations.nfl.com/rules-officiating/2026-nfl-rulebook |
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ The ball and the gear of every sport, trophies, medals, crests and the icon of e
|
|
|
16
16
|
<p align="center">
|
|
17
17
|
<picture>
|
|
18
18
|
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/dougu-sports/main/docs/images/balls-desk-dark.webp">
|
|
19
|
-
<img alt="
|
|
19
|
+
<img alt="Forty-five balls and pucks drawn in 3D, in five rows of nine: soccer balls of several constructions and colours, then basketball, the 3x3 ball, baseball, softball, a football, a rugby ball, an Australian football and two volleyballs, then tennis, padel, pickleball with its holes, squash with its two dots, racquetball, golf, red and pink cricket and water polo, then sepak takraw, a shuttlecock, a puck, a curling stone, lacrosse, handball, table tennis, a sliotar and a roundnet ball, and last a row of vintage leather balls (hand-stitched, laced, 18 strips, 12 panels, 32 panels), a lemon-peel baseball and an aged baseball, and soccer balls in the style of the late 1970s and the early 2020s." src="https://raw.githubusercontent.com/johnmorrisdotca/dougu-sports/main/docs/images/balls-desk-light.webp" width="760">
|
|
20
20
|
</picture>
|
|
21
21
|
</p>
|
|
22
22
|
|
|
@@ -45,7 +45,7 @@ The ball and the gear of every sport, trophies, medals, crests and the icon of e
|
|
|
45
45
|
</tr>
|
|
46
46
|
</table>
|
|
47
47
|
|
|
48
|
-
> **Status: preview.**
|
|
48
|
+
> **Status: preview.** 244 emblems: 24 trophies, 5 awards, 6 medals, 4 crests, a pictogram for each of the 38 sports, 116 balls and implements drawn in 3D, 8 printed pucks ([docs/pucks.md](./docs/pucks.md)), 42 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.
|
|
49
49
|
|
|
50
50
|
## In 30 seconds
|
|
51
51
|
|
|
@@ -73,7 +73,7 @@ Put the string in a page (`element.innerHTML = svg`), write it to a file, or sen
|
|
|
73
73
|
|
|
74
74
|
## Features
|
|
75
75
|
|
|
76
|
-
- **Equipment first (道具 is the word for it)**:
|
|
76
|
+
- **Equipment first (道具 is the word for it)**: 116 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).
|
|
77
77
|
- **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).
|
|
78
78
|
- **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.
|
|
79
79
|
- **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).
|
|
@@ -407,7 +407,7 @@ An emblem is a small object: `id`, `en` and `ja` names, a `source` (who drew it)
|
|
|
407
407
|
| `trophy` | Twenty-four trophies: ten that stand for a league's cup in a generic form (a tall banded cup, a ring of pennants round a baseball, a football on a stand, a ball over a net, a wide two-handled bowl, a globe held up by spiral figures, a gilt dish, a lidded cup with a pineapple, an urn, a gilded cup), plain cups, bowls and dishes, a plaque, a belt, a wreath, a rosette, a championship ring, a rafter banner, a phoenix-handled sumo cup and a championship banner | `src/emblems/trophy/` |
|
|
408
408
|
| `medal` | Gold, silver and bronze medals on a ribbon of five stripes, and a badge for each of the first three places | `src/emblems/medal/` |
|
|
409
409
|
| `crest` | Four generic crests, tinted by a team's colours, with its letters | `src/emblems/crest/` |
|
|
410
|
-
| `ball` |
|
|
410
|
+
| `ball` | 116 balls and implements, in 3D, each with its official size: soccer (in 32, 26, 18, 14, 12 and 6 panels, in three colourways, with futsal and beach soccer; [docs/soccer-balls.md](./docs/soccer-balls.md); and vintage leather, era-style and patterned balls, [docs/vintage-balls.md](./docs/vintage-balls.md)), basketball, baseball, tennis, rugby, the footballs, golf, the puck, the curling stone and the rest | `src/emblems/ball/` |
|
|
411
411
|
| `puck` | 8 printed pucks: the same puck with a print on its top face that turns with it (a flag and CANADA, a red leaf, stars and stripes, a player's name and number, a made-up signature, a souvenir), and a `face` option for any picture, emblem or flag code; [docs/pucks.md](./docs/pucks.md) | `src/emblems/ball/ball.puck.print.ts` |
|
|
412
412
|
| `gear` | 42 pieces of equipment: sticks, rackets, skates, a glove, a helmet, nets and posts, a javelin, starting blocks, and for the Japanese sports a war fan, a sumo belt, a bamboo sword, a kendo helmet, a judo suit, a belt, a long bow, an arrow and a karate suit | `src/emblems/gear/` |
|
|
413
413
|
| `sport` | One pictogram for each of the thirty-eight sports | `src/emblems/sport/` |
|
|
@@ -459,6 +459,7 @@ The thirty-eight sports the ball, gear and sport emblems are made for are `SPORT
|
|
|
459
459
|
| `size` | A number of pixels, 4096 at most | `64` |
|
|
460
460
|
| `tint` | `#rgb`, `#rrggbb` or `rgb(r, g, b)` | the emblem's own colour |
|
|
461
461
|
| `accent` | `#rgb`, `#rrggbb` or `rgb(r, g, b)` | the emblem's second colour (a ribbon, a band, pennants) |
|
|
462
|
+
| `colors` | Three colours, each `#rgb`, `#rrggbb` or `rgb(r, g, b)` (a volleyball in national colours: [docs/vintage-balls.md](./docs/vintage-balls.md#volleyballs-in-national-colours-11)); fewer are repeated, and what is not a colour is dropped | the ball's own three |
|
|
462
463
|
| `initials` | Up to three letters (a crest, or the championship banner) | none: the crest has a star, the banner a ring and a star |
|
|
463
464
|
| `theme` | `"inherit"`, `"auto"`, `"light"`, `"dark"` | `"inherit"` |
|
|
464
465
|
| `grain` | `true` or `false` (a ball only) | on for a ball of 48 pixels or more that is not turning |
|
|
@@ -593,7 +594,7 @@ Bug reports and ideas go in the [issues](https://github.com/johnmorrisdotca/doug
|
|
|
593
594
|
|
|
594
595
|
## Changes
|
|
595
596
|
|
|
596
|
-
See [CHANGELOG.md](./CHANGELOG.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.
|
|
597
|
+
See [CHANGELOG.md](./CHANGELOG.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.
|
|
597
598
|
|
|
598
599
|
## Credits
|
|
599
600
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The 1850s and 1860s: a cover of four pieces sewn along four seams that run from one point to the opposite one, like the peel of a lemon, in aged leather with a dark thread. No rule fixed the ball then, so it is drawn at the size of today's. */
|
|
2
|
+
export declare const BALL_BASEBALL_LEMON_PEEL: import("./ball.core.ts").Piece<import("./ball.engine.ts").BallSpec>;
|
|
3
|
+
/** The figure-8 seam stitched with alternate red and blue stitches (the American League's laces, in the recollection of a 1930s player). */
|
|
4
|
+
export declare const BALL_BASEBALL_RED_BLUE: import("./ball.core.ts").Piece<import("./ball.engine.ts").BallSpec>;
|
|
5
|
+
/** The figure-8 seam stitched with alternate black and red stitches (the National League's laces, in the same recollection). */
|
|
6
|
+
export declare const BALL_BASEBALL_BLACK_RED: import("./ball.core.ts").Piece<import("./ball.engine.ts").BallSpec>;
|
|
7
|
+
/** An old ball: the leather gone the colour of tea and scuffed, the red thread faded. */
|
|
8
|
+
export declare const BALL_BASEBALL_VINTAGE_LEATHER: import("./ball.core.ts").Piece<import("./ball.engine.ts").BallSpec>;
|
|
9
|
+
/** The optic yellow 11 inch softball: 27.9 cm (circumference 27.6 to 28.3 cm, 166.5 to 173.6 g), red stitch, as the WBSC's Slow Pitch rules set it for women and girls. */
|
|
10
|
+
export declare const BALL_SOFTBALL_11_OPTIC: import("./ball.core.ts").Piece<import("./ball.engine.ts").BallSpec>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Baseballs of other times and threads: the early "lemon-peel" ball (four lines of stitching that meet at two points), the figure-8 ball with a seam stitched in red and blue and in black and red (the
|
|
2
|
+
// colours of the two leagues' laces in the 1930s), an aged leather one, and an optic yellow 11 inch softball. The figure-8 seam is the parametric curve of ball.math.ts, as for the modern baseball.
|
|
3
|
+
import { defineBall } from "./ball.engine.js";
|
|
4
|
+
import { mix } from "./ball.core.js";
|
|
5
|
+
import { across, scuffs } from "./ball.decal.js";
|
|
6
|
+
import { seamedBall } from "./ball.family.seamed.js";
|
|
7
|
+
import { gores } from "./ball.tilings.js";
|
|
8
|
+
import { BALL_BASEBALL } from "./ball.baseball.js";
|
|
9
|
+
const SIZE = BALL_BASEBALL.size;
|
|
10
|
+
/** The 1850s and 1860s: a cover of four pieces sewn along four seams that run from one point to the opposite one, like the peel of a lemon, in aged leather with a dark thread. No rule fixed the ball then, so it is drawn at the size of today's. */
|
|
11
|
+
export const BALL_BASEBALL_LEMON_PEEL = /* @__PURE__ */ defineBall({
|
|
12
|
+
id: "ball:baseball-lemon-peel",
|
|
13
|
+
en: "Baseball, lemon-peel",
|
|
14
|
+
ja: "レモンピール型野球ボール",
|
|
15
|
+
size: { ...SIZE, rule: "Official Baseball Rules, Rule 3.01 The Ball (today's size; no rule fixed the ball in the 1850s and 1860s)", note: "Drawn at the size of today's ball. Diameter worked out from the middle of the circumference range." },
|
|
16
|
+
spec: {
|
|
17
|
+
view: { yaw: 18, pitch: 30, roll: -10 },
|
|
18
|
+
base: "#e1cfa6",
|
|
19
|
+
mat: { gloss: 0.25, shade: "#4a3a2a", grain: { f: 1.6, a: 0.2 } },
|
|
20
|
+
draw: (s, base, accent) => {
|
|
21
|
+
const g = gores(4, 0, [0, 28, 0], 24, 4);
|
|
22
|
+
let out = "";
|
|
23
|
+
for (let i = 0; i < 4; i += 2)
|
|
24
|
+
out += s.panel(g.panels[i], mix(base, -0.07), { n: 1 });
|
|
25
|
+
out += scuffs(s, mix(base, -0.55), mix(base, 0.5), { dark: 10, pale: 5, seed: 4 });
|
|
26
|
+
out += s.strokes(g.seams, "#8a7452", 0.9);
|
|
27
|
+
out += s.strokes(across(g.seams, 0.06, 0.03, 0.012), accent ?? "#5a3a24", 0.85, { ink: false });
|
|
28
|
+
return out;
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const STITCH = { count: 108, half: 0.075, width: 0.85, lean: 0.032 };
|
|
33
|
+
const WHITE = "#f4f0e6";
|
|
34
|
+
const common = { size: SIZE, base: WHITE, b: 0.4, view: { yaw: 18, pitch: 28, roll: -10 }, mat: { gloss: 0.3, shade: "#4a3a2a", grain: { f: 1.4, a: 0.12 } }, groove: "#8a7a62" };
|
|
35
|
+
/** The figure-8 seam stitched with alternate red and blue stitches (the American League's laces, in the recollection of a 1930s player). */
|
|
36
|
+
export const BALL_BASEBALL_RED_BLUE = /* @__PURE__ */ seamedBall({ ...common, id: "ball:baseball-red-blue", en: "Baseball, red and blue", ja: "赤青の縫い糸の野球ボール", stitch: { ...STITCH, colour: "#c4262e", colour2: "#2257a8" } });
|
|
37
|
+
/** The figure-8 seam stitched with alternate black and red stitches (the National League's laces, in the same recollection). */
|
|
38
|
+
export const BALL_BASEBALL_BLACK_RED = /* @__PURE__ */ seamedBall({ ...common, id: "ball:baseball-black-red", en: "Baseball, black and red", ja: "黒赤の縫い糸の野球ボール", stitch: { ...STITCH, colour: "#c4262e", colour2: "#1d1d20" } });
|
|
39
|
+
/** An old ball: the leather gone the colour of tea and scuffed, the red thread faded. */
|
|
40
|
+
export const BALL_BASEBALL_VINTAGE_LEATHER = /* @__PURE__ */ seamedBall({
|
|
41
|
+
...common,
|
|
42
|
+
id: "ball:baseball-vintage-leather",
|
|
43
|
+
en: "Baseball, aged leather",
|
|
44
|
+
ja: "古びた革の野球ボール",
|
|
45
|
+
base: "#d8c08e",
|
|
46
|
+
mat: { gloss: 0.2, shade: "#43301c", grain: { f: 1.7, a: 0.26 } },
|
|
47
|
+
groove: "#7a6240",
|
|
48
|
+
stitch: { ...STITCH, colour: "#a23a2c" },
|
|
49
|
+
wear: { dark: 16, pale: 8, seed: 6 },
|
|
50
|
+
});
|
|
51
|
+
/** The optic yellow 11 inch softball: 27.9 cm (circumference 27.6 to 28.3 cm, 166.5 to 173.6 g), red stitch, as the WBSC's Slow Pitch rules set it for women and girls. */
|
|
52
|
+
export const BALL_SOFTBALL_11_OPTIC = /* @__PURE__ */ seamedBall({
|
|
53
|
+
id: "ball:softball-11-optic",
|
|
54
|
+
en: "Softball, 11 in, optic",
|
|
55
|
+
ja: "11インチ蛍光ソフト",
|
|
56
|
+
size: {
|
|
57
|
+
extentMm: 279.5 / Math.PI,
|
|
58
|
+
diameterMm: 279.5 / Math.PI,
|
|
59
|
+
circumferenceMm: 279.5,
|
|
60
|
+
massG: [166.5, 173.6],
|
|
61
|
+
rule: "WBSC Official Rules of Softball Slow Pitch 2026-2029, Appendix 3 (Ball Standards), B.2 and the 'Ball standards' table: the 27.9 cm (11 in) ball, white or yellow optic cover, red stitch",
|
|
62
|
+
note: "Circumference 27.6 to 28.3 cm. Diameter worked out from the middle of the range. The rule asks for at least 80 stitches in each cover on a flat-seam ball (88 on the 12 in ball); 80 are drawn.",
|
|
63
|
+
},
|
|
64
|
+
base: "#d8e22a",
|
|
65
|
+
b: 0.4,
|
|
66
|
+
view: { yaw: 18, pitch: 28, roll: -10 },
|
|
67
|
+
mat: { gloss: 0.35, shade: "#4c5a10", grain: { f: 1.4, a: 0.12 } },
|
|
68
|
+
groove: "#8a9420",
|
|
69
|
+
stitch: { colour: "#d0262c", count: 80, half: 0.085, width: 0.95, lean: 0.04 },
|
|
70
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PuckFace } from "./ball.face.ts";
|
|
1
|
+
import type { FlagRenderer, PuckFace } from "./ball.face.ts";
|
|
2
2
|
/** `metal` (also written `colour`): full colour, with shading. `line`: one outline in `currentColor`. */
|
|
3
3
|
export type PieceStyle = "metal" | "colour" | "color" | "line";
|
|
4
4
|
/** `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. */
|
|
@@ -19,8 +19,12 @@ export interface PieceOptions {
|
|
|
19
19
|
locale?: "en" | "ja";
|
|
20
20
|
/** A string is the `<title>` (escaped); `false` drops it and marks the drawing `aria-hidden="true"`. */
|
|
21
21
|
title?: string | false;
|
|
22
|
+
/** A ball with panels in several colours (the national volleyballs): the colours of its panels, three of them, each a hex colour or `rgb()` (any that is not is dropped; one or two are repeated to make three). Anything else ignores it. */
|
|
23
|
+
colors?: readonly string[];
|
|
22
24
|
/** A puck: the print on its top face (see `PuckFace`). The printed pucks (`puck:*`) take it, and so does `ball:puck` through `renderEmblem`, `emblemFrame` and `ballFrame`. Anything else ignores it. */
|
|
23
25
|
face?: PuckFace;
|
|
26
|
+
/** A puck: a renderer for the flag codes this package does not draw (`face.flag`), `(code, { shape, size, theme }) => svg`, the one taisen-brackets takes. Synchronous. */
|
|
27
|
+
flags?: FlagRenderer;
|
|
24
28
|
}
|
|
25
29
|
/** A turn: `yaw` degrees about the vertical axis (0 is the default view), `pitch` the tilt toward you, `roll` the turn about the line of sight. */
|
|
26
30
|
export interface BallFrameOptions extends PieceOptions {
|
|
@@ -87,8 +91,11 @@ export interface ResolvedOptions {
|
|
|
87
91
|
theme: PieceTheme;
|
|
88
92
|
tint: string | undefined;
|
|
89
93
|
accent: string | undefined;
|
|
94
|
+
/** Three colours for the panels, or undefined. */
|
|
95
|
+
colors: readonly [string, string, string] | undefined;
|
|
90
96
|
grain: boolean | undefined;
|
|
91
97
|
face: PuckFace | undefined;
|
|
98
|
+
flags: FlagRenderer | undefined;
|
|
92
99
|
/** A string that tells two faces apart, for the ids of a drawing. */
|
|
93
100
|
faceKey: string;
|
|
94
101
|
}
|
|
@@ -53,12 +53,22 @@ function faceKeyOf(face) {
|
|
|
53
53
|
return "?";
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
/** Up to three colours from a list, any that is not a colour dropped and the rest repeated to make three; undefined when none is one. */
|
|
57
|
+
function coloursOf(list) {
|
|
58
|
+
if (!Array.isArray(list))
|
|
59
|
+
return undefined;
|
|
60
|
+
const ok = list.slice(0, 3).flatMap((c) => {
|
|
61
|
+
const rgb = typeof c === "string" ? readColour(c) : undefined;
|
|
62
|
+
return rgb === undefined ? [] : [hexOf(rgb)];
|
|
63
|
+
});
|
|
64
|
+
return ok.length === 0 ? undefined : [ok[0], ok[1 % ok.length], ok[2 % ok.length]];
|
|
65
|
+
}
|
|
56
66
|
export function resolveOptions(o) {
|
|
57
67
|
const size = typeof o?.size === "number" && Number.isFinite(o.size) && o.size > 0 ? Math.min(Math.round(o.size * 100) / 100, 4096) : 64;
|
|
58
68
|
const theme = o?.theme === "auto" || o?.theme === "light" || o?.theme === "dark" ? o.theme : "inherit";
|
|
59
69
|
const tint = readColour(o?.tint);
|
|
60
70
|
const accent = readColour(o?.accent);
|
|
61
|
-
return { style: o?.style === "line" ? "line" : "colour", size, theme, tint: tint === undefined ? undefined : hexOf(tint), accent: accent === undefined ? undefined : hexOf(accent), grain: typeof o?.grain === "boolean" ? o.grain : undefined, face: typeof o?.face === "object" && o.face !== null ? o.face : undefined, faceKey: faceKeyOf(o?.face) };
|
|
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}` : "") };
|
|
62
72
|
}
|
|
63
73
|
/** The ink of a drawing when a theme sets it, and the page it is made to read on. */
|
|
64
74
|
export const PIECE_INK = { light: "#20262d", dark: "#eef1f3" };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Surface } from "./ball.engine.ts";
|
|
2
|
+
import { type V3 } from "./ball.math.ts";
|
|
3
|
+
type P2 = readonly [number, number];
|
|
4
|
+
/** A frame on the sphere at `c`: `(x, y)` is a point of the plane that touches the sphere there (x toward `toward`, y a quarter turn from it), carried onto the sphere. */
|
|
5
|
+
export declare function frameAt(c: V3, toward?: V3): (x: number, y: number) => V3;
|
|
6
|
+
/** A flat shape put on the sphere. */
|
|
7
|
+
export declare const place: (frame: (x: number, y: number) => V3, shape: readonly P2[]) => V3[];
|
|
8
|
+
/** The shape turned by `angle` radians about the middle and scaled. */
|
|
9
|
+
export declare function turned(shape: readonly P2[], angle: number, scale?: number): P2[];
|
|
10
|
+
/** A regular polygon of `n` sides with a corner at angle `start`. */
|
|
11
|
+
export declare const ngon: (n: number, r: number, start?: number) => P2[];
|
|
12
|
+
/** A star of `n` points, corners alternately at `outer` and `inner`. */
|
|
13
|
+
export declare const star: (n: number, outer: number, inner: number, start?: number) => P2[];
|
|
14
|
+
/**
|
|
15
|
+
* A tongue: a shape along a spine, `spine(u)` for u from 0 to 1, as wide as `width(u)` (to each side of the spine, so twice that across). It is a leaf, a flame, a blade, a ribbon or a shard according to the
|
|
16
|
+
* spine and the width given. The result is a closed outline of `2 (steps + 1)` points.
|
|
17
|
+
*/
|
|
18
|
+
export declare function tongue(spine: (u: number) => P2, width: (u: number) => number, steps?: number): P2[];
|
|
19
|
+
/** A spiral spine from radius `r0` to `r1` that turns through `sweep` radians, starting at angle `a`. */
|
|
20
|
+
export declare const spiral: (r0: number, r1: number, a: number, sweep: number) => (u: number) => P2;
|
|
21
|
+
/** A straight spine from one point to another. */
|
|
22
|
+
export declare const line: (a: P2, b: P2) => (u: number) => P2;
|
|
23
|
+
/** A bump that is 0 at both ends and `h` in the middle; `p` below 1 makes it fuller. */
|
|
24
|
+
export declare const lens: (h: number, p?: number) => (u: number) => number;
|
|
25
|
+
/** A taper from `h` at the start to 0 at the end. */
|
|
26
|
+
export declare const taper: (h: number, p?: number) => (u: number) => number;
|
|
27
|
+
/** The length of a curve on the sphere, in radians. */
|
|
28
|
+
export declare function lengthOf(pts: readonly V3[]): number;
|
|
29
|
+
/** A curve resampled at points a fixed distance apart along it (radians), the ends included. */
|
|
30
|
+
export declare function resample(pts: readonly V3[], step: number): V3[];
|
|
31
|
+
/** A curve moved sideways along the sphere by `d` radians (to the left of its direction for a positive `d`). */
|
|
32
|
+
export declare function offset(pts: readonly V3[], d: number): V3[];
|
|
33
|
+
/** Short stitches along a seam, running with it: a dash and a gap of `step / 2` each. `side` moves the row to one side of the seam. */
|
|
34
|
+
export declare function running(curves: ReadonlyArray<readonly V3[]>, step?: number, side?: number): V3[][];
|
|
35
|
+
/** Stitches across a seam: a short mark every `step` radians, `half` to each side of the seam, leaning along it by `lean` (a hand stitch's cross). */
|
|
36
|
+
export declare function across(curves: ReadonlyArray<readonly V3[]>, step?: number, half?: number, lean?: number): V3[][];
|
|
37
|
+
/** A small deterministic number in [0, 1) from two integers, for scuffs and pixels that must be the same every time. */
|
|
38
|
+
export declare function noise(a: number, b?: number): number;
|
|
39
|
+
/** The scuffs of worn leather: soft dark patches and a few pale ones, spread over the ball. */
|
|
40
|
+
export declare function scuffs(s: Surface, dark: string, pale: string, o?: {
|
|
41
|
+
dark?: number;
|
|
42
|
+
pale?: number;
|
|
43
|
+
seed?: number;
|
|
44
|
+
size?: number;
|
|
45
|
+
}): string;
|
|
46
|
+
/**
|
|
47
|
+
* A lace across a slit: the slit along `along` from the middle `c` (`length` radians long), a row of eyelets down each side and a lace that crosses from side to side, tied off with two ends. Returns the
|
|
48
|
+
* slit, the eyelet centres, the crossing strokes and the ends, for the caller to draw.
|
|
49
|
+
*/
|
|
50
|
+
export declare function lacing(c: V3, along: V3, length: number, pairs: number, gap?: number): {
|
|
51
|
+
slit: V3[];
|
|
52
|
+
eyelets: V3[];
|
|
53
|
+
crossings: V3[][];
|
|
54
|
+
ends: V3[][];
|
|
55
|
+
};
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { add, basis, cross, dot, mul, norm, slerp, spread, sub } from "./ball.math.js";
|
|
2
|
+
/** A frame on the sphere at `c`: `(x, y)` is a point of the plane that touches the sphere there (x toward `toward`, y a quarter turn from it), carried onto the sphere. */
|
|
3
|
+
export function frameAt(c, toward) {
|
|
4
|
+
let ex;
|
|
5
|
+
if (toward === undefined)
|
|
6
|
+
ex = basis(c)[0];
|
|
7
|
+
else
|
|
8
|
+
ex = norm(sub(toward, mul(c, dot(toward, c))));
|
|
9
|
+
const ey = cross(c, ex);
|
|
10
|
+
return (x, y) => norm(add(c, add(mul(ex, x), mul(ey, y))));
|
|
11
|
+
}
|
|
12
|
+
/** A flat shape put on the sphere. */
|
|
13
|
+
export const place = (frame, shape) => shape.map(([x, y]) => frame(x, y));
|
|
14
|
+
/** The shape turned by `angle` radians about the middle and scaled. */
|
|
15
|
+
export function turned(shape, angle, scale = 1) {
|
|
16
|
+
const c = Math.cos(angle), s = Math.sin(angle);
|
|
17
|
+
return shape.map(([x, y]) => [(x * c - y * s) * scale, (x * s + y * c) * scale]);
|
|
18
|
+
}
|
|
19
|
+
/** A regular polygon of `n` sides with a corner at angle `start`. */
|
|
20
|
+
export const ngon = (n, r, start = 0) => Array.from({ length: n }, (_, i) => [r * Math.cos(start + (2 * Math.PI * i) / n), r * Math.sin(start + (2 * Math.PI * i) / n)]);
|
|
21
|
+
/** A star of `n` points, corners alternately at `outer` and `inner`. */
|
|
22
|
+
export const star = (n, outer, inner, start = -Math.PI / 2) => Array.from({ length: 2 * n }, (_, i) => {
|
|
23
|
+
const r = i % 2 === 0 ? outer : inner;
|
|
24
|
+
const a = start + (Math.PI * i) / n;
|
|
25
|
+
return [r * Math.cos(a), r * Math.sin(a)];
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* A tongue: a shape along a spine, `spine(u)` for u from 0 to 1, as wide as `width(u)` (to each side of the spine, so twice that across). It is a leaf, a flame, a blade, a ribbon or a shard according to the
|
|
29
|
+
* spine and the width given. The result is a closed outline of `2 (steps + 1)` points.
|
|
30
|
+
*/
|
|
31
|
+
export function tongue(spine, width, steps = 10) {
|
|
32
|
+
const left = [], right = [];
|
|
33
|
+
for (let i = 0; i <= steps; i++) {
|
|
34
|
+
const u = i / steps;
|
|
35
|
+
const [x, y] = spine(u);
|
|
36
|
+
const [x2, y2] = spine(Math.min(1, u + 0.01));
|
|
37
|
+
const [x1, y1] = spine(Math.max(0, u - 0.01));
|
|
38
|
+
const l = Math.hypot(x2 - x1, y2 - y1) || 1;
|
|
39
|
+
const nx = -(y2 - y1) / l, ny = (x2 - x1) / l;
|
|
40
|
+
const w = width(u);
|
|
41
|
+
left.push([x + nx * w, y + ny * w]);
|
|
42
|
+
right.push([x - nx * w, y - ny * w]);
|
|
43
|
+
}
|
|
44
|
+
return [...left, ...right.reverse()];
|
|
45
|
+
}
|
|
46
|
+
/** A spiral spine from radius `r0` to `r1` that turns through `sweep` radians, starting at angle `a`. */
|
|
47
|
+
export const spiral = (r0, r1, a, sweep) => (u) => [(r0 + (r1 - r0) * u) * Math.cos(a + sweep * u), (r0 + (r1 - r0) * u) * Math.sin(a + sweep * u)];
|
|
48
|
+
/** A straight spine from one point to another. */
|
|
49
|
+
export const line = (a, b) => (u) => [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u];
|
|
50
|
+
/** A bump that is 0 at both ends and `h` in the middle; `p` below 1 makes it fuller. */
|
|
51
|
+
export const lens = (h, p = 1) => (u) => h * Math.sin(Math.PI * u) ** p;
|
|
52
|
+
/** A taper from `h` at the start to 0 at the end. */
|
|
53
|
+
export const taper = (h, p = 1) => (u) => h * (1 - u) ** p;
|
|
54
|
+
/** The length of a curve on the sphere, in radians. */
|
|
55
|
+
export function lengthOf(pts) {
|
|
56
|
+
let total = 0;
|
|
57
|
+
for (let i = 0; i + 1 < pts.length; i++)
|
|
58
|
+
total += Math.acos(Math.max(-1, Math.min(1, dot(pts[i], pts[i + 1]))));
|
|
59
|
+
return total;
|
|
60
|
+
}
|
|
61
|
+
/** A curve resampled at points a fixed distance apart along it (radians), the ends included. */
|
|
62
|
+
export function resample(pts, step) {
|
|
63
|
+
const total = lengthOf(pts);
|
|
64
|
+
const n = Math.max(1, Math.round(total / step));
|
|
65
|
+
const out = [pts[0]];
|
|
66
|
+
let walked = 0;
|
|
67
|
+
let k = 0;
|
|
68
|
+
for (let i = 1; i <= n; i++) {
|
|
69
|
+
const target = (total * i) / n;
|
|
70
|
+
while (k + 1 < pts.length - 1 && walked + Math.acos(Math.max(-1, Math.min(1, dot(pts[k], pts[k + 1])))) < target) {
|
|
71
|
+
walked += Math.acos(Math.max(-1, Math.min(1, dot(pts[k], pts[k + 1]))));
|
|
72
|
+
k++;
|
|
73
|
+
}
|
|
74
|
+
const seg = Math.acos(Math.max(-1, Math.min(1, dot(pts[k], pts[k + 1])))) || 1e-9;
|
|
75
|
+
out.push(slerp(pts[k], pts[k + 1], Math.max(0, Math.min(1, (target - walked) / seg))));
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
/** A curve moved sideways along the sphere by `d` radians (to the left of its direction for a positive `d`). */
|
|
80
|
+
export function offset(pts, d) {
|
|
81
|
+
return pts.map((p, i) => {
|
|
82
|
+
const t = sub(pts[Math.min(pts.length - 1, i + 1)], pts[Math.max(0, i - 1)]);
|
|
83
|
+
return norm(add(p, mul(norm(cross(p, t)), d)));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/** Short stitches along a seam, running with it: a dash and a gap of `step / 2` each. `side` moves the row to one side of the seam. */
|
|
87
|
+
export function running(curves, step = 0.07, side = 0) {
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const curve of curves) {
|
|
90
|
+
const pts = resample(side === 0 ? curve : offset(curve, side), step / 2);
|
|
91
|
+
for (let i = 0; i + 1 < pts.length; i += 2)
|
|
92
|
+
out.push([pts[i], pts[i + 1]]);
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
/** Stitches across a seam: a short mark every `step` radians, `half` to each side of the seam, leaning along it by `lean` (a hand stitch's cross). */
|
|
97
|
+
export function across(curves, step = 0.09, half = 0.022, lean = 0) {
|
|
98
|
+
const out = [];
|
|
99
|
+
for (const curve of curves) {
|
|
100
|
+
const pts = resample(curve, step);
|
|
101
|
+
for (let i = 1; i + 1 < pts.length; i++) {
|
|
102
|
+
const t = norm(sub(pts[i + 1], pts[i - 1]));
|
|
103
|
+
const side = norm(cross(pts[i], t));
|
|
104
|
+
out.push([norm(add(add(pts[i], mul(side, half)), mul(t, -lean))), norm(add(sub(pts[i], mul(side, half)), mul(t, lean)))]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
/** A small deterministic number in [0, 1) from two integers, for scuffs and pixels that must be the same every time. */
|
|
110
|
+
export function noise(a, b = 0) {
|
|
111
|
+
let h = Math.imul(a + 374761393, 668265263) ^ Math.imul(b + 1274126177, 2246822519);
|
|
112
|
+
h = Math.imul(h ^ (h >>> 13), 1274126177);
|
|
113
|
+
return ((h ^ (h >>> 16)) >>> 0) / 4294967296;
|
|
114
|
+
}
|
|
115
|
+
/** The scuffs of worn leather: soft dark patches and a few pale ones, spread over the ball. */
|
|
116
|
+
export function scuffs(s, dark, pale, o) {
|
|
117
|
+
const seed = o?.seed ?? 1;
|
|
118
|
+
const k = o?.size ?? 1;
|
|
119
|
+
const mark = (pts, base, fill, opacity) => {
|
|
120
|
+
let out = "";
|
|
121
|
+
pts.forEach((p, i) => {
|
|
122
|
+
const r = (base + 0.07 * noise(i, seed)) * k;
|
|
123
|
+
out += s.dots([p], r, fill, { opacity: opacity * (0.6 + 0.8 * noise(i, seed + 9)), light: false });
|
|
124
|
+
});
|
|
125
|
+
return out;
|
|
126
|
+
};
|
|
127
|
+
return mark(spread(o?.dark ?? 16, seed), 0.075, dark, 0.13) + mark(spread(o?.pale ?? 9, seed + 3), 0.05, pale, 0.2);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* A lace across a slit: the slit along `along` from the middle `c` (`length` radians long), a row of eyelets down each side and a lace that crosses from side to side, tied off with two ends. Returns the
|
|
131
|
+
* slit, the eyelet centres, the crossing strokes and the ends, for the caller to draw.
|
|
132
|
+
*/
|
|
133
|
+
export function lacing(c, along, length, pairs, gap = 0.03) {
|
|
134
|
+
const f = frameAt(c, along);
|
|
135
|
+
const at = (x, y) => f(x, y);
|
|
136
|
+
const slit = Array.from({ length: 7 }, (_, j) => at(-length / 2 + (length * j) / 6, 0));
|
|
137
|
+
const xs = Array.from({ length: pairs }, (_, i) => -length / 2 + (length * (i + 0.5)) / pairs);
|
|
138
|
+
const eyelets = xs.flatMap((x) => [at(x, gap), at(x, -gap)]);
|
|
139
|
+
const crossings = [];
|
|
140
|
+
for (let i = 0; i + 1 < pairs; i++) {
|
|
141
|
+
crossings.push([at(xs[i], gap), at(xs[i + 1], -gap)], [at(xs[i], -gap), at(xs[i + 1], gap)]);
|
|
142
|
+
}
|
|
143
|
+
const last = xs[pairs - 1];
|
|
144
|
+
const ends = [[at(last, gap), at(last + gap * 1.4, gap * 2.2)], [at(last, -gap), at(last + gap * 1.8, -gap * 1.8)]];
|
|
145
|
+
return { slit, eyelets, crossings, ends };
|
|
146
|
+
}
|
|
@@ -33,6 +33,12 @@ export interface Surface {
|
|
|
33
33
|
opacity?: number;
|
|
34
34
|
n?: number;
|
|
35
35
|
}): string;
|
|
36
|
+
/** Several closed polygons of one fill, drawn as one path (a smaller document than a path for each). */
|
|
37
|
+
panels(rings: ReadonlyArray<readonly V3[]>, fill: string, o?: {
|
|
38
|
+
ink?: boolean;
|
|
39
|
+
opacity?: number;
|
|
40
|
+
n?: number;
|
|
41
|
+
}): string;
|
|
36
42
|
/** 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. */
|
|
37
43
|
stroke(pts: readonly V3[], colour: string, width: number, o?: StrokeOptions): string;
|
|
38
44
|
/** Several curves drawn as one path. */
|
|
@@ -83,7 +89,7 @@ export interface BallSpec {
|
|
|
83
89
|
ground?: string;
|
|
84
90
|
mat: Material;
|
|
85
91
|
/** Lays the panels and seams. `base` is the main colour, or the tint; `accent` is the second colour or undefined. */
|
|
86
|
-
draw: (s: Surface, base: string, accent: string | undefined) => string;
|
|
92
|
+
draw: (s: Surface, base: string, accent: string | undefined, colors?: readonly [string, string, string]) => string;
|
|
87
93
|
/** 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. */
|
|
88
94
|
centre?: readonly [number, number];
|
|
89
95
|
/** 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. */
|
|
@@ -183,6 +183,14 @@ function surfaceOf(g, line, minWidth) {
|
|
|
183
183
|
return "";
|
|
184
184
|
return `<path d="${d}" fill="${line ? INK : fill}"${o?.opacity === undefined ? "" : ` opacity="${o.opacity}"`}/>`;
|
|
185
185
|
},
|
|
186
|
+
panels(rings, fill, o) {
|
|
187
|
+
if (line && o?.ink !== true)
|
|
188
|
+
return "";
|
|
189
|
+
const d = rings.map((ring) => ringPath(g, sampleRing(ring, o?.n ?? 4))).join("");
|
|
190
|
+
if (d === "")
|
|
191
|
+
return "";
|
|
192
|
+
return `<path d="${d}" fill="${line ? INK : fill}"${o?.opacity === undefined ? "" : ` opacity="${o.opacity}"`}/>`;
|
|
193
|
+
},
|
|
186
194
|
stroke: (pts, colour, width, o) => s.strokes([pts], colour, width, o),
|
|
187
195
|
strokes(curves, colour, width, o) {
|
|
188
196
|
if (line && (o?.deco === true || o?.ink === false))
|
|
@@ -291,9 +299,9 @@ export function drawBall(title, key, spec, opts, turn, turning) {
|
|
|
291
299
|
const minWidth = 80 / opts.size;
|
|
292
300
|
const base = opts.tint ?? spec.base;
|
|
293
301
|
const surface = surfaceOf(g, line, minWidth);
|
|
294
|
-
const layers = spec.draw(surface, base, opts.accent);
|
|
302
|
+
const layers = spec.draw(surface, base, opts.accent, opts.colors);
|
|
295
303
|
const sil = silhouette(g);
|
|
296
|
-
const id = `k${hashOf(`${key}|${opts.style}|${opts.tint ?? ""}|${opts.accent ?? ""}|${sil.tag}${sil.d}`)}`;
|
|
304
|
+
const id = `k${hashOf(`${key}|${opts.style}|${opts.tint ?? ""}|${opts.accent ?? ""}${opts.colors === undefined ? "" : `|${opts.colors.join("")}`}|${sil.tag}${sil.d}`)}`;
|
|
297
305
|
// A long outline (the hull of a pointed ball) is written once, in the definitions, and used wherever the silhouette is.
|
|
298
306
|
const long = sil.d.length > 400;
|
|
299
307
|
const outline = long ? `<path id="${id}s" d="${sil.d}"/>` : "";
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/** The designs a face starts from. */
|
|
2
2
|
export type PuckDesign = "canada" | "canada-red" | "canada-white" | "usa" | "usa-navy" | "player" | "souvenir";
|
|
3
|
-
/**
|
|
4
|
-
export type
|
|
5
|
-
|
|
3
|
+
/** How a flag is framed: `round` in a circle, `square`, or `rect` in its own proportions (the puck asks for `rect`). */
|
|
4
|
+
export type FlagShape = "round" | "square" | "rect";
|
|
5
|
+
/** What a drawing asks a `flags` renderer for: the shape, a width and height in pixels, and the look (`inherit` for a page whose ink is its own). */
|
|
6
|
+
export interface FlagRequest {
|
|
7
|
+
shape: FlagShape;
|
|
6
8
|
size: number;
|
|
7
|
-
theme: "
|
|
8
|
-
}
|
|
9
|
+
theme: "inherit" | "light" | "dark";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A function from a country code and a request to an SVG string, or `null` or `undefined` for a code it has no flag for: the same renderer taisen-brackets and kyogijo take, so one adapter over hata serves them
|
|
13
|
+
* all. It must be synchronous (a drawing is a pure function of its options): hata's `flag()` is asynchronous, so load the flags first and pass `(code) => loaded[code]`. Pass it as the `flags` option of the call.
|
|
14
|
+
*/
|
|
15
|
+
export type FlagRenderer = (code: string, request: FlagRequest) => string | null | undefined;
|
|
9
16
|
/** The colours of a face, by name. The array form is the same list in this order: field, ring, ink, accent, signature. */
|
|
10
17
|
export interface FaceColors {
|
|
11
18
|
/** The disc's own colour. */
|
|
@@ -28,10 +35,8 @@ export interface PuckFace {
|
|
|
28
35
|
design?: PuckDesign;
|
|
29
36
|
/** Any SVG: a logo, a crest, a drawing. Made safe first (see `safeImage`): shapes, gradients and clip paths only. It is fitted into the middle of the face and replaces the design's own picture. */
|
|
30
37
|
image?: string;
|
|
31
|
-
/** A country code (`"ca"` and `"us"` are drawn by this package; another needs `flags`). It replaces the design's own picture. */
|
|
38
|
+
/** A country code (`"ca"` and `"us"` are drawn by this package; another needs the `flags` option of the call). It replaces the design's own picture. */
|
|
32
39
|
flag?: string;
|
|
33
|
-
/** A renderer for the flag codes this package does not draw: `(code, { shape, size, theme }) => svg`, the shape of the function hata exports. */
|
|
34
|
-
flags?: FlagRenderer;
|
|
35
40
|
/** The id of a dougu emblem (`"trophy:cup"`, `"sport:nhl"`), drawn small in the middle. Resolved by `renderEmblem`, `emblemFrame` and `ballFrame`. */
|
|
36
41
|
emblem?: string;
|
|
37
42
|
/** The words under the picture (`"CANADA"`), one line. */
|
|
@@ -48,7 +53,7 @@ export interface PuckFace {
|
|
|
48
53
|
colors?: FaceColors | readonly (string | undefined)[];
|
|
49
54
|
}
|
|
50
55
|
/** The face of a design, in the face's units (a disc of radius 1, up is up): its definitions and its drawing. `prefix` makes every id unique to this puck. */
|
|
51
|
-
export declare function faceMarkup(face: PuckFace | undefined, design: PuckDesign, own: PuckFace | undefined, prefix: string): {
|
|
56
|
+
export declare function faceMarkup(face: PuckFace | undefined, design: PuckDesign, own: PuckFace | undefined, prefix: string, flags?: FlagRenderer, theme?: FlagRequest["theme"]): {
|
|
52
57
|
defs: string;
|
|
53
58
|
body: string;
|
|
54
59
|
};
|