@luceosports/play-rendering 2.6.3 → 2.8.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.
@@ -9,9 +9,7 @@ export declare const getEffectiveTimeline: (anim: HasKeyTimes, timeline: Timelin
9
9
  export declare const keyTimesToChunks: (keyTimes: number[]) => [number, number][];
10
10
  export declare const deriveLineChunksWithFallback: (params: {
11
11
  timeline: TimelineKey;
12
- playerAnimation: HasKeyTimes;
13
12
  lineAnimation: HasKeyTimes;
14
13
  lineType: LineType;
15
14
  lineParts: LinePart[];
16
- legacySupport?: boolean;
17
15
  }) => [number, number][];
@@ -1,4 +1,4 @@
1
- import { Color, CourtPoint, Line, Player, PlayerAnimation, Position, AnimationMode } from "../../play-rendering";
1
+ import { Color, CourtPoint, Line, Player, Position, AnimationMode } from "../../play-rendering";
2
2
 
3
3
  export class PlayerModel {
4
4
  constructor(data: Player);
@@ -9,13 +9,10 @@ export class PlayerModel {
9
9
  get isDefender(): boolean;
10
10
  get possession(): boolean;
11
11
  set possession(data: boolean);
12
+ get startBallsCount();
13
+ set startBallsCount(data: number);
12
14
  get position(): Position;
13
15
  get location(): CourtPoint;
14
16
  get color(): Color;
15
- get animations(): PlayerAnimation[];
16
- animationsByEndTime(mode: AnimationMode): number[];
17
- lastAnimEndTime(mode: AnimationMode): number;
18
- get lastAnimation(): PlayerAnimation;
19
- get lastAnimationLastLinePartControlPoint(): CourtPoint;
20
17
  setPossession(prevPassLines: Line[]): void;
21
18
  }
@@ -59,27 +59,24 @@ export interface Court {
59
59
  type: CourtType;
60
60
  courtRect: CourtRect;
61
61
  }
62
- export type PlayerAnimationType = 'POSITION';
63
- export interface Animation {
64
- id: string;
65
- keyTimes: number[];
66
- keyTimesSeq?: number[];
67
- }
68
- export interface PlayerAnimation extends Animation {
69
- type: PlayerAnimationType;
70
- lineParts: LinePart[];
71
- }
72
62
  export interface Player {
73
63
  id: string;
74
64
  possession: boolean;
65
+ startBallsCount?: number;
75
66
  color: Color;
76
67
  position: Position;
77
68
  location: CourtPoint;
78
69
  textOverride?: string;
79
- animations: PlayerAnimation[];
70
+ }
71
+ export interface Animation {
72
+ id: string;
73
+ keyTimes: number[];
80
74
  }
81
75
  export type LineAnimationType = 'LINESTROKE';
82
- export interface LineAnimation extends Animation {
76
+ export interface LineAnimation {
77
+ id: string;
78
+ keyTimes: number[];
79
+ keyTimesSeq?: number[];
83
80
  type: LineAnimationType;
84
81
  strokeStartValues: [number, number, number];
85
82
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luceosports/play-rendering",
3
- "version": "2.6.3",
3
+ "version": "2.8.0",
4
4
  "main": "dist/play-rendering.js",
5
5
  "types": "dist/play-rendering.d.ts",
6
6
  "scripts": {
@@ -66,13 +66,11 @@ const splitChunkByPartDurations = (params: {
66
66
 
67
67
  export const deriveLineChunksWithFallback = (params: {
68
68
  timeline: TimelineKey;
69
- playerAnimation: HasKeyTimes;
70
69
  lineAnimation: HasKeyTimes;
71
70
  lineType: LineType;
72
71
  lineParts: LinePart[];
73
- legacySupport?: boolean;
74
72
  }): [number, number][] => {
75
- const { timeline, playerAnimation, lineAnimation, lineType, lineParts, legacySupport = true } = params;
73
+ const { timeline, lineAnimation, lineType, lineParts } = params;
76
74
  const linePartsCount = lineParts.length ?? 0;
77
75
 
78
76
  const chunks = keyTimesToChunks(getEffectiveTimeline(lineAnimation, timeline));
@@ -81,32 +79,15 @@ export const deriveLineChunksWithFallback = (params: {
81
79
  // If there are no chunks (bad/partial data), just return as-is.
82
80
  if (!chunks.length) return chunks;
83
81
 
84
- // Prefer geometry-based splitting over player animation fallback.
82
+ // Geometry-based splitting fallback.
85
83
  // Typical legacy case: multi-part line stored as a single [start,end] chunk.
86
84
  if (chunks.length === 1 && lineParts?.length) {
87
85
  const [[start, end]] = chunks;
88
86
  const split = splitChunkByPartDurations({ start, end, lineParts, lineType });
89
- console.log('geometry based', { chunks, split, linePartsCount });
90
87
  if (split.length === linePartsCount) return split;
91
88
  }
92
89
 
93
- console.warn('PLAYER FALLBACK ATTEMPT!!!', { chunks, linePartsCount });
94
-
95
- // If legacy support is disabled, do not attempt reconstruction from player timeline.
96
- if (!legacySupport) return chunks;
97
-
98
- // Backward compatibility:
99
- // legacy multi-part lines stored only [start,end] in the line animation timeline,
100
- // so reconstruct per-part timings from the player animation timeline slice.
101
- const [[start, end]] = chunks;
102
- const playerTimeline = getEffectiveTimeline(playerAnimation, timeline);
103
-
104
- const startIndex = playerTimeline.indexOf(start);
105
- const endIndex = playerTimeline.indexOf(end);
106
- if (startIndex < 0 || endIndex < 0) return chunks;
107
-
108
- const sliced = playerTimeline.slice(startIndex, endIndex + 1);
109
- return keyTimesToChunks(sliced);
90
+ return chunks;
110
91
  };
111
92
 
112
93
  export const getLineAnimationChunks = (params: {
@@ -121,22 +102,12 @@ export const getLineAnimationChunks = (params: {
121
102
  const lineAnimation = line.animations?.[0] as unknown as HasKeyTimes | undefined;
122
103
  if (!lineAnimation) return [];
123
104
 
124
- const linePlayer = players.find(p => p.position === line.playerPositionOrigin);
125
- const playerAnimation = (linePlayer?.animations?.[0] as unknown as HasKeyTimes | undefined) ?? undefined;
126
-
127
- // Fast path: no player timeline available, just chunk the line timeline as-is.
128
- if (!playerAnimation) {
129
- return keyTimesToChunks(getEffectiveTimeline(lineAnimation, timeline));
130
- }
131
-
132
105
  // Full path: handle chunking + legacy reconstruction.
133
106
  return deriveLineChunksWithFallback({
134
107
  timeline,
135
- playerAnimation,
136
108
  lineAnimation,
137
109
  lineType: line.type,
138
- lineParts: line.lineParts,
139
- legacySupport: false
110
+ lineParts: line.lineParts
140
111
  });
141
112
  };
142
113
 
@@ -1,6 +1,5 @@
1
- import _ from 'lodash';
2
1
  import Model from './Base/InternalFrameModel';
3
- import { AnimationMode, Line, Player as PlayerData, PlayerAnimation } from '../types';
2
+ import { Line, Player as PlayerData } from '../types';
4
3
 
5
4
  export default class PlayerModel extends Model<PlayerData, PlayerData> {
6
5
  get id() {
@@ -31,6 +30,19 @@ export default class PlayerModel extends Model<PlayerData, PlayerData> {
31
30
  this._setAttr('possession', data);
32
31
  }
33
32
 
33
+ get startBallsCount() {
34
+ const raw = this._getAttr('startBallsCount');
35
+ if (typeof raw === 'number') return raw;
36
+
37
+ // Backward-compatible fallback:
38
+ // legacy model encoded "starts with 1 ball" as possession=true.
39
+ return this.possession ? 1 : 0;
40
+ }
41
+
42
+ set startBallsCount(data) {
43
+ this._setAttr('startBallsCount', data);
44
+ }
45
+
34
46
  get position() {
35
47
  return this._getAttr('position');
36
48
  }
@@ -47,35 +59,13 @@ export default class PlayerModel extends Model<PlayerData, PlayerData> {
47
59
  return this._getAttr('color');
48
60
  }
49
61
 
50
- get animations() {
51
- return this._getAttr('animations');
52
- }
53
-
54
- private getAnimationKeyTimes(animation: PlayerAnimation, mode: AnimationMode) {
55
- return mode === AnimationMode.SEQUENTIAL ? animation.keyTimesSeq || animation.keyTimes : animation.keyTimes;
56
- }
57
-
58
- animationsByEndTime(mode: AnimationMode) {
59
- return this._getAttr('animations').map(a => _.last(this.getAnimationKeyTimes(a, mode)));
60
- }
61
-
62
- lastAnimEndTime(mode: AnimationMode) {
63
- return _.max(this.animationsByEndTime(mode));
64
- }
65
-
66
- get lastAnimation() {
67
- return [...this.animations].pop();
68
- }
62
+ setPossession(prevPassLines: Line[]) {
63
+ // Support multiple balls: start from startBallsCount, then apply transfers.
64
+ let ballsCount = this.startBallsCount;
69
65
 
70
- get lastAnimationLastLinePartControlPoint() {
71
- return [...[...this.lastAnimation!.lineParts].pop()!.controlPoints].pop();
72
- }
66
+ ballsCount += prevPassLines.filter(l => l.playerPositionTerminus === this.position).length;
67
+ ballsCount -= prevPassLines.filter(l => l.playerPositionOrigin === this.position).length;
73
68
 
74
- setPossession(prevPassLines: Line[]) {
75
- if (!prevPassLines.length) return;
76
- let possessionCount = this.possession ? 1 : 0;
77
- possessionCount += prevPassLines.filter(l => l.playerPositionTerminus === this.position).length;
78
- possessionCount -= prevPassLines.filter(l => l.playerPositionOrigin === this.position).length;
79
- this.possession = possessionCount > 0;
69
+ this.possession = ballsCount > 0;
80
70
  }
81
71
  }
@@ -133,32 +133,31 @@ export interface Court {
133
133
  courtRect: CourtRect;
134
134
  }
135
135
 
136
- export type PlayerAnimationType = 'POSITION';
137
-
138
- export interface Animation {
139
- id: string;
140
- keyTimes: number[];
141
- keyTimesSeq?: number[];
142
- }
143
-
144
- export interface PlayerAnimation extends Animation {
145
- type: PlayerAnimationType;
146
- lineParts: LinePart[];
147
- }
148
-
149
136
  export interface Player {
150
137
  id: string;
151
138
  possession: boolean;
139
+ /**
140
+ * How many balls the player starts with before applying PASS/HANDOFF/SHOT transfers.
141
+ * If omitted, legacy data falls back to `possession ? 1 : 0`.
142
+ */
143
+ startBallsCount?: number;
152
144
  color: Color;
153
145
  position: Position;
154
146
  location: CourtPoint;
155
147
  textOverride?: string;
156
- animations: PlayerAnimation[];
148
+ }
149
+
150
+ export interface Animation {
151
+ id: string;
152
+ keyTimes: number[];
157
153
  }
158
154
 
159
155
  export type LineAnimationType = 'LINESTROKE';
160
156
 
161
- export interface LineAnimation extends Animation {
157
+ export interface LineAnimation {
158
+ id: string;
159
+ keyTimes: number[];
160
+ keyTimesSeq?: number[];
162
161
  type: LineAnimationType;
163
162
  strokeStartValues: [number, number, number];
164
163
  }