@luceosports/play-rendering 2.0.0 → 2.0.2

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.
@@ -1,40 +1,51 @@
1
- import { PlayConstructData, PlayData } from "../../play-rendering";
2
-
3
- export type PlayModelOptions = {
4
- width?: number;
5
- lineColor?: string;
6
- linesDisplay?: boolean;
7
- linesSelectedIds?: string[];
8
- shapeSelectedId?: string | null;
9
- noteSelectedId?: string | null;
10
- playersHiddenPositions?: string[];
11
- background?: string;
12
- watermark?: string | null;
13
- mirror?: boolean;
14
- speed?: number;
15
- position?: string | null;
16
- huddleMode?: boolean;
17
- magnetMode?: boolean;
18
- playerTokenScale?: number;
19
- flipPlayerLabels?: boolean;
20
- linesDisplayOnMoveOnly?: boolean;
21
- linesHiddenIds?: string[];
22
- legacyPrintStyle?: boolean;
23
- showHalfCourtCircle?: boolean;
24
- }
25
-
26
- export class PlayModel {
27
- name: string;
28
- playData: PlayData;
29
- options: PlayModelOptions;
30
- constructor(data: PlayConstructData, options?: Partial<PlayModelOptions>);
31
- static init({ teamLogoPath }?: {
32
- teamLogoPath?: string;
33
- }): Promise<void>;
34
- get totalPhasesCount(): number;
35
- get scale(): number;
36
- get width(): number;
37
- get height(): number;
38
- setOptions(options: Partial<PlayModelOptions>): void;
39
- setPlayData(playData: PlayData): void;
1
+ import { PlayConstructData, PlayData, } from "../../play-rendering";
2
+ import { Position } from '../../../src/types';
3
+
4
+ export type PlayersMapItem = {
5
+ position: Position;
6
+ textOverride: string | null;
7
+ teamPlayerId: string | null;
8
+ playerHatKey?: string | null;
9
+ };
10
+
11
+ export type PlayModelOptions = {
12
+ width: number;
13
+ lineColor: string;
14
+ linesDisplay: boolean;
15
+ linesSelectedIds: string[];
16
+ shapeSelectedId: string | null;
17
+ noteSelectedId: string | null;
18
+ playersHiddenPositions?: string[];
19
+ background: string;
20
+ watermark: string | null;
21
+ mirror: boolean;
22
+ speed: number;
23
+ position: string | null;
24
+ huddleMode: boolean;
25
+ magnetMode: boolean;
26
+ playerTokenScale: number;
27
+ flipPlayerLabels: boolean;
28
+ linesDisplayOnMoveOnly: boolean;
29
+ linesHiddenIds: string[];
30
+ legacyPrintStyle: boolean;
31
+ showHalfCourtCircle: boolean;
32
+ playersMap: PlayersMapItem[];
33
+ labelsOverrideType: 'Headshot' | null;
34
+ }
35
+
36
+ export class PlayModel {
37
+ name: string;
38
+ playData: PlayData;
39
+ options: PlayModelOptions;
40
+ constructor(data: PlayConstructData, options?: Partial<PlayModelOptions>);
41
+ static init({ teamLogoPath }?: {
42
+ teamLogoPath?: string;
43
+ }): Promise<void>;
44
+ static loadPlayerHeadshots(data: { id: string; headshotUrl?: string | null }[]): Promise<void>
45
+ get totalPhasesCount(): number;
46
+ get scale(): number;
47
+ get width(): number;
48
+ get height(): number;
49
+ setOptions(options: Partial<PlayModelOptions>): void;
50
+ setPlayData(playData: PlayData): void;
40
51
  }
@@ -3,6 +3,8 @@ import { Color, CourtPoint, Line, Player, PlayerAnimation, Position } from "../.
3
3
  export class PlayerModel {
4
4
  constructor(data: Player);
5
5
  get id(): string;
6
+ get textOverride(): string;
7
+ set textOverride(data: string);
6
8
  get textLabel(): string;
7
9
  get isDefender(): boolean;
8
10
  get possession(): boolean;
@@ -10,7 +12,6 @@ export class PlayerModel {
10
12
  get position(): Position;
11
13
  get location(): CourtPoint;
12
14
  get color(): Color;
13
- get playerHatKey(): string;
14
15
  get animations(): PlayerAnimation[];
15
16
  get animationsByEndTime(): number[];
16
17
  get lastAnimEndTime(): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luceosports/play-rendering",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "main": "dist/play-rendering.js",
5
5
  "types": "dist/play-rendering.d.ts",
6
6
  "scripts": {
@@ -21,6 +21,7 @@
21
21
  },
22
22
  "devDependencies": {
23
23
  "@babel/core": "^7.18.6",
24
+ "@babel/plugin-proposal-class-properties": "^7.18.6",
24
25
  "@babel/plugin-transform-runtime": "^7.9.0",
25
26
  "@babel/preset-env": "^7.9.0",
26
27
  "@babel/preset-typescript": "^7.24.6",
@@ -31,8 +31,9 @@ export default class PlayerLayer extends BaseLayer {
31
31
  this.setPlayerLabel(player);
32
32
 
33
33
  if (this.staticData.playerHats && this.staticData.playerHats.length) {
34
+ const playerMapItem = this.options.playersMap.find(item => item.position === player.position);
34
35
  const playerHat = this.staticData.playerHats.find(hat => {
35
- return player.playerHatKey ? hat.key === player.playerHatKey : false;
36
+ return playerMapItem?.playerHatKey ? hat.key === playerMapItem.playerHatKey : false;
36
37
  });
37
38
  if (playerHat) {
38
39
  if (playerHat.reverse === true) {
@@ -100,6 +101,24 @@ export default class PlayerLayer extends BaseLayer {
100
101
 
101
102
  setPlayerLabel(player: PlayerModel) {
102
103
  const { x, y } = player.location;
104
+
105
+ if (this.staticData.playerHeadshots && this.options.labelsOverrideType === 'Headshot') {
106
+ const playerMapItem = this.options.playersMap.find(item => item.position === player.position);
107
+ const headshotImage = this.staticData.playerHeadshots.find(item => item.id === playerMapItem?.teamPlayerId);
108
+ if (headshotImage) {
109
+ this.ctx.save();
110
+ this.ctx.translate(x, y);
111
+ if (this.options.flipPlayerLabels === true) {
112
+ this.ctx.rotate(Math.PI);
113
+ }
114
+ this.ctx.arc(0, 0, this.radius, 0, Math.PI * 2, true);
115
+ this.ctx.clip();
116
+ this.ctx.drawImage(headshotImage.image, 0 - this.radius, 0 - this.radius, this.radius * 2, this.radius * 2);
117
+ this.ctx.restore();
118
+ return;
119
+ }
120
+ }
121
+
103
122
  const { alpha } = player.color;
104
123
 
105
124
  const fontSizeLength = 3 - player.textLabel.length * 0.5;
@@ -22,7 +22,9 @@ export function useDefaults(options?: Partial<PlayModelOptions>): PlayModelOptio
22
22
  legacyPrintStyle: false,
23
23
  playerTokenScale: 1,
24
24
  // TODO: refactor NBA court type constants below
25
- showHalfCourtCircle: true
25
+ showHalfCourtCircle: true,
26
+ playersMap: [],
27
+ labelsOverrideType: null
26
28
  };
27
29
  return {
28
30
  ...defaults,
@@ -13,7 +13,7 @@ import {
13
13
  SPORT_TYPE_VOLLEYBALL
14
14
  } from '../constants';
15
15
 
16
- import { PlayConstructData, PlayData, SportType } from '../types';
16
+ import { PlayConstructData, PlayData, Position, SportType } from '../types';
17
17
 
18
18
  import hardwoodImageData from '../assets/wood_bg.png';
19
19
  import grassImageData from '../assets/grass_bg.png';
@@ -31,40 +31,56 @@ export type ImageConfigItem = {
31
31
  reverse?: boolean;
32
32
  };
33
33
 
34
+ export type PlayerHeadshotItem = {
35
+ id: string;
36
+ image: HTMLImageElement;
37
+ };
38
+
34
39
  export type PlayStaticData = {
35
40
  backgroundOptions: typeof PlayModel.backgroundOptions;
36
41
  watermark: typeof PlayModel.watermark;
37
42
  playerHats: readonly ImageConfigItem[];
38
43
  shapes: readonly ImageConfigItem[];
44
+ playerHeadshots: typeof PlayModel.playerHeadshots;
45
+ };
46
+
47
+ export type PlayersMapItem = {
48
+ position: Position;
49
+ textOverride: string | null;
50
+ teamPlayerId: string | null;
51
+ playerHatKey?: string | null;
39
52
  };
40
53
 
41
54
  export type PlayModelOptions = {
42
- width?: number;
43
- lineColor?: string;
44
- linesDisplay?: boolean;
45
- linesSelectedIds?: string[];
46
- shapeSelectedId?: string | null;
47
- noteSelectedId?: string | null;
55
+ width: number;
56
+ lineColor: string;
57
+ linesDisplay: boolean;
58
+ linesSelectedIds: string[];
59
+ shapeSelectedId: string | null;
60
+ noteSelectedId: string | null;
48
61
  playersHiddenPositions?: string[];
49
- background?: string;
50
- watermark?: string | null;
51
- mirror?: boolean;
52
- speed?: number;
53
- position?: string | null;
54
- huddleMode?: boolean;
55
- magnetMode?: boolean;
56
- playerTokenScale?: number;
57
- flipPlayerLabels?: boolean;
58
- linesDisplayOnMoveOnly?: boolean;
59
- linesHiddenIds?: string[];
60
- legacyPrintStyle?: boolean;
61
- showHalfCourtCircle?: boolean;
62
+ background: string;
63
+ watermark: string | null;
64
+ mirror: boolean;
65
+ speed: number;
66
+ position: string | null;
67
+ huddleMode: boolean;
68
+ magnetMode: boolean;
69
+ playerTokenScale: number;
70
+ flipPlayerLabels: boolean;
71
+ linesDisplayOnMoveOnly: boolean;
72
+ linesHiddenIds: string[];
73
+ legacyPrintStyle: boolean;
74
+ showHalfCourtCircle: boolean;
75
+ playersMap: PlayersMapItem[];
76
+ labelsOverrideType: 'Headshot' | null;
62
77
  };
63
78
 
64
79
  export default class PlayModel {
65
80
  public name: string;
66
81
  public playData: PlayData;
67
82
  public options: PlayModelOptions;
83
+ public static playerHeadshots: PlayerHeadshotItem[] = [];
68
84
  public static playerHats: readonly ImageConfigItem[];
69
85
  public static shapes: readonly ImageConfigItem[];
70
86
  public static watermark: { LuceoSports: HTMLImageElement; TeamLogo: HTMLImageElement };
@@ -103,6 +119,22 @@ export default class PlayModel {
103
119
  };
104
120
  }
105
121
 
122
+ static async loadPlayerHeadshots(data: { id: string; headshotUrl?: string | null }[]) {
123
+ await Promise.all(
124
+ data.map(async ({ id, headshotUrl }) => {
125
+ try {
126
+ if (headshotUrl) {
127
+ const image = await loadImage(headshotUrl);
128
+ PlayModel.playerHeadshots.push({ id, image });
129
+ }
130
+ } catch (e) {
131
+ console.error(e);
132
+ }
133
+ return true;
134
+ })
135
+ );
136
+ }
137
+
106
138
  get totalPhasesCount() {
107
139
  const distinctPhases = _.uniq(_.map(this.playData.lines, 'phase'));
108
140
  return distinctPhases.length || 1;
@@ -125,7 +157,8 @@ export default class PlayModel {
125
157
  backgroundOptions: PlayModel.backgroundOptions,
126
158
  watermark: PlayModel.watermark,
127
159
  playerHats: PlayModel.playerHats,
128
- shapes: PlayModel.shapes
160
+ shapes: PlayModel.shapes,
161
+ playerHeadshots: PlayModel.playerHeadshots
129
162
  };
130
163
  }
131
164
 
@@ -7,6 +7,14 @@ export default class PlayerModel extends Model<PlayerData, PlayerData> {
7
7
  return this._getAttr('id');
8
8
  }
9
9
 
10
+ get textOverride() {
11
+ return this._getAttr('textOverride');
12
+ }
13
+
14
+ set textOverride(data) {
15
+ this._setAttr('textOverride', data);
16
+ }
17
+
10
18
  get textLabel() {
11
19
  return this._getAttr('textOverride') || this.position;
12
20
  }
@@ -39,10 +47,6 @@ export default class PlayerModel extends Model<PlayerData, PlayerData> {
39
47
  return this._getAttr('color');
40
48
  }
41
49
 
42
- get playerHatKey() {
43
- return this._getAttr('playerHatKey');
44
- }
45
-
46
50
  get animations() {
47
51
  return this._getAttr('animations');
48
52
  }
@@ -1,6 +1,6 @@
1
1
  import ShapeModel from '../ShapeModel';
2
2
 
3
- export default class XMarkShape extends ShapeModel {
3
+ export default class XmarkShape extends ShapeModel {
4
4
  get outerCircleRadius() {
5
5
  return 10;
6
6
  }
@@ -11,7 +11,7 @@ import LineShape from './LineShape';
11
11
  import SquareShape from './SquareShape';
12
12
  import StraightShape from './StraightShape';
13
13
  import TriangleShape from './TriangleShape';
14
- import XMarkShape from './XMarkShape';
14
+ import XmarkShape from './XmarkShape';
15
15
 
16
16
  export {
17
17
  CutLineShape,
@@ -26,5 +26,5 @@ export {
26
26
  SquareShape,
27
27
  StraightShape,
28
28
  TriangleShape,
29
- XMarkShape
29
+ XmarkShape
30
30
  };
@@ -126,7 +126,6 @@ export interface Player {
126
126
  position: Position;
127
127
  location: CourtPoint;
128
128
  textOverride?: string;
129
- playerHatKey?: string;
130
129
  animations: PlayerAnimation[];
131
130
  }
132
131
 
package/tsconfig.json CHANGED
@@ -13,7 +13,8 @@
13
13
  "dom"
14
14
  ],
15
15
  "target": "es2015",
16
- "moduleResolution": "node"
16
+ "moduleResolution": "node",
17
+ "strictNullChecks": false // TODO make everything strict
17
18
  },
18
19
  "exclude": [
19
20
  "node_modules"