@johnmorrisdotca/dougu-sports 0.26.0 → 0.28.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 +45 -0
- package/README.md +3 -3
- package/dist/emblems/emblem.descriptions.js +16 -4
- package/dist/emblems/emblem.ids.d.ts +2 -2
- package/dist/emblems/emblem.ids.js +12 -0
- package/dist/emblems/emblem.lathe.d.ts +124 -4
- package/dist/emblems/emblem.lathe.js +28 -6
- package/dist/emblems/emblem.named.js +125 -5
- package/dist/emblems/emblem.registry.js +24 -0
- package/dist/emblems/lathe.ballrods.d.ts +2 -0
- package/dist/emblems/lathe.ballrods.js +155 -0
- package/dist/emblems/lathe.conference.d.ts +2 -0
- package/dist/emblems/lathe.conference.js +219 -0
- package/dist/emblems/lathe.extras.d.ts +3 -1
- package/dist/emblems/lathe.extras.js +77 -2
- package/dist/emblems/lathe.framed.d.ts +30 -0
- package/dist/emblems/lathe.framed.js +98 -0
- package/dist/emblems/lathe.sculpt.d.ts +6 -0
- package/dist/emblems/lathe.sculpt.js +320 -0
- package/dist/emblems/nba.parts.d.ts +27 -0
- package/dist/emblems/nba.parts.js +34 -0
- package/dist/emblems/trophy/bill-russell-trophy.d.ts +1 -1
- package/dist/emblems/trophy/bill-russell-trophy.js +17 -13
- package/dist/emblems/trophy/bob-cousy-trophy.d.ts +2 -0
- package/dist/emblems/trophy/bob-cousy-trophy.js +18 -0
- package/dist/emblems/trophy/calder-cup.js +4 -4
- package/dist/emblems/trophy/foundation-player-award.d.ts +2 -0
- package/dist/emblems/trophy/foundation-player-award.js +31 -0
- package/dist/emblems/trophy/george-halas-trophy.d.ts +2 -0
- package/dist/emblems/trophy/george-halas-trophy.js +6 -0
- package/dist/emblems/trophy/jackie-robinson-award.d.ts +2 -0
- package/dist/emblems/trophy/jackie-robinson-award.js +60 -0
- package/dist/emblems/trophy/kelly-cup.js +7 -6
- package/dist/emblems/trophy/lamar-hunt-trophy.d.ts +1 -1
- package/dist/emblems/trophy/lamar-hunt-trophy.js +5 -16
- package/dist/emblems/trophy/larry-bird-trophy.d.ts +2 -0
- package/dist/emblems/trophy/larry-bird-trophy.js +23 -0
- package/dist/emblems/trophy/larry-obrien-trophy-1977.d.ts +2 -0
- package/dist/emblems/trophy/larry-obrien-trophy-1977.js +23 -0
- package/dist/emblems/trophy/larry-obrien-trophy.d.ts +1 -1
- package/dist/emblems/trophy/larry-obrien-trophy.js +18 -11
- package/dist/emblems/trophy/magic-johnson-trophy.d.ts +2 -0
- package/dist/emblems/trophy/magic-johnson-trophy.js +20 -0
- package/dist/emblems/trophy/messier-leadership-award.d.ts +2 -0
- package/dist/emblems/trophy/messier-leadership-award.js +79 -0
- package/dist/emblems/trophy/nba-eastern-conference-2001.d.ts +2 -0
- package/dist/emblems/trophy/nba-eastern-conference-2001.js +18 -0
- package/dist/emblems/trophy/nba-western-conference-2001.d.ts +2 -0
- package/dist/emblems/trophy/nba-western-conference-2001.js +19 -0
- package/dist/emblems/trophy/oscar-robertson-trophy.d.ts +2 -0
- package/dist/emblems/trophy/oscar-robertson-trophy.js +19 -0
- package/dist/emblems/trophy/willie-oree-award.d.ts +2 -0
- package/dist/emblems/trophy/willie-oree-award.js +55 -0
- package/dist/emblems/trophy/wnba-championship-trophy.d.ts +1 -1
- package/dist/emblems/trophy/wnba-championship-trophy.js +6 -17
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -216,9 +216,9 @@ export const NAMED_TROPHIES = [
|
|
|
216
216
|
sport: "basketball",
|
|
217
217
|
competition: "National Basketball Association",
|
|
218
218
|
kind: "champion",
|
|
219
|
-
honours: "The winner of the NBA Finals.",
|
|
219
|
+
honours: "The winner of the NBA Finals, in the design redrawn for 2022 (the earlier design is trophy:larry-obrien-trophy-1977).",
|
|
220
220
|
first: "1977-78",
|
|
221
|
-
reference: "File:P20211108AS-1503 (51846495221).jpg (Wikimedia Commons, public domain)",
|
|
221
|
+
reference: "File:P20211108AS-1503 (51846495221).jpg (Wikimedia Commons, public domain) for the earlier design, and photographs of the 2022 design supplied by John Morris, looked at and measured only; not copied.",
|
|
222
222
|
},
|
|
223
223
|
{
|
|
224
224
|
id: "trophy:commissioners-trophy",
|
|
@@ -278,7 +278,7 @@ export const NAMED_TROPHIES = [
|
|
|
278
278
|
kind: "champion",
|
|
279
279
|
honours: "The winner of the WNBA Finals.",
|
|
280
280
|
first: "1997",
|
|
281
|
-
reference: "File:Jaylyn Sharrrod
|
|
281
|
+
reference: "File:Jaylyn Sharrrod.jpg and File:Sloot Trophy.png (Wikimedia Commons, CC BY-SA 4.0), with a description sent by John Morris (2026-10-30); looked at and measured only, not copied",
|
|
282
282
|
},
|
|
283
283
|
{
|
|
284
284
|
id: "trophy:lamar-hunt-trophy",
|
|
@@ -288,7 +288,17 @@ export const NAMED_TROPHIES = [
|
|
|
288
288
|
kind: "team",
|
|
289
289
|
honours: "The champion of the American Football Conference.",
|
|
290
290
|
first: "1970",
|
|
291
|
-
reference: "
|
|
291
|
+
reference: "Photographs supplied by John Morris (2026-10-30) of the George Halas Trophy, which is the same design; looked at and measured only, not copied",
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: "trophy:george-halas-trophy",
|
|
295
|
+
name: "George Halas Trophy",
|
|
296
|
+
sport: "football",
|
|
297
|
+
competition: "National Football League",
|
|
298
|
+
kind: "team",
|
|
299
|
+
honours: "The champion of the National Football Conference.",
|
|
300
|
+
first: "1970",
|
|
301
|
+
reference: "Photographs supplied by John Morris (2026-10-30), three-quarter and front; looked at and measured only, not copied",
|
|
292
302
|
},
|
|
293
303
|
{
|
|
294
304
|
id: "trophy:silver-slugger-award",
|
|
@@ -478,7 +488,77 @@ export const NAMED_TROPHIES = [
|
|
|
478
488
|
kind: "player",
|
|
479
489
|
honours: "The most valuable player of the championship series.",
|
|
480
490
|
first: "1969",
|
|
481
|
-
reference: "A press photograph of the 2022 design standing beside the championship trophy (cbssports.com, 12 May 2022), looked at and measured only; not copied. No freely licensed photograph on Wikimedia Commons (searched 2026-10-
|
|
491
|
+
reference: "A press photograph of the 2022 design standing beside the championship trophy (cbssports.com, 12 May 2022) and the 2022 photographs supplied by John Morris, looked at and measured only; not copied. No freely licensed photograph on Wikimedia Commons (searched 2026-10-30).",
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
id: "trophy:nba-eastern-conference-2001",
|
|
495
|
+
name: "Eastern Conference Trophy",
|
|
496
|
+
sport: "basketball",
|
|
497
|
+
competition: "National Basketball Association",
|
|
498
|
+
kind: "champion",
|
|
499
|
+
honours: "The Eastern Conference champions, in the design given from 2001 to 2021 (the 2022 redesign is trophy:bob-cousy-trophy).",
|
|
500
|
+
first: "2001",
|
|
501
|
+
reference: "The published description of the trophy before 2022 (a frosted grained ball, a black and polished silver base) set against the 2022 photographs supplied by John Morris, looked at and measured only; not copied.",
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
id: "trophy:nba-western-conference-2001",
|
|
505
|
+
name: "Western Conference Trophy",
|
|
506
|
+
sport: "basketball",
|
|
507
|
+
competition: "National Basketball Association",
|
|
508
|
+
kind: "champion",
|
|
509
|
+
honours: "The Western Conference champions, in the design given from 2001 to 2021 (the 2022 redesign is trophy:oscar-robertson-trophy).",
|
|
510
|
+
first: "2001",
|
|
511
|
+
reference: "The published description of the trophy before 2022 (a grained ball, a black and polished silver base) set against the 2022 photographs supplied by John Morris, looked at and measured only; not copied.",
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
id: "trophy:larry-obrien-trophy-1977",
|
|
515
|
+
name: "Larry O'Brien Trophy 1977",
|
|
516
|
+
sport: "basketball",
|
|
517
|
+
competition: "National Basketball Association",
|
|
518
|
+
kind: "champion",
|
|
519
|
+
honours: "The winner of the NBA Finals, in the design given from 1977-78 to 2020-21 (the 2022 redesign is trophy:larry-obrien-trophy).",
|
|
520
|
+
first: "1977",
|
|
521
|
+
reference: "The published description of the trophy before 2022 (a smooth gold ball, a net over the lower half of the vase, a square plinth) set against the 2022 photographs supplied by John Morris, looked at and measured only; not copied.",
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
id: "trophy:larry-bird-trophy",
|
|
525
|
+
name: "Larry Bird Trophy",
|
|
526
|
+
sport: "basketball",
|
|
527
|
+
competition: "National Basketball Association",
|
|
528
|
+
kind: "player",
|
|
529
|
+
honours: "The most valuable player of the Eastern Conference finals.",
|
|
530
|
+
first: "2022",
|
|
531
|
+
reference: "Photographs of the 2022 postseason trophies supplied by John Morris (two views of the conference trophies and a lineup of all of them), looked at and measured only; not copied. No freely licensed photograph on Wikimedia Commons (searched 2026-10-30).",
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
id: "trophy:magic-johnson-trophy",
|
|
535
|
+
name: "Magic Johnson Trophy",
|
|
536
|
+
sport: "basketball",
|
|
537
|
+
competition: "National Basketball Association",
|
|
538
|
+
kind: "player",
|
|
539
|
+
honours: "The most valuable player of the Western Conference finals.",
|
|
540
|
+
first: "2022",
|
|
541
|
+
reference: "Photographs of the 2022 postseason trophies supplied by John Morris (two views of the conference trophies and a lineup of all of them), looked at and measured only; not copied. No freely licensed photograph on Wikimedia Commons (searched 2026-10-30).",
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
id: "trophy:bob-cousy-trophy",
|
|
545
|
+
name: "Bob Cousy Trophy",
|
|
546
|
+
sport: "basketball",
|
|
547
|
+
competition: "National Basketball Association",
|
|
548
|
+
kind: "champion",
|
|
549
|
+
honours: "The Eastern Conference champions; named for Bob Cousy in 2022 (given since 2001).",
|
|
550
|
+
first: "2022",
|
|
551
|
+
reference: "Photographs of the 2022 postseason trophies supplied by John Morris (two views of the conference trophies and a lineup of all of them), looked at and measured only; not copied. No freely licensed photograph on Wikimedia Commons (searched 2026-10-30).",
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
id: "trophy:oscar-robertson-trophy",
|
|
555
|
+
name: "Oscar Robertson Trophy",
|
|
556
|
+
sport: "basketball",
|
|
557
|
+
competition: "National Basketball Association",
|
|
558
|
+
kind: "champion",
|
|
559
|
+
honours: "The Western Conference champions; named for Oscar Robertson in 2022 (given since 2001).",
|
|
560
|
+
first: "2022",
|
|
561
|
+
reference: "Photographs of the 2022 postseason trophies supplied by John Morris (two views of the conference trophies and a lineup of all of them), looked at and measured only; not copied. No freely licensed photograph on Wikimedia Commons (searched 2026-10-30).",
|
|
482
562
|
},
|
|
483
563
|
{
|
|
484
564
|
id: "trophy:hank-aaron-award",
|
|
@@ -540,6 +620,46 @@ export const NAMED_TROPHIES = [
|
|
|
540
620
|
first: "1996",
|
|
541
621
|
reference: "File:KC Ballet & Sporting KC (11232058043).jpg (Wikimedia Commons, CC BY 2.0), looked at and measured only; not copied.",
|
|
542
622
|
},
|
|
623
|
+
{
|
|
624
|
+
id: "trophy:foundation-player-award",
|
|
625
|
+
name: "NHL Foundation Player Award",
|
|
626
|
+
sport: "hockey",
|
|
627
|
+
competition: "National Hockey League",
|
|
628
|
+
kind: "player",
|
|
629
|
+
honours: "The player who best applies hockey's core values of commitment, perseverance and teamwork to enrich the lives of people in his community.",
|
|
630
|
+
first: "2003-04",
|
|
631
|
+
reference: "Photographs of the 2022 and 2020 award on a dark ground, supplied by John Morris (Dropbox /Projects/Resources/dougu-sports/nhl-foundation-player/), looked at and measured only; never copied or committed; not ours to licence. No photograph on Wikimedia Commons.",
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
id: "trophy:messier-leadership-award",
|
|
635
|
+
name: "Mark Messier NHL Leadership Award",
|
|
636
|
+
sport: "hockey",
|
|
637
|
+
competition: "National Hockey League",
|
|
638
|
+
kind: "player",
|
|
639
|
+
honours: "The player who best exemplifies leadership on and off the ice, including his charitable and community work.",
|
|
640
|
+
first: "2006-07",
|
|
641
|
+
reference: "Five photographs of the award (front, three-quarter and dark-ground views, and a render), supplied by John Morris (Dropbox /Projects/Resources/dougu-sports/nhl-mark-messier-leadership/), looked at and measured only; never copied or committed; not ours to licence. No photograph on Wikimedia Commons.",
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
id: "trophy:willie-oree-award",
|
|
645
|
+
name: "Willie O'Ree Community Hero Award",
|
|
646
|
+
sport: "hockey",
|
|
647
|
+
competition: "National Hockey League",
|
|
648
|
+
kind: "other",
|
|
649
|
+
honours: "A person who, through the game of hockey, has positively impacted his or her community, culture or society.",
|
|
650
|
+
first: "2017-18",
|
|
651
|
+
reference: "Six photographs of the award (two front views on white and on a dark ground, a large clear front view, a three-quarter view on a table and a display case, which carries a sponsor's branding and was looked at only), supplied by John Morris (Dropbox /Projects/Resources/dougu-sports/nhl-willie-oree/), looked at and measured only; never copied or committed; not ours to licence. No photograph on Wikimedia Commons.",
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
id: "trophy:jackie-robinson-award",
|
|
655
|
+
name: "Jackie Robinson Rookie of the Year Award",
|
|
656
|
+
sport: "baseball",
|
|
657
|
+
competition: "Major League Baseball",
|
|
658
|
+
kind: "player",
|
|
659
|
+
honours: "The best rookie of each league (the Rookie of the Year Award, named for Jackie Robinson, with this plaque since at least 1989).",
|
|
660
|
+
first: "1947",
|
|
661
|
+
reference: "Photographs of the plaques of 1989 and 1992, a close photograph of the bronze relief and one of the nameplate and emblem, supplied by John (looked at and measured only; not copied; a Getty photograph and a 1940s plaque looked at only, not used). No freely licensed photograph on Wikimedia Commons.",
|
|
662
|
+
},
|
|
543
663
|
];
|
|
544
664
|
/** The facts of a named trophy by its emblem id, or `undefined` for a generic trophy. */
|
|
545
665
|
export function namedTrophy(id) {
|
|
@@ -57,9 +57,17 @@ import { TROPHY_MLB_MVP_AWARD } from "./trophy/mlb-mvp-award.js";
|
|
|
57
57
|
import { TROPHY_RELIEF_MAN_AWARD } from "./trophy/relief-man-award.js";
|
|
58
58
|
import { TROPHY_HANK_AARON_AWARD } from "./trophy/hank-aaron-award.js";
|
|
59
59
|
import { TROPHY_BILL_RUSSELL_TROPHY } from "./trophy/bill-russell-trophy.js";
|
|
60
|
+
import { TROPHY_NBA_WESTERN_CONFERENCE_2001 } from "./trophy/nba-western-conference-2001.js";
|
|
61
|
+
import { TROPHY_NBA_EASTERN_CONFERENCE_2001 } from "./trophy/nba-eastern-conference-2001.js";
|
|
62
|
+
import { TROPHY_OSCAR_ROBERTSON_TROPHY } from "./trophy/oscar-robertson-trophy.js";
|
|
63
|
+
import { TROPHY_BOB_COUSY_TROPHY } from "./trophy/bob-cousy-trophy.js";
|
|
64
|
+
import { TROPHY_MAGIC_JOHNSON_TROPHY } from "./trophy/magic-johnson-trophy.js";
|
|
65
|
+
import { TROPHY_LARRY_BIRD_TROPHY } from "./trophy/larry-bird-trophy.js";
|
|
66
|
+
import { TROPHY_LARRY_OBRIEN_TROPHY_1977 } from "./trophy/larry-obrien-trophy-1977.js";
|
|
60
67
|
import { TROPHY_WALTER_CUP } from "./trophy/walter-cup.js";
|
|
61
68
|
import { TROPHY_WNBA_CHAMPIONSHIP_TROPHY } from "./trophy/wnba-championship-trophy.js";
|
|
62
69
|
import { TROPHY_LAMAR_HUNT_TROPHY } from "./trophy/lamar-hunt-trophy.js";
|
|
70
|
+
import { TROPHY_GEORGE_HALAS_TROPHY } from "./trophy/george-halas-trophy.js";
|
|
63
71
|
import { TROPHY_SILVER_SLUGGER_AWARD } from "./trophy/silver-slugger-award.js";
|
|
64
72
|
import { TROPHY_WORLD_SERIES_MVP_TROPHY } from "./trophy/world-series-mvp-trophy.js";
|
|
65
73
|
import { TROPHY_AVCO_WORLD_TROPHY } from "./trophy/avco-world-trophy.js";
|
|
@@ -69,10 +77,14 @@ import { TROPHY_FIFA_WORLD_CUP } from "./trophy/fifa-world-cup.js";
|
|
|
69
77
|
import { TROPHY_WOODLAWN_VASE } from "./trophy/woodlawn-vase.js";
|
|
70
78
|
import { TROPHY_DFB_POKAL } from "./trophy/dfb-pokal.js";
|
|
71
79
|
import { TROPHY_CHAMPIONS_LEAGUE_TROPHY } from "./trophy/champions-league-trophy.js";
|
|
80
|
+
import { TROPHY_JACKIE_ROBINSON_AWARD } from "./trophy/jackie-robinson-award.js";
|
|
72
81
|
import { TROPHY_CALDER_CUP } from "./trophy/calder-cup.js";
|
|
73
82
|
import { TROPHY_KELLY_CUP } from "./trophy/kelly-cup.js";
|
|
74
83
|
import { TROPHY_CFP_TROPHY } from "./trophy/cfp-trophy.js";
|
|
75
84
|
import { TROPHY_MLS_CUP } from "./trophy/mls-cup.js";
|
|
85
|
+
import { TROPHY_FOUNDATION_PLAYER_AWARD } from "./trophy/foundation-player-award.js";
|
|
86
|
+
import { TROPHY_WILLIE_OREE_AWARD } from "./trophy/willie-oree-award.js";
|
|
87
|
+
import { TROPHY_MESSIER_LEADERSHIP_AWARD } from "./trophy/messier-leadership-award.js";
|
|
76
88
|
import { TROPHY_WEBB_ELLIS_CUP } from "./trophy/webb-ellis-cup.js";
|
|
77
89
|
import { TROPHY_FA_CUP } from "./trophy/fa-cup.js";
|
|
78
90
|
import { TROPHY_CRICKET_WORLD_CUP } from "./trophy/cricket-world-cup.js";
|
|
@@ -182,9 +194,13 @@ const OWN = {
|
|
|
182
194
|
"trophy:webb-ellis-cup": TROPHY_WEBB_ELLIS_CUP,
|
|
183
195
|
"trophy:champions-league-trophy": TROPHY_CHAMPIONS_LEAGUE_TROPHY,
|
|
184
196
|
"trophy:calder-cup": TROPHY_CALDER_CUP,
|
|
197
|
+
"trophy:jackie-robinson-award": TROPHY_JACKIE_ROBINSON_AWARD,
|
|
185
198
|
"trophy:kelly-cup": TROPHY_KELLY_CUP,
|
|
186
199
|
"trophy:cfp-trophy": TROPHY_CFP_TROPHY,
|
|
187
200
|
"trophy:mls-cup": TROPHY_MLS_CUP,
|
|
201
|
+
"trophy:foundation-player-award": TROPHY_FOUNDATION_PLAYER_AWARD,
|
|
202
|
+
"trophy:willie-oree-award": TROPHY_WILLIE_OREE_AWARD,
|
|
203
|
+
"trophy:messier-leadership-award": TROPHY_MESSIER_LEADERSHIP_AWARD,
|
|
188
204
|
"trophy:dfb-pokal": TROPHY_DFB_POKAL,
|
|
189
205
|
"trophy:woodlawn-vase": TROPHY_WOODLAWN_VASE,
|
|
190
206
|
"trophy:fifa-world-cup": TROPHY_FIFA_WORLD_CUP,
|
|
@@ -194,6 +210,7 @@ const OWN = {
|
|
|
194
210
|
"trophy:world-series-mvp-trophy": TROPHY_WORLD_SERIES_MVP_TROPHY,
|
|
195
211
|
"trophy:silver-slugger-award": TROPHY_SILVER_SLUGGER_AWARD,
|
|
196
212
|
"trophy:lamar-hunt-trophy": TROPHY_LAMAR_HUNT_TROPHY,
|
|
213
|
+
"trophy:george-halas-trophy": TROPHY_GEORGE_HALAS_TROPHY,
|
|
197
214
|
"trophy:wnba-championship-trophy": TROPHY_WNBA_CHAMPIONSHIP_TROPHY,
|
|
198
215
|
"trophy:walter-cup": TROPHY_WALTER_CUP,
|
|
199
216
|
"trophy:mlb-mvp-award": TROPHY_MLB_MVP_AWARD,
|
|
@@ -201,6 +218,13 @@ const OWN = {
|
|
|
201
218
|
"trophy:relief-man-award": TROPHY_RELIEF_MAN_AWARD,
|
|
202
219
|
"trophy:hank-aaron-award": TROPHY_HANK_AARON_AWARD,
|
|
203
220
|
"trophy:bill-russell-trophy": TROPHY_BILL_RUSSELL_TROPHY,
|
|
221
|
+
"trophy:nba-western-conference-2001": TROPHY_NBA_WESTERN_CONFERENCE_2001,
|
|
222
|
+
"trophy:nba-eastern-conference-2001": TROPHY_NBA_EASTERN_CONFERENCE_2001,
|
|
223
|
+
"trophy:oscar-robertson-trophy": TROPHY_OSCAR_ROBERTSON_TROPHY,
|
|
224
|
+
"trophy:bob-cousy-trophy": TROPHY_BOB_COUSY_TROPHY,
|
|
225
|
+
"trophy:magic-johnson-trophy": TROPHY_MAGIC_JOHNSON_TROPHY,
|
|
226
|
+
"trophy:larry-bird-trophy": TROPHY_LARRY_BIRD_TROPHY,
|
|
227
|
+
"trophy:larry-obrien-trophy-1977": TROPHY_LARRY_OBRIEN_TROPHY_1977,
|
|
204
228
|
"trophy:grey-cup": TROPHY_GREY_CUP,
|
|
205
229
|
"trophy:commissioners-trophy": TROPHY_COMMISSIONERS_TROPHY,
|
|
206
230
|
"trophy:larry-obrien-trophy": TROPHY_LARRY_OBRIEN_TROPHY,
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// A polished silver ball held up on three thin rods that lean forward from a low, rounded, triangular plinth: the WNBA championship trophy. The pieces are placed in 3D (`framed` in emblem.lathe.ts; the helpers are in
|
|
2
|
+
// lathe.framed.ts): a sphere with its seams cut as grooves, three tubes that rise from small collars and meet under it, and a plinth of facets (a flat wall, a bevel and a top) that is the one thing the engraving is set on.
|
|
3
|
+
// Drawn for this package from photographs of the real trophy (docs/likeness.md); the maker's mark, the league's mark and every letter on it are left out.
|
|
4
|
+
import { mix } from "./emblem.core.js";
|
|
5
|
+
import { sideText } from "./emblem.engrave.js";
|
|
6
|
+
import { chrome, cross, line, normalOf, shadeOf, sub, unit, viewOf } from "./lathe.framed.js";
|
|
7
|
+
const RAD = Math.PI / 180;
|
|
8
|
+
/** The whole tripod leans forward by this much from the upright, toward `D` (the eye, a little to the left), so the ball sits forward of the plinth's centre. */
|
|
9
|
+
const LEAN = 18;
|
|
10
|
+
const D = [-0.26, 0, 0.966];
|
|
11
|
+
const E = [D[2], 0, -D[0]];
|
|
12
|
+
const AXIS = [Math.sin(LEAN * RAD) * D[0], -Math.cos(LEAN * RAD), Math.sin(LEAN * RAD) * D[2]];
|
|
13
|
+
/** The ball (a ball and a half-width across the plinth, two and a bit across the rods) and the rods: from the top of the plinth to the ball's underside is two and a fifth ball widths. */
|
|
14
|
+
const BALL = 7.6;
|
|
15
|
+
const RISE = 2.2 * 2 * BALL;
|
|
16
|
+
const TOP = 57;
|
|
17
|
+
const add = (a, b, k = 1) => [a[0] + k * b[0], a[1] + k * b[1], a[2] + k * b[2]];
|
|
18
|
+
const FOOT = [0, TOP, 0];
|
|
19
|
+
const SEAT = add(FOOT, AXIS, RISE / Math.cos(LEAN * RAD));
|
|
20
|
+
const CENTRE = add(SEAT, AXIS, BALL - 0.6);
|
|
21
|
+
/** The plinth is a rounded triangle in plan: its corners are arcs round the points of a small triangle (a flat side faces the eye at rest). Each rod stands in its own collar near a corner (two at the front, one at the back) and they lean together to a point under the ball. */
|
|
22
|
+
const CORE = 7.6;
|
|
23
|
+
const CORNER = 3.8;
|
|
24
|
+
const WALL = TOP + 4.3;
|
|
25
|
+
const RIM = TOP + 2.2;
|
|
26
|
+
const corner = (i) => [0.8 * CORE * Math.sin((60 + 120 * i) * RAD), TOP, 0.8 * CORE * Math.cos((60 + 120 * i) * RAD)];
|
|
27
|
+
const rods = [0, 1, 2].map((i) => ({ from: corner(i), to: add(SEAT, [corner(i)[0], 0, corner(i)[2]], 0.13) }));
|
|
28
|
+
/** The plinth: a wall, a bevel and a top on the rounded triangle; each corner is an arc of 16 steps round a point of the small triangle. */
|
|
29
|
+
const SIDES = 51;
|
|
30
|
+
const ring = (k, inset, y) => {
|
|
31
|
+
const i = ((k % SIDES) + SIDES) % SIDES;
|
|
32
|
+
const vertex = Math.floor(i / 17);
|
|
33
|
+
const psi = (120 * vertex + (120 * (i % 17)) / 16) * RAD;
|
|
34
|
+
const phi = (60 + 120 * vertex) * RAD;
|
|
35
|
+
const r = CORNER - inset;
|
|
36
|
+
return [CORE * Math.sin(phi) + r * Math.sin(psi), y, CORE * Math.cos(phi) + r * Math.cos(psi)];
|
|
37
|
+
};
|
|
38
|
+
/** The sphere's gradient: bright where it faces the light, dark at its edge, as a polished ball is. */
|
|
39
|
+
function sphereGradient(rgb) {
|
|
40
|
+
const id = `dgsr${rgb.map((n) => n.toString(16).padStart(2, "0")).join("")}`;
|
|
41
|
+
const list = [[0, 0.88], [0.25, 0.5], [0.55, -0.02], [0.85, -0.45], [1, -0.62]];
|
|
42
|
+
const stops = list.map(([at, k]) => `<stop offset="${at}" stop-color="${mix(rgb, k)}"/>`).join("");
|
|
43
|
+
return { id, def: `<radialGradient id="${id}" cx=".38" cy=".3" r=".85">${stops}</radialGradient>`, fill: `url(#${id})` };
|
|
44
|
+
}
|
|
45
|
+
/** A circle on the sphere: its plane's normal and how far the plane is from the centre (a fraction of the radius). */
|
|
46
|
+
function circle(n, k) {
|
|
47
|
+
const ref = Math.abs(n[1]) > 0.9 ? [1, 0, 0] : [0, -1, 0];
|
|
48
|
+
const u = unit(cross(n, ref));
|
|
49
|
+
const w = cross(n, u);
|
|
50
|
+
const r = BALL * Math.sqrt(1 - k * k);
|
|
51
|
+
const c = add(CENTRE, n, k * BALL);
|
|
52
|
+
return Array.from({ length: 31 }, (_, i) => add(add(c, u, r * Math.cos(i * 12 * RAD)), w, r * Math.sin(i * 12 * RAD)));
|
|
53
|
+
}
|
|
54
|
+
/** The seams of a basketball: the equator, one upright circle, and a curved seam each side of it. */
|
|
55
|
+
const SEAMS = [circle([0, -1, 0], 0), circle(E, 0), circle(D, 0.55), circle(D, -0.55)];
|
|
56
|
+
function framed(c) {
|
|
57
|
+
const v = viewOf(c);
|
|
58
|
+
const main = c.tone();
|
|
59
|
+
const at = (p) => v.at(v.rot(p));
|
|
60
|
+
let out = "";
|
|
61
|
+
// The plinth: every facet that faces the eye, grouped by shade (they do not overlap), then the top.
|
|
62
|
+
const wall = [];
|
|
63
|
+
for (let k = 0; k < SIDES; k++) {
|
|
64
|
+
wall.push([ring(k, 0, WALL), ring(k + 1, 0, WALL), ring(k + 1, 0, RIM), ring(k, 0, RIM)]);
|
|
65
|
+
wall.push([ring(k, 0, RIM), ring(k + 1, 0, RIM), ring(k + 1, 1.4, TOP), ring(k, 1.4, TOP)]);
|
|
66
|
+
}
|
|
67
|
+
const groups = new Map();
|
|
68
|
+
for (const q of wall) {
|
|
69
|
+
if (c.line)
|
|
70
|
+
break;
|
|
71
|
+
const mid = q.reduce((m, p) => [m[0] + p[0] / 4, m[1] + p[1] / 4, m[2] + p[2] / 4], [0, 0, 0]);
|
|
72
|
+
let n = unit(normalOf(q));
|
|
73
|
+
const bevel = q[0][1] === RIM;
|
|
74
|
+
const away = [mid[0], bevel ? -1.4 : 0, mid[2]];
|
|
75
|
+
if (n[0] * away[0] + n[1] * away[1] + n[2] * away[2] < 0)
|
|
76
|
+
n = [-n[0], -n[1], -n[2]];
|
|
77
|
+
const turned = v.rot(n);
|
|
78
|
+
if (v.facing(turned) <= 0.02)
|
|
79
|
+
continue;
|
|
80
|
+
const colour = c.flat ? mix(main.rgb, bevel ? 0.2 : -0.1) : shadeOf(main.rgb, Math.min(0.9, chrome(turned, c.s) + (bevel ? 0.25 : 0.05)), 10);
|
|
81
|
+
groups.set(colour, (groups.get(colour) ?? "") + line(q.map(at), true));
|
|
82
|
+
}
|
|
83
|
+
out += `<g stroke="none">${[...groups].map(([colour, d]) => `<path d="${d}" fill="${colour}"/>`).join("")}</g>`;
|
|
84
|
+
const rim = (inset, y) => Array.from({ length: SIDES }, (_, k) => at(ring(k, inset, y)));
|
|
85
|
+
out += `<path d="${line(rim(1.4, TOP), true)}" fill="${c.line ? "none" : c.flat ? mix(main.rgb, 0.45) : mix(main.rgb, 0.7)}"/>`;
|
|
86
|
+
const foot = rim(0, WALL);
|
|
87
|
+
out += `<path d="${line(foot, true)}"${c.line ? "" : ' stroke-opacity=".6"'}/>`;
|
|
88
|
+
if (c.line) {
|
|
89
|
+
const side = rim(0, RIM);
|
|
90
|
+
const lo = foot.reduce((m, p, i) => (p[0] < foot[m][0] ? i : m), 0);
|
|
91
|
+
const hi = foot.reduce((m, p, i) => (p[0] > foot[m][0] ? i : m), 0);
|
|
92
|
+
out += `<path d="${line([foot[lo], side[lo]], false)}${line([foot[hi], side[hi]], false)}"/>`;
|
|
93
|
+
}
|
|
94
|
+
if (c.engrave !== "") {
|
|
95
|
+
const p0 = at(ring(50, 0, (WALL + RIM) / 2));
|
|
96
|
+
const p1 = at(ring(0, 0, (WALL + RIM) / 2));
|
|
97
|
+
out += sideText(c.engrave, 11, 1.9, c.ink, p0, p1, Math.cos((c.yaw * Math.PI) / 180));
|
|
98
|
+
}
|
|
99
|
+
// The rods and the ball, back to front: a rod that meets the ball on the near side is drawn after it.
|
|
100
|
+
const ball = at(CENTRE);
|
|
101
|
+
const ballDepth = v.depth(v.rot(CENTRE));
|
|
102
|
+
const items = [];
|
|
103
|
+
const tube = (a, b, w) => {
|
|
104
|
+
const [x0, y0] = at(a);
|
|
105
|
+
const [x1, y1] = at(b);
|
|
106
|
+
const d = `M${Math.round(x0 * 10) / 10} ${Math.round(y0 * 10) / 10}L${Math.round(x1 * 10) / 10} ${Math.round(y1 * 10) / 10}`;
|
|
107
|
+
if (c.line)
|
|
108
|
+
return w > 2.8 ? "" : `<path d="${d}"/>`;
|
|
109
|
+
return `<path d="${d}" stroke-width="${w + 0.9}"/><path d="${d}" stroke="${c.flat ? main.fill : mix(main.rgb, 0.3)}" stroke-width="${w}"/>${c.flat ? "" : `<path d="${d}" transform="translate(-.45 -.35)" stroke="#fff" stroke-opacity=".7" stroke-width=".5"/>`}`;
|
|
110
|
+
};
|
|
111
|
+
for (const { from, to } of rods) {
|
|
112
|
+
const collar = tube(from, add(from, AXIS, 0.8), 2.8);
|
|
113
|
+
const body = tube(add(from, AXIS, 0.8), to, 1.9);
|
|
114
|
+
items.push({ depth: ballDepth + (v.depth(v.rot(to)) > ballDepth ? 0.5 : -0.5) + v.depth(v.rot(from)) * 1e-4, svg: body + collar });
|
|
115
|
+
}
|
|
116
|
+
const R = BALL;
|
|
117
|
+
const fill = c.paint(sphereGradient(main.rgb), main.rgb);
|
|
118
|
+
let ballSvg = `<circle cx="${Math.round(ball[0] * 10) / 10}" cy="${Math.round(ball[1] * 10) / 10}" r="${R}" fill="${fill}"/>`;
|
|
119
|
+
if (!c.line && !c.flat) {
|
|
120
|
+
const band = (h) => Math.sqrt(R * R - h * h);
|
|
121
|
+
const [h1, h2] = [0.05 * R, 0.34 * R];
|
|
122
|
+
const f = (n) => Math.round(n * 10) / 10;
|
|
123
|
+
ballSvg += `<path d="M${f(ball[0] - band(h1))} ${f(ball[1] + h1)}Q${f(ball[0])} ${f(ball[1] + h1 - 0.16 * R)} ${f(ball[0] + band(h1))} ${f(ball[1] + h1)}A${R} ${R} 0 0 1 ${f(ball[0] + band(h2))} ${f(ball[1] + h2)}Q${f(ball[0])} ${f(ball[1] + h2 + 0.1 * R)} ${f(ball[0] - band(h2))} ${f(ball[1] + h2)}A${R} ${R} 0 0 1 ${f(ball[0] - band(h1))} ${f(ball[1] + h1)}z" fill="${mix(main.rgb, -0.5)}" fill-opacity=".5" stroke="none"/>`;
|
|
124
|
+
ballSvg += `<ellipse cx="${f(ball[0] - 0.38 * R)}" cy="${f(ball[1] - 0.42 * R)}" rx="${f(0.28 * R)}" ry="${f(0.15 * R)}" fill="#fff" fill-opacity=".6" stroke="none" transform="rotate(-32 ${f(ball[0] - 0.38 * R)} ${f(ball[1] - 0.42 * R)})"/>`;
|
|
125
|
+
}
|
|
126
|
+
// The seams, cut as grooves: the halves that face the eye.
|
|
127
|
+
let seams = "";
|
|
128
|
+
for (const ringPts of SEAMS) {
|
|
129
|
+
let pen = false;
|
|
130
|
+
for (const p of ringPts) {
|
|
131
|
+
const visible = v.facing(v.rot(sub(p, CENTRE))) > 0.04;
|
|
132
|
+
if (!visible) {
|
|
133
|
+
pen = false;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const [x, y] = at(p);
|
|
137
|
+
seams += `${pen ? "L" : "M"}${Math.round(x * 10) / 10} ${Math.round(y * 10) / 10}`;
|
|
138
|
+
pen = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
ballSvg += `<path d="${seams}" stroke="${c.line ? "currentColor" : mix(main.rgb, -0.62)}" stroke-width="${c.line ? 1.1 : 0.7}"/>`;
|
|
142
|
+
items.push({ depth: ballDepth, svg: ballSvg });
|
|
143
|
+
items.sort((p, q) => p.depth - q.depth);
|
|
144
|
+
return out + items.map((i) => i.svg).join("");
|
|
145
|
+
}
|
|
146
|
+
export const BALL_ON_RODS = {
|
|
147
|
+
metal: "silver",
|
|
148
|
+
box: "0 0 64 64",
|
|
149
|
+
turnBox: "-7.8 -6.8 79.7 79.7",
|
|
150
|
+
tilt: 12,
|
|
151
|
+
edgeWidth: 0.4,
|
|
152
|
+
lineWidth: 1.1,
|
|
153
|
+
solids: [],
|
|
154
|
+
framed,
|
|
155
|
+
};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// The drawing the George Halas Trophy and the Lamar Hunt Trophy share: an open football of polished silver standing on its tip on a round flared base with a black foot. The football is three parts of one football-shaped
|
|
2
|
+
// surface turned about the upright axis: a front plate (the ribbon) that follows the ball's front, with seven slots cut through where the laces are, a narrower neck below them and a satin point on its face; and two
|
|
3
|
+
// curved bands, the left and right sides of the ball's outline, that meet the plate at the tip and the top. Drawn for this package from John's photographs of the trophy (docs/likeness.md): the heights, the half
|
|
4
|
+
// widths of the ring and of the plate, the slots and the base were read off the front photograph, the depth and the way the bands come round off the three-quarter ones. The marks on the plate and on the base
|
|
5
|
+
// (the shield, the conference letters, the engraving) are left out.
|
|
6
|
+
import { hexOf, mix, readColour } from "./emblem.core.js";
|
|
7
|
+
import { chrome, cross, curve, line, lin, normalOf, shadeOf, sub, unit, viewOf } from "./lathe.framed.js";
|
|
8
|
+
const RAD = Math.PI / 180;
|
|
9
|
+
/** The eye at rest looks down by this much (sin 7 degrees); the measurements were read off the photograph's page, so a point near the eye is set up by what that tilt will push down. */
|
|
10
|
+
const S0 = Math.sin(7 * RAD);
|
|
11
|
+
/** The football leans back from the upright by this much, so that from the front it looks shorter than it is long, and from the side it leans away; read off the three-quarter photographs. */
|
|
12
|
+
const LEAN = 45;
|
|
13
|
+
const CT = Math.cos(LEAN * RAD);
|
|
14
|
+
const ST = Math.sin(LEAN * RAD);
|
|
15
|
+
/** Where the ball's lower pole is, in the lathe's heights: a little under the top of the base, which hides it. A point of the ball is placed by its distance `t` along the leaning axis, how far `x` it is across and how far `w` it stands forward. */
|
|
16
|
+
const POLE = 49.3;
|
|
17
|
+
const lean = (x, t, w) => [x, POLE - t * CT - w * ST, w * CT - t * ST];
|
|
18
|
+
/** How far the ring seen from the front climbs on the page for each unit along the axis. */
|
|
19
|
+
const CLIMB = CT + S0 * ST;
|
|
20
|
+
/** Half the ball's width at each height of the front photograph, from the lower pole to the upper one (a pointed oval, a football); the heights are what the page shows, and become distances along the axis. */
|
|
21
|
+
const RING_READ = [[2.5, 0], [6.4, 6.4], [8.3, 9.9], [10.3, 12.8], [12.8, 15.3], [16, 17.3], [19.2, 18.5], [22.4, 19], [25.6, 19.2], [28.8, 19], [35.3, 17.2], [38.5, 15.3], [41.7, 12.2], [44.9, 8], [47.3, 3], [49.3, 0]];
|
|
22
|
+
/** The upper pole, as a distance along the axis. */
|
|
23
|
+
const TOP_T = (POLE - 2.5) / CLIMB;
|
|
24
|
+
const rho = curve(RING_READ.map(([y, r]) => [(POLE - y) / CLIMB, r]).reverse());
|
|
25
|
+
/** Half the plate's width at a height: it is as wide as the laces, then steps in to a neck that narrows to the tip. */
|
|
26
|
+
const WIDTH = lin([[1, 3.3], [2, 4.5], [3.2, 5], [26.3, 5], [27.6, 3], [28, 3], [39, 2.2], [47, 1.7]]);
|
|
27
|
+
const ROWS = [1, 1.6, 2.2, 3.2, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 26.3, 27.6, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 47];
|
|
28
|
+
/** Where the laces are: the heights of the slots, and the half length of each. */
|
|
29
|
+
const SLOTS = [4.2, 6, 8.2, 10.4, 12.9, 15.7, 19];
|
|
30
|
+
const SLOT_HEIGHT = 0.7;
|
|
31
|
+
/** The plate is as thick as this, and the ring is a sheet drawn with its edge lines. */
|
|
32
|
+
const THICK = 1.1;
|
|
33
|
+
const TIP = 47;
|
|
34
|
+
/** The distance along the axis of the plate's centre line at a height of the page: the first place up the ball where its face reaches that height. */
|
|
35
|
+
const reach = new Map();
|
|
36
|
+
function along(y) {
|
|
37
|
+
const known = reach.get(y);
|
|
38
|
+
if (known !== undefined)
|
|
39
|
+
return known;
|
|
40
|
+
let found = TOP_T + 4;
|
|
41
|
+
for (let t = 0; t < TOP_T + 4; t += 0.1) {
|
|
42
|
+
const w = Math.sqrt(Math.max(0.09, rho(t) ** 2));
|
|
43
|
+
const p = lean(0, t, w);
|
|
44
|
+
if (p[1] + S0 * p[2] <= y) {
|
|
45
|
+
found = t;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
reach.set(y, found);
|
|
50
|
+
return found;
|
|
51
|
+
}
|
|
52
|
+
/** A point of the plate's face at a height of the page: x across, and the plate lying on the ball. */
|
|
53
|
+
const front = (x, y) => {
|
|
54
|
+
const t = along(y);
|
|
55
|
+
return lean(x, t, Math.sqrt(Math.max(0.09, rho(t) ** 2 - x * x)));
|
|
56
|
+
};
|
|
57
|
+
const normal = (x, y) => {
|
|
58
|
+
const n = unit(cross(sub(front(x + 0.05, y), front(x - 0.05, y)), sub(front(x, y + 0.05), front(x, y - 0.05))));
|
|
59
|
+
return n[2] < 0 ? [-n[0], -n[1], -n[2]] : n;
|
|
60
|
+
};
|
|
61
|
+
const back = (x, y) => {
|
|
62
|
+
const p = front(x, y);
|
|
63
|
+
const n = normal(x, y);
|
|
64
|
+
return [p[0] - THICK * n[0], p[1] - THICK * n[1], p[2] - THICK * n[2]];
|
|
65
|
+
};
|
|
66
|
+
const half = (i) => 3.2 + (0.4 * i) / 6;
|
|
67
|
+
/** The plate's outline as points of its face (`face` is front or back), with the slots as holes, and its two side walls. */
|
|
68
|
+
const outline = (face) => {
|
|
69
|
+
const pts = [];
|
|
70
|
+
for (const k of [-1, -0.5, 0, 0.5, 1])
|
|
71
|
+
pts.push(face(k * (WIDTH(ROWS[0])), ROWS[0]));
|
|
72
|
+
for (const y of ROWS.slice(1))
|
|
73
|
+
pts.push(face(WIDTH(y), y));
|
|
74
|
+
pts.push(face(0, TIP));
|
|
75
|
+
for (const y of [...ROWS].reverse().slice(1))
|
|
76
|
+
pts.push(face(-WIDTH(y), y));
|
|
77
|
+
return pts;
|
|
78
|
+
};
|
|
79
|
+
const slotOf = (face, i) => {
|
|
80
|
+
const c = SLOTS[i];
|
|
81
|
+
const w = half(i);
|
|
82
|
+
return [face(-w, c - SLOT_HEIGHT / 2), face(w, c - SLOT_HEIGHT / 2), face(w, c + SLOT_HEIGHT / 2), face(-w, c + SLOT_HEIGHT / 2)];
|
|
83
|
+
};
|
|
84
|
+
const wall = (side) => {
|
|
85
|
+
const down = ROWS.map((y) => front(side * WIDTH(y), y));
|
|
86
|
+
const up = [...ROWS].reverse().map((y) => back(side * WIDTH(y), y));
|
|
87
|
+
return [...down, ...up];
|
|
88
|
+
};
|
|
89
|
+
/** The satin point below the laces: a long triangle on the plate's face. */
|
|
90
|
+
const point = () => [[-1.5, 27.8], [0, 27.8], [1.5, 27.8], [0.75, 34.1], [0, 40.4], [-0.75, 34.1]].map(([x, y]) => {
|
|
91
|
+
const p = front(x, y);
|
|
92
|
+
const n = normal(x, y);
|
|
93
|
+
return [p[0] + 0.06 * n[0], p[1] + 0.06 * n[1], p[2] + 0.06 * n[2]];
|
|
94
|
+
});
|
|
95
|
+
/** The band of one side of the ball between two parameters of its length: the points of its two edges. */
|
|
96
|
+
const SEGMENTS = 32;
|
|
97
|
+
const BAND_WIDTH = 7.2;
|
|
98
|
+
function band(side, from, to) {
|
|
99
|
+
const a = [];
|
|
100
|
+
const b = [];
|
|
101
|
+
const low = (POLE - 47) / CT;
|
|
102
|
+
const high = TOP_T - 0.6;
|
|
103
|
+
for (let i = from; i <= to; i++) {
|
|
104
|
+
const u = i / SEGMENTS;
|
|
105
|
+
const t = low + (high - low) * (0.5 - 0.5 * Math.cos(Math.PI * u));
|
|
106
|
+
const r = Math.max(0.4, rho(t));
|
|
107
|
+
const d = Math.min(0.46, Math.max(0.14, BAND_WIDTH / (2 * r)));
|
|
108
|
+
const rb = (r / Math.cos(d)) * 0.99;
|
|
109
|
+
a.push(lean(side * rb * Math.cos(d), t, rb * Math.sin(d)));
|
|
110
|
+
b.push(lean(side * rb * Math.cos(d), t, -rb * Math.sin(d)));
|
|
111
|
+
}
|
|
112
|
+
return { a, b };
|
|
113
|
+
}
|
|
114
|
+
const CHUNKS = [[0, 6], [6, 26], [26, SEGMENTS]];
|
|
115
|
+
const lighter = (rgb, k) => readColour(mix(rgb, k));
|
|
116
|
+
const f1 = (n) => String(Math.round(n * 10) / 10);
|
|
117
|
+
const mean = (pts) => [pts.reduce((s, p) => s + p[0], 0) / pts.length, pts.reduce((s, p) => s + p[1], 0) / pts.length, pts.reduce((s, p) => s + p[2], 0) / pts.length];
|
|
118
|
+
/** The shading of the bright plate: a dark edge, a white band left of the middle, and a soft grey side, as a flat mirror of silver shows a room. `shift` slides the white band (a turn). */
|
|
119
|
+
function brightGradient(rgb, shift) {
|
|
120
|
+
const h = Math.max(0.1, Math.min(0.5, 0.3 + shift));
|
|
121
|
+
const id = `dgsn${hexOf(rgb).slice(1)}${Math.round(shift * 100) + 100}`;
|
|
122
|
+
const list = [[0, -0.45], [0.07, 0.4], [h, 0.88], [h + 0.3, 0.6], [0.9, 0.18], [1, -0.32]];
|
|
123
|
+
const stops = list.map(([at, k]) => `<stop offset="${Math.round(at * 100) / 100}" stop-color="${mix(rgb, k)}"/>`).join("");
|
|
124
|
+
return { id, def: `<linearGradient id="${id}">${stops}</linearGradient>`, fill: `url(#${id})` };
|
|
125
|
+
}
|
|
126
|
+
/** The flat cut on the front of the base: a bright facet wide at the rim and running down to a point, where a plane that leans out more steeply than the cone meets the cone's surface. */
|
|
127
|
+
const BASE_TOP = 47.4;
|
|
128
|
+
const BASE_FOOT = 59.2;
|
|
129
|
+
const coneR = (y) => 14.5 + ((19.6 - 14.5) * (y - BASE_TOP)) / (BASE_FOOT - BASE_TOP);
|
|
130
|
+
function facet() {
|
|
131
|
+
return [[-11.6, BASE_TOP, 8.7], [0, BASE_TOP, 8.4], [11.6, BASE_TOP, 8.7], [0, 58.6, coneR(58.6) + 0.1]];
|
|
132
|
+
}
|
|
133
|
+
/** The parts of the trophy that are not bodies of revolution, turned and drawn from the back to the front. */
|
|
134
|
+
function framed(c) {
|
|
135
|
+
const v = viewOf(c);
|
|
136
|
+
const main = c.tone();
|
|
137
|
+
const place = (p) => p.map((q) => v.at(v.rot(q)));
|
|
138
|
+
const items = [];
|
|
139
|
+
const faceFront = Math.cos((c.yaw * Math.PI) / 180) >= 0;
|
|
140
|
+
const face = faceFront ? front : back;
|
|
141
|
+
if (c.line) {
|
|
142
|
+
const bands = [-1, 1].map((side) => band(side, 0, SEGMENTS));
|
|
143
|
+
let out = "";
|
|
144
|
+
for (const { a, b } of bands)
|
|
145
|
+
out += line(place([...a, ...[...b].reverse()]), true, 2);
|
|
146
|
+
const plate = outline(face);
|
|
147
|
+
out += line(place(plate), true, 2);
|
|
148
|
+
SLOTS.forEach((y, i) => {
|
|
149
|
+
out += line(place([face(-half(i), y), face(half(i), y)]), false);
|
|
150
|
+
});
|
|
151
|
+
if (faceFront)
|
|
152
|
+
out += line(place(point()), true, 2);
|
|
153
|
+
return `<path d="${out}"/>`;
|
|
154
|
+
}
|
|
155
|
+
// The plate: its face with the slots cut through, one shape filled across its width like the round parts.
|
|
156
|
+
const slots = SLOTS.map((_, i) => line(place(slotOf(face, i)), true)).join("");
|
|
157
|
+
const plate = line(place(outline(face)), true);
|
|
158
|
+
const dim = SLOTS.map((_, i) => {
|
|
159
|
+
const q = slotOf(face, i);
|
|
160
|
+
const mid = (a, b) => [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2];
|
|
161
|
+
return line(place([mid(q[0], q[3]), mid(q[1], q[2])]), false);
|
|
162
|
+
}).join("");
|
|
163
|
+
const centre = v.rot(face(0, 25));
|
|
164
|
+
items.push({ depth: v.depth(centre), svg: `<path d="${plate}${slots}" fill="${c.paint(brightGradient(main.rgb, 0.2 * Math.sin((c.yaw * Math.PI) / 180)), lighter(main.rgb, 0.2))}" fill-rule="evenodd"/>` });
|
|
165
|
+
if (v.facing(v.rot([0, 0, 1])) > 0)
|
|
166
|
+
items.push({ depth: -999, svg: `<path d="${line(place(facet()), true)}" fill="${c.paint(brightGradient(main.rgb, 0.2 * Math.sin((c.yaw * Math.PI) / 180)), lighter(main.rgb, 0.25))}" stroke="none"/>` });
|
|
167
|
+
items.push({ depth: v.depth(centre) + 0.005, svg: `<path d="${dim}" stroke="${mix(main.rgb, -0.5)}" stroke-width=".55"/>` });
|
|
168
|
+
if (faceFront)
|
|
169
|
+
items.push({ depth: v.depth(centre) + 0.01, svg: `<path d="${line(place(point()), true)}" fill="${mix(main.rgb, -0.22)}" stroke="none"/>` });
|
|
170
|
+
// The two side walls of the plate: visible when it turns.
|
|
171
|
+
for (const side of [-1, 1]) {
|
|
172
|
+
const w = wall(side);
|
|
173
|
+
const n = v.rot([side, 0, 0.2]);
|
|
174
|
+
if (v.facing(n) <= 0)
|
|
175
|
+
continue;
|
|
176
|
+
items.push({ depth: v.depth(v.rot(mean(w))), svg: `<path d="${line(place(w), true)}" fill="${c.flat ? mix(main.rgb, -0.3) : shadeOf(main.rgb, chrome(unit(n), c.s), 12)}"/>` });
|
|
177
|
+
}
|
|
178
|
+
// The bands, in three runs each so that the ends can pass in front of or behind the plate as the trophy turns.
|
|
179
|
+
for (const side of [-1, 1]) {
|
|
180
|
+
for (const [from, to] of CHUNKS) {
|
|
181
|
+
const { a, b } = band(side, from, to);
|
|
182
|
+
const pa = place(a);
|
|
183
|
+
const pb = place(b);
|
|
184
|
+
let fills = "";
|
|
185
|
+
for (let i = 0; i < a.length - 1; i++) {
|
|
186
|
+
const quad = [a[i], a[i + 1], b[i + 1], b[i]];
|
|
187
|
+
const n = unit(normalOf(quad.map((q) => v.rot(q))));
|
|
188
|
+
const shade = chrome(n, c.s);
|
|
189
|
+
const colour = c.flat ? mix(main.rgb, Math.round(shade * 3) / 6) : shadeOf(main.rgb, shade);
|
|
190
|
+
const grow = (p, q) => [q[0] + 0.1 * (q[0] - p[0]), q[1] + 0.1 * (q[1] - p[1])];
|
|
191
|
+
const pts = [pa[i], grow(pa[i], pa[i + 1]), grow(pb[i], pb[i + 1]), pb[i]];
|
|
192
|
+
fills += `<path d="${line(pts, true)}" fill="${colour}"/>`;
|
|
193
|
+
}
|
|
194
|
+
const edges = line(pa, false, 2) + line(pb, false, 2);
|
|
195
|
+
// A band seen edge on is a thin bright edge; seen face on, its two edges are fine lines round the face.
|
|
196
|
+
const across = pa.reduce((m, p, i) => m + Math.hypot(p[0] - pb[i][0], p[1] - pb[i][1]), 0) / pa.length;
|
|
197
|
+
const edge = Math.max(1, 2.4 - 0.45 * across);
|
|
198
|
+
const tube = `<path d="${edges}" stroke-width="${f1(edge)}"/><path d="${edges}" stroke="${c.flat ? main.fill : mix(main.rgb, 0.5)}" stroke-width="${f1(edge - 0.7)}"/>`;
|
|
199
|
+
items.push({ depth: v.depth(v.rot(mean([...a, ...b]))), svg: `<g stroke="none">${fills}</g>${tube}` });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
items.sort((p, q) => p.depth - q.depth);
|
|
203
|
+
return items.map((i) => i.svg).join("");
|
|
204
|
+
}
|
|
205
|
+
export const NFL_CONFERENCE = {
|
|
206
|
+
metal: "silver",
|
|
207
|
+
box: "-0.8 0 65.5 65.5",
|
|
208
|
+
turnBox: "-14.2 -18.6 92.4 92.4",
|
|
209
|
+
tones: { dark: "#17181c" },
|
|
210
|
+
tilt: 7,
|
|
211
|
+
lineWidth: 1.1,
|
|
212
|
+
edgeWidth: 0.35,
|
|
213
|
+
maxElevation: 34,
|
|
214
|
+
solids: [
|
|
215
|
+
{ p: [[46.4, 13.6, 1], [47.4, 14.5], [59.2, 19.6, 1], [59.2, 19.6, 1, "dark"], [62.4, 18.9, 1]] },
|
|
216
|
+
],
|
|
217
|
+
plate: { y0: 52, y1: 57.5, at: 62 },
|
|
218
|
+
framed,
|
|
219
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import { type DecorCtx, type ExtraDecor } from "./emblem.lathe.ts";
|
|
1
|
+
import { type BallCtx, type DecorCtx, type ExtraDecor } from "./emblem.lathe.ts";
|
|
2
2
|
/** Draws one extra decoration of a solid, as markup ("" when none of it shows). */
|
|
3
3
|
export declare function LATHE_EXTRAS(c: DecorCtx, d: ExtraDecor): string;
|
|
4
|
+
/** Draws the seams of a basketball as grooves (each the front half of a great circle: one elliptical arc), and the specks of a grained finish. */
|
|
5
|
+
export declare function LATHE_BALL(c: BallCtx): string;
|