@luceosports/play-rendering 2.6.1 → 2.6.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.
- package/dist/play-rendering.js +23 -23
- package/dist/play-rendering.js.map +1 -1
- package/dist/types/helpers/animation.d.ts +4 -3
- package/package.json +1 -1
- package/src/constants.ts +65 -63
- package/src/helpers/animation.ts +61 -8
- package/src/helpers/common.ts +5 -1
- package/src/models/FrameModel.ts +570 -536
- package/src/models/LineModel.ts +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnimationMode } from '../types';
|
|
1
|
+
import { AnimationMode, LinePart, LineType } from '../types';
|
|
2
2
|
export type TimelineKey = 'keyTimes' | 'keyTimesSeq';
|
|
3
3
|
export type HasKeyTimes = {
|
|
4
4
|
keyTimes: number[];
|
|
@@ -7,10 +7,11 @@ export type HasKeyTimes = {
|
|
|
7
7
|
export declare const getTimelineKey: (mode: AnimationMode) => TimelineKey;
|
|
8
8
|
export declare const getEffectiveTimeline: (anim: HasKeyTimes, timeline: TimelineKey) => number[];
|
|
9
9
|
export declare const keyTimesToChunks: (keyTimes: number[]) => [number, number][];
|
|
10
|
-
export declare const
|
|
10
|
+
export declare const deriveLineChunksWithFallback: (params: {
|
|
11
11
|
timeline: TimelineKey;
|
|
12
12
|
playerAnimation: HasKeyTimes;
|
|
13
13
|
lineAnimation: HasKeyTimes;
|
|
14
|
-
|
|
14
|
+
lineType: LineType;
|
|
15
|
+
lineParts: LinePart[];
|
|
15
16
|
legacySupport?: boolean;
|
|
16
17
|
}) => [number, number][];
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -1,63 +1,65 @@
|
|
|
1
|
-
export const
|
|
2
|
-
|
|
3
|
-
export const
|
|
4
|
-
export const
|
|
5
|
-
export const
|
|
6
|
-
export const
|
|
7
|
-
export const
|
|
8
|
-
export const
|
|
9
|
-
export const
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
export const
|
|
13
|
-
|
|
14
|
-
export const
|
|
15
|
-
export const
|
|
16
|
-
export const
|
|
17
|
-
export const
|
|
18
|
-
export const
|
|
19
|
-
export const
|
|
20
|
-
|
|
21
|
-
export const
|
|
22
|
-
|
|
23
|
-
export const
|
|
24
|
-
export const
|
|
25
|
-
|
|
26
|
-
export const
|
|
27
|
-
|
|
28
|
-
export const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
export const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
export const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
export const
|
|
38
|
-
|
|
39
|
-
export const
|
|
40
|
-
export const
|
|
41
|
-
export const
|
|
42
|
-
export const
|
|
43
|
-
|
|
44
|
-
export const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export const
|
|
48
|
-
|
|
49
|
-
export const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export const
|
|
53
|
-
|
|
54
|
-
export const
|
|
55
|
-
export const
|
|
56
|
-
export const
|
|
57
|
-
export const
|
|
58
|
-
export const
|
|
59
|
-
export const
|
|
60
|
-
export const
|
|
61
|
-
export const
|
|
62
|
-
export const
|
|
63
|
-
export const
|
|
1
|
+
export const BALL_TRANSFER_LINE_DURATION_DEFAULT = 0.03;
|
|
2
|
+
|
|
3
|
+
export const SPORT_TYPE_BASKETBALL = 'BASKETBALL';
|
|
4
|
+
export const SPORT_TYPE_VOLLEYBALL = 'VOLLEYBALL';
|
|
5
|
+
export const SPORT_TYPE_BEACH_VOLLEYBALL = 'BEACH_VOLLEYBALL';
|
|
6
|
+
export const SPORT_TYPE_FOOTBALL = 'FOOTBALL';
|
|
7
|
+
export const SPORT_TYPE_LACROSSE = 'LACROSSE';
|
|
8
|
+
export const SPORT_TYPE_LACROSSE_BOX = 'LACROSSE_BOX';
|
|
9
|
+
export const SPORT_TYPE_SOCCER = 'SOCCER';
|
|
10
|
+
export const SPORT_TYPE_HOCKEY = 'HOCKEY';
|
|
11
|
+
export const SPORT_TYPE_BASEBALL = 'BASEBALL';
|
|
12
|
+
export const SPORT_TYPE_SOFTBALL = 'SOFTBALL';
|
|
13
|
+
|
|
14
|
+
export const COURT_TYPE_BIG3 = 'BIG3';
|
|
15
|
+
export const COURT_TYPE_FIBA = 'FIBA';
|
|
16
|
+
export const COURT_TYPE_NBA = 'NBA';
|
|
17
|
+
export const COURT_TYPE_NCAAM = 'NCAAM';
|
|
18
|
+
export const COURT_TYPE_NCAAW = 'NCAAW';
|
|
19
|
+
export const COURT_TYPE_US_HIGH_SCHOOL = 'US_HIGH_SCHOOL';
|
|
20
|
+
export const COURT_TYPE_US_JUNIOR_HIGH = 'US_JUNIOR_HIGH';
|
|
21
|
+
export const COURT_TYPE_WNBA = 'WNBA';
|
|
22
|
+
|
|
23
|
+
export const COURT_TYPE_FOOTBALL_COLLEGE = 'FOOTBALL_COLLEGE';
|
|
24
|
+
export const COURT_TYPE_FOOTBALL_NFL = 'FOOTBALL_NFL';
|
|
25
|
+
export const COURT_TYPE_FOOTBALL_HIGH_SCHOOL = 'FOOTBALL_HIGH_SCHOOL';
|
|
26
|
+
export const COURT_TYPE_FOOTBALL_HIGH_SCHOOL_LEGACY = 'FOOTBALL';
|
|
27
|
+
|
|
28
|
+
export const COURT_TYPE_VOLLEYBALL_INDOOR = 'VOLLEYBALL_INDOOR';
|
|
29
|
+
|
|
30
|
+
export const COURT_TYPE_BEACH_VOLLEYBALL_NCAA = 'BEACH_VOLLEYBALL_NCAA';
|
|
31
|
+
export const COURT_TYPE_BEACH_VOLLEYBALL_RECREATIONAL = 'BEACH_VOLLEYBALL_RECREATIONAL';
|
|
32
|
+
|
|
33
|
+
export const COURT_TYPE_LACROSSE_US_M = 'LACROSSE_US_M';
|
|
34
|
+
export const COURT_TYPE_LACROSSE_US_W = 'LACROSSE_US_W';
|
|
35
|
+
|
|
36
|
+
export const COURT_TYPE_LACROSSE_BOX_US = 'LACROSSE_BOX_US';
|
|
37
|
+
export const COURT_TYPE_LACROSSE_BOX_CLA = 'LACROSSE_BOX_CLA';
|
|
38
|
+
|
|
39
|
+
export const COURT_TYPE_SOCCER_FIFA = 'SOCCER_FIFA';
|
|
40
|
+
export const COURT_TYPE_SOCCER_NCAA = 'SOCCER_NCAA';
|
|
41
|
+
export const COURT_TYPE_SOCCER_NFHS = 'SOCCER_NFHS';
|
|
42
|
+
export const COURT_TYPE_SOCCER_U10 = 'SOCCER_U10';
|
|
43
|
+
export const COURT_TYPE_SOCCER_U12 = 'SOCCER_U12';
|
|
44
|
+
export const COURT_TYPE_SOCCER_U19 = 'SOCCER_U19';
|
|
45
|
+
|
|
46
|
+
export const COURT_TYPE_HOCKEY_NHL = 'HOCKEY_NHL';
|
|
47
|
+
export const COURT_TYPE_HOCKEY_INTERNATIONAL = 'HOCKEY_INTERNATIONAL';
|
|
48
|
+
|
|
49
|
+
export const COURT_TYPE_BASEBALL_HIGH_SCHOOL = 'BASEBALL_HIGH_SCHOOL';
|
|
50
|
+
|
|
51
|
+
export const COURT_TYPE_SOFTBALL_FP_COLLEGE = 'SOFTBALL_FP_COLLEGE';
|
|
52
|
+
export const COURT_TYPE_SOFTBALL_FP_HIGH_SCHOOL = 'SOFTBALL_FP_HS';
|
|
53
|
+
|
|
54
|
+
export const SHAPE_TYPE_CIRCLE = 'CIRCLE';
|
|
55
|
+
export const SHAPE_TYPE_SQUARE = 'SQUARE';
|
|
56
|
+
export const SHAPE_TYPE_TRIANGLE = 'TRIANGLE';
|
|
57
|
+
export const SHAPE_TYPE_FOV = 'FOV';
|
|
58
|
+
export const SHAPE_TYPE_XMARK = 'XMARK';
|
|
59
|
+
export const SHAPE_TYPE_STRAIGHT = 'STRAIGHT';
|
|
60
|
+
export const SHAPE_TYPE_CONE = 'CONE';
|
|
61
|
+
export const SHAPE_TYPE_LINE_CUT = 'LINE.CUT';
|
|
62
|
+
export const SHAPE_TYPE_LINE_SCREEN = 'LINE.SCREEN';
|
|
63
|
+
export const SHAPE_TYPE_LINE_DRIBBLE = 'LINE.DRIBBLE';
|
|
64
|
+
export const SHAPE_TYPE_LINE_PASS = 'LINE.PASS';
|
|
65
|
+
export const SHAPE_TYPE_LINE_HANDOFF = 'LINE.HANDOFF';
|
package/src/helpers/animation.ts
CHANGED
|
@@ -2,7 +2,10 @@ import NoteModel from '../models/NoteModel';
|
|
|
2
2
|
import ShapeModel from '../models/ShapeModel';
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import { FrameDataOptions } from '../models/FrameModel';
|
|
5
|
-
import { AnimationMode, Line, Player } from '../types';
|
|
5
|
+
import { AnimationMode, Line, LinePart, LineType, Player } from '../types';
|
|
6
|
+
import Bezier from '../math/Bezier';
|
|
7
|
+
import { isBallTransferLineType } from './common';
|
|
8
|
+
import { BALL_TRANSFER_LINE_DURATION_DEFAULT } from '../constants';
|
|
6
9
|
|
|
7
10
|
export type TimelineKey = 'keyTimes' | 'keyTimesSeq';
|
|
8
11
|
export type HasKeyTimes = { keyTimes: number[]; keyTimesSeq?: number[] };
|
|
@@ -24,14 +27,53 @@ export const keyTimesToChunks = (keyTimes: number[]): [number, number][] => {
|
|
|
24
27
|
return chunks;
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
export
|
|
30
|
+
export function computeDefaultLineAnimationDuration(lineType: LineType, linePart: LinePart): number {
|
|
31
|
+
if (isBallTransferLineType(lineType)) return BALL_TRANSFER_LINE_DURATION_DEFAULT;
|
|
32
|
+
const [cp1, cp2, cp3, cp4] = linePart.controlPoints;
|
|
33
|
+
return new Bezier(cp1, cp2, cp3 ?? null, cp4 ?? null).length / 100;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const splitChunkByPartDurations = (params: {
|
|
37
|
+
start: number;
|
|
38
|
+
end: number;
|
|
39
|
+
lineParts: LinePart[];
|
|
40
|
+
lineType: LineType;
|
|
41
|
+
}): [number, number][] => {
|
|
42
|
+
const { start, end, lineParts, lineType } = params;
|
|
43
|
+
|
|
44
|
+
const total = end - start;
|
|
45
|
+
if (!Number.isFinite(total) || total <= 0) return [[start, end]];
|
|
46
|
+
|
|
47
|
+
const durations = lineParts.map(lp => computeDefaultLineAnimationDuration(lineType, lp));
|
|
48
|
+
const sum = durations.reduce((acc, v) => acc + (Number.isFinite(v) ? v : 0), 0);
|
|
49
|
+
|
|
50
|
+
// If geometry is degenerate, fall back to equal split.
|
|
51
|
+
const weights =
|
|
52
|
+
sum > 0 ? durations.map(d => (Number.isFinite(d) ? d / sum : 0)) : lineParts.map(() => 1 / lineParts.length);
|
|
53
|
+
|
|
54
|
+
const keyTimes: number[] = [start];
|
|
55
|
+
let cursor = start;
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < weights.length; i += 1) {
|
|
58
|
+
const isLast = i === weights.length - 1;
|
|
59
|
+
const dt = isLast ? end - cursor : total * weights[i];
|
|
60
|
+
cursor += dt;
|
|
61
|
+
keyTimes.push(isLast ? end : cursor);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return keyTimesToChunks(keyTimes);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const deriveLineChunksWithFallback = (params: {
|
|
28
68
|
timeline: TimelineKey;
|
|
29
69
|
playerAnimation: HasKeyTimes;
|
|
30
70
|
lineAnimation: HasKeyTimes;
|
|
31
|
-
|
|
71
|
+
lineType: LineType;
|
|
72
|
+
lineParts: LinePart[];
|
|
32
73
|
legacySupport?: boolean;
|
|
33
74
|
}): [number, number][] => {
|
|
34
|
-
const { timeline, playerAnimation, lineAnimation,
|
|
75
|
+
const { timeline, playerAnimation, lineAnimation, lineType, lineParts, legacySupport = true } = params;
|
|
76
|
+
const linePartsCount = lineParts.length ?? 0;
|
|
35
77
|
|
|
36
78
|
const chunks = keyTimesToChunks(getEffectiveTimeline(lineAnimation, timeline));
|
|
37
79
|
if (chunks.length === linePartsCount) return chunks;
|
|
@@ -39,6 +81,17 @@ export const deriveLineChunksWithPlayerFallback = (params: {
|
|
|
39
81
|
// If there are no chunks (bad/partial data), just return as-is.
|
|
40
82
|
if (!chunks.length) return chunks;
|
|
41
83
|
|
|
84
|
+
// Prefer geometry-based splitting over player animation fallback.
|
|
85
|
+
// Typical legacy case: multi-part line stored as a single [start,end] chunk.
|
|
86
|
+
if (chunks.length === 1 && lineParts?.length) {
|
|
87
|
+
const [[start, end]] = chunks;
|
|
88
|
+
const split = splitChunkByPartDurations({ start, end, lineParts, lineType });
|
|
89
|
+
console.log('geometry based', { chunks, split, linePartsCount });
|
|
90
|
+
if (split.length === linePartsCount) return split;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.warn('PLAYER FALLBACK ATTEMPT!!!', { chunks, linePartsCount });
|
|
94
|
+
|
|
42
95
|
// If legacy support is disabled, do not attempt reconstruction from player timeline.
|
|
43
96
|
if (!legacySupport) return chunks;
|
|
44
97
|
|
|
@@ -68,8 +121,6 @@ export const getLineAnimationChunks = (params: {
|
|
|
68
121
|
const lineAnimation = line.animations?.[0] as unknown as HasKeyTimes | undefined;
|
|
69
122
|
if (!lineAnimation) return [];
|
|
70
123
|
|
|
71
|
-
const linePartsCount = line.lineParts?.length ?? 0;
|
|
72
|
-
|
|
73
124
|
const linePlayer = players.find(p => p.position === line.playerPositionOrigin);
|
|
74
125
|
const playerAnimation = (linePlayer?.animations?.[0] as unknown as HasKeyTimes | undefined) ?? undefined;
|
|
75
126
|
|
|
@@ -79,11 +130,13 @@ export const getLineAnimationChunks = (params: {
|
|
|
79
130
|
}
|
|
80
131
|
|
|
81
132
|
// Full path: handle chunking + legacy reconstruction.
|
|
82
|
-
return
|
|
133
|
+
return deriveLineChunksWithFallback({
|
|
83
134
|
timeline,
|
|
84
135
|
playerAnimation,
|
|
85
136
|
lineAnimation,
|
|
86
|
-
|
|
137
|
+
lineType: line.type,
|
|
138
|
+
lineParts: line.lineParts,
|
|
139
|
+
legacySupport: false
|
|
87
140
|
});
|
|
88
141
|
};
|
|
89
142
|
|
package/src/helpers/common.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { ShapeType } from '../types';
|
|
1
|
+
import { LineType, ShapeType } from '../types';
|
|
2
2
|
import { ShapeModels } from '../index';
|
|
3
3
|
|
|
4
|
+
export function isBallTransferLineType(type: LineType) {
|
|
5
|
+
return ['PASS', 'HANDOFF', 'SHOT'].includes(type);
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
export function animationProgress(globalProgress: number, start: number, end: number) {
|
|
5
9
|
const max = end - start;
|
|
6
10
|
const local = globalProgress - start;
|