@luceosports/play-rendering 2.0.7 → 2.1.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.
@@ -1,4 +1,4 @@
1
- import { Color, CourtPoint, Note, NoteAnimation, NoteFont } from "../../play-rendering";
1
+ import { Color, CourtPoint, Note, Animation, NoteFont } from "../../play-rendering";
2
2
 
3
3
  export class NoteModel {
4
4
  constructor(data: Note);
@@ -7,7 +7,7 @@ export class NoteModel {
7
7
  get location(): CourtPoint;
8
8
  get color(): Color;
9
9
  get showBorder(): boolean;
10
- get animations(): NoteAnimation[];
10
+ get animations(): Animation[];
11
11
  get font(): NoteFont;
12
12
  get fontSize(): number;
13
13
  get lineHeight(): number;
@@ -1,4 +1,4 @@
1
- import { Color, CourtPoint, LinePart, Scale, Shape, ShapeType } from '../../play-rendering';
1
+ import { Color, CourtPoint, LinePart, Scale, Shape, ShapeType, Animation } from '../../play-rendering';
2
2
 
3
3
  export class ShapeModel {
4
4
  constructor(data: Shape)
@@ -12,6 +12,7 @@ export class ShapeModel {
12
12
  get showBorder(): boolean;
13
13
  get outerCircleRadius(): number;
14
14
  get linePart(): LinePart;
15
+ get animations(): Animation[];
15
16
  get rectWrapPointsPure(): {
16
17
  x: number;
17
18
  y: number;
@@ -1,138 +1,136 @@
1
- export type PlayerPosition = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10' | '11';
2
- export type DefenderPosition = 'x1' | 'x2' | 'x3' | 'x4' | 'x5' | 'x6' | 'x7' | 'x8' | 'x9' | 'x10' | 'x11';
3
- export type CoachPosition = 'C';
4
- export type Position = PlayerPosition | DefenderPosition | CoachPosition;
5
- export type LineType = 'PASS' | 'CUT' | 'SCREEN' | 'DRIBBLE' | 'HANDOFF' | 'SHOT';
6
- export type LineShapeType = 'LINE.CUT' | 'LINE.SCREEN' | 'LINE.DRIBBLE' | 'LINE.PASS' | 'LINE.HANDOFF';
7
- export type ShapeType = 'CIRCLE' | 'SQUARE' | 'TRIANGLE' | 'FOV' | 'XMARK' | 'STRAIGHT' | 'CONE' | LineShapeType;
8
- export type SportType = 'FOOTBALL' | 'BASKETBALL' | 'VOLLEYBALL' | 'LACROSSE' | 'SOCCER' | 'HOCKEY' | 'BASEBALL';
9
- export type CourtTypeSportBasketball = 'BIG3' | 'NBA' | 'WNBA' | 'FIBA' | 'NCAAM' | 'NCAAW' | 'US_HIGH_SCHOOL' | 'US_JUNIOR_HIGH';
10
- export type CourtTypeSportVolleyball = 'VOLLEYBALL_INDOOR';
11
- export type CourtTypeSportSoccer = 'SOCCER_FIFA' | 'SOCCER_NCAA' | 'SOCCER_NFHS' | 'SOCCER_U10' | 'SOCCER_U12' | 'SOCCER_U19';
12
- export type CourtTypeSportHockey = 'HOCKEY_NHL' | 'HOCKEY_INTERNATIONAL';
13
- export type CourtTypeSportBaseball = 'BASEBALL_HIGH_SCHOOL';
14
- export type CourtTypeSportLacrosse = 'LACROSSE_US_M' | 'LACROSSE_US_W';
15
- export type CourtTypeSportFootball = 'FOOTBALL_HIGH_SCHOOL';
16
- export type CourtTypeSportFootballLegacy = 'FOOTBALL';
17
- export type CourtType = CourtTypeSportBasketball | CourtTypeSportVolleyball | CourtTypeSportLacrosse | CourtTypeSportSoccer | CourtTypeSportHockey | CourtTypeSportBaseball | CourtTypeSportFootball | CourtTypeSportFootballLegacy;
18
- export type ShapeControlPoints = [CourtPoint, CourtPoint] | [CourtPoint, CourtPoint, CourtPoint] | [CourtPoint, CourtPoint, CourtPoint, CourtPoint];
19
- export type NoteDisplayModes = ['onCourt'] | ['playNote'] | ['onCourt', 'playNote'];
20
- export interface SportConstants {
21
- PLAYER_TOKEN_RADIUS: number;
22
- PLAYER_TOKEN_SCALE: number;
23
- }
24
- export interface CourtTypeConstants {
25
- COURT_RECT_WIDTH: number;
26
- COURT_RECT_HEIGHT: number;
27
- }
28
- export interface CourtPoint {
29
- x: number;
30
- y: number;
31
- }
32
- export interface CourtSize {
33
- height: number;
34
- width: number;
35
- }
36
- export interface Scale {
37
- x: number;
38
- y: number;
39
- }
40
- export interface LinePart {
41
- controlPoints: [CourtPoint, CourtPoint] | [CourtPoint, CourtPoint, CourtPoint] | [CourtPoint, CourtPoint, CourtPoint, CourtPoint];
42
- }
43
- export interface Color {
44
- red: number;
45
- green: number;
46
- blue: number;
47
- alpha: number;
48
- }
49
- export interface CourtRect {
50
- origin: CourtPoint;
51
- size: CourtSize;
52
- }
53
- export interface Court {
54
- type: CourtType;
55
- courtRect: CourtRect;
56
- }
57
- export type PlayerAnimationType = 'POSITION';
58
- export interface PlayerAnimation {
59
- id: string;
60
- type: PlayerAnimationType;
61
- keyTimes: number[];
62
- lineParts: LinePart[];
63
- }
64
- export interface Player {
65
- id: string;
66
- possession: boolean;
67
- color: Color;
68
- position: Position;
69
- location: CourtPoint;
70
- textOverride?: string;
71
- playerHatKey?: string;
72
- animations: PlayerAnimation[];
73
- }
74
- export type LineAnimationType = 'LINESTROKE';
75
- export interface LineAnimation {
76
- id: string;
77
- type: LineAnimationType;
78
- keyTimes: number[];
79
- strokeStartValues: [number, number, number];
80
- }
81
- export interface Line {
82
- id: string;
83
- type: LineType;
84
- phase: number;
85
- playerPositionOrigin: Position;
86
- playerPositionTerminus: Position | null;
87
- playerLineSequence: number;
88
- lineParts: LinePart[];
89
- color: Color;
90
- hideLineTip?: boolean;
91
- animations: LineAnimation[];
92
- }
93
- export interface Shape {
94
- id: string;
95
- type: ShapeType;
96
- location: CourtPoint;
97
- color: Color;
98
- scale: Scale;
99
- angle?: number;
100
- showBorder?: boolean;
101
- linePart?: LinePart;
102
- }
103
- export interface Note {
104
- id: string;
105
- location: CourtPoint;
106
- displayModes: NoteDisplayModes;
107
- text: string;
108
- animations: NoteAnimation[];
109
- font: NoteFont;
110
- color: Color;
111
- showBorder: boolean;
112
- }
113
- export interface NoteAnimation {
114
- id: string;
115
- keyTimes: number[];
116
- }
117
- export interface NoteFont {
118
- bold: boolean;
119
- italic: boolean;
120
- underline: boolean;
121
- strikethrough: boolean;
122
- fontSize: number;
123
- }
124
- export interface PlayConstructData {
125
- id?: string;
126
- lastUpdtTS?: string;
127
- name: string;
128
- playData: PlayData;
129
- }
130
- export interface PlayData {
131
- animationDuration: number;
132
- sport: SportType;
133
- court: Court;
134
- players: Player[];
135
- lines: Line[];
136
- shapes?: Shape[];
137
- notes?: Note[];
138
- }
1
+ export type PlayerPosition = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10' | '11';
2
+ export type DefenderPosition = 'x1' | 'x2' | 'x3' | 'x4' | 'x5' | 'x6' | 'x7' | 'x8' | 'x9' | 'x10' | 'x11';
3
+ export type CoachPosition = 'C';
4
+ export type Position = PlayerPosition | DefenderPosition | CoachPosition;
5
+ export type LineType = 'PASS' | 'CUT' | 'SCREEN' | 'DRIBBLE' | 'HANDOFF' | 'SHOT';
6
+ export type LineShapeType = 'LINE.CUT' | 'LINE.SCREEN' | 'LINE.DRIBBLE' | 'LINE.PASS' | 'LINE.HANDOFF';
7
+ export type ShapeType = 'CIRCLE' | 'SQUARE' | 'TRIANGLE' | 'FOV' | 'XMARK' | 'STRAIGHT' | 'CONE' | LineShapeType;
8
+ export type SportType = 'FOOTBALL' | 'BASKETBALL' | 'VOLLEYBALL' | 'LACROSSE' | 'SOCCER' | 'HOCKEY' | 'BASEBALL';
9
+ export type CourtTypeSportBasketball = 'BIG3' | 'NBA' | 'WNBA' | 'FIBA' | 'NCAAM' | 'NCAAW' | 'US_HIGH_SCHOOL' | 'US_JUNIOR_HIGH';
10
+ export type CourtTypeSportVolleyball = 'VOLLEYBALL_INDOOR';
11
+ export type CourtTypeSportSoccer = 'SOCCER_FIFA' | 'SOCCER_NCAA' | 'SOCCER_NFHS' | 'SOCCER_U10' | 'SOCCER_U12' | 'SOCCER_U19';
12
+ export type CourtTypeSportHockey = 'HOCKEY_NHL' | 'HOCKEY_INTERNATIONAL';
13
+ export type CourtTypeSportBaseball = 'BASEBALL_HIGH_SCHOOL';
14
+ export type CourtTypeSportLacrosse = 'LACROSSE_US_M' | 'LACROSSE_US_W';
15
+ export type CourtTypeSportFootball = 'FOOTBALL_HIGH_SCHOOL';
16
+ export type CourtTypeSportFootballLegacy = 'FOOTBALL';
17
+ export type CourtType = CourtTypeSportBasketball | CourtTypeSportVolleyball | CourtTypeSportLacrosse | CourtTypeSportSoccer | CourtTypeSportHockey | CourtTypeSportBaseball | CourtTypeSportFootball | CourtTypeSportFootballLegacy;
18
+ export type ShapeControlPoints = [CourtPoint, CourtPoint] | [CourtPoint, CourtPoint, CourtPoint] | [CourtPoint, CourtPoint, CourtPoint, CourtPoint];
19
+ export type NoteDisplayModes = ['onCourt'] | ['playNote'] | ['onCourt', 'playNote'];
20
+ export interface SportConstants {
21
+ PLAYER_TOKEN_RADIUS: number;
22
+ PLAYER_TOKEN_SCALE: number;
23
+ }
24
+ export interface CourtTypeConstants {
25
+ COURT_RECT_WIDTH: number;
26
+ COURT_RECT_HEIGHT: number;
27
+ }
28
+ export interface CourtPoint {
29
+ x: number;
30
+ y: number;
31
+ }
32
+ export interface CourtSize {
33
+ height: number;
34
+ width: number;
35
+ }
36
+ export interface Scale {
37
+ x: number;
38
+ y: number;
39
+ }
40
+ export interface LinePart {
41
+ controlPoints: [CourtPoint, CourtPoint] | [CourtPoint, CourtPoint, CourtPoint] | [CourtPoint, CourtPoint, CourtPoint, CourtPoint];
42
+ }
43
+ export interface Color {
44
+ red: number;
45
+ green: number;
46
+ blue: number;
47
+ alpha: number;
48
+ }
49
+ export interface CourtRect {
50
+ origin: CourtPoint;
51
+ size: CourtSize;
52
+ }
53
+ export interface Court {
54
+ type: CourtType;
55
+ courtRect: CourtRect;
56
+ }
57
+ export type PlayerAnimationType = 'POSITION';
58
+ export interface Animation {
59
+ id: string;
60
+ keyTimes: number[];
61
+ }
62
+ export interface PlayerAnimation extends Animation {
63
+ type: PlayerAnimationType;
64
+ lineParts: LinePart[];
65
+ }
66
+ export interface Player {
67
+ id: string;
68
+ possession: boolean;
69
+ color: Color;
70
+ position: Position;
71
+ location: CourtPoint;
72
+ textOverride?: string;
73
+ animations: PlayerAnimation[];
74
+ }
75
+ export type LineAnimationType = 'LINESTROKE';
76
+ export interface LineAnimation {
77
+ id: string;
78
+ type: LineAnimationType;
79
+ keyTimes: number[];
80
+ strokeStartValues: [number, number, number];
81
+ }
82
+ export interface Line {
83
+ id: string;
84
+ type: LineType;
85
+ phase: number;
86
+ playerPositionOrigin: Position;
87
+ playerPositionTerminus: Position | null;
88
+ playerLineSequence: number;
89
+ lineParts: LinePart[];
90
+ color: Color;
91
+ hideLineTip?: boolean;
92
+ animations: LineAnimation[];
93
+ }
94
+ export interface Shape {
95
+ id: string;
96
+ type: ShapeType;
97
+ location: CourtPoint;
98
+ color: Color;
99
+ scale: Scale;
100
+ angle?: number;
101
+ showBorder?: boolean;
102
+ linePart?: LinePart;
103
+ animations: Animation[];
104
+ }
105
+ export interface Note {
106
+ id: string;
107
+ location: CourtPoint;
108
+ displayModes: NoteDisplayModes;
109
+ text: string;
110
+ animations: Animation[];
111
+ font: NoteFont;
112
+ color: Color;
113
+ showBorder: boolean;
114
+ }
115
+ export interface NoteFont {
116
+ bold: boolean;
117
+ italic: boolean;
118
+ underline: boolean;
119
+ strikethrough: boolean;
120
+ fontSize: number;
121
+ }
122
+ export interface PlayConstructData {
123
+ id?: string;
124
+ lastUpdtTS?: string;
125
+ name: string;
126
+ playData: PlayData;
127
+ }
128
+ export interface PlayData {
129
+ animationDuration: number;
130
+ sport: SportType;
131
+ court: Court;
132
+ players: Player[];
133
+ lines: Line[];
134
+ shapes?: Shape[];
135
+ notes?: Note[];
136
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luceosports/play-rendering",
3
- "version": "2.0.7",
3
+ "version": "2.1.0",
4
4
  "main": "dist/play-rendering.js",
5
5
  "types": "dist/play-rendering.d.ts",
6
6
  "scripts": {
@@ -0,0 +1,32 @@
1
+ import NoteModel from '../models/NoteModel';
2
+ import ShapeModel from '../models/ShapeModel';
3
+ import _ from 'lodash';
4
+ import { FrameDataOptions } from '../models/FrameModel';
5
+
6
+ export function computeAnimationAlpha(model: NoteModel | ShapeModel, options: FrameDataOptions) {
7
+ let alpha = 1;
8
+ if (options.animationGlobalProgress) {
9
+ alpha = 0;
10
+ const animationKeyTimeChunks = model.animations.map(a => {
11
+ const [startPercent, endPercent] = a.keyTimes;
12
+ return [startPercent, endPercent];
13
+ });
14
+ animationKeyTimeChunks.forEach(([start, end]) => {
15
+ const fadeInDuration = 0.03;
16
+ const fadeInStart = start - fadeInDuration;
17
+ const fadeInEnd = start;
18
+ const fadeOutStart = end;
19
+ const fadeOutEnd = end + fadeInDuration;
20
+ if (_.inRange(options.animationGlobalProgress, fadeInStart, fadeInEnd)) {
21
+ alpha = (fadeInDuration - (fadeInEnd - options.animationGlobalProgress)) * 10;
22
+ }
23
+ if (_.inRange(options.animationGlobalProgress, start, end)) {
24
+ alpha = 1;
25
+ }
26
+ if (_.inRange(options.animationGlobalProgress, fadeOutStart, fadeOutEnd)) {
27
+ alpha = (fadeOutEnd - options.animationGlobalProgress) * 10;
28
+ }
29
+ });
30
+ }
31
+ return alpha;
32
+ }
@@ -1,9 +1,9 @@
1
- import _ from 'lodash';
2
1
  import BaseLayer from './base/BaseLayer';
3
2
  import NoteModel from '../models/NoteModel';
3
+ import { computeAnimationAlpha } from '../helpers/animation';
4
4
 
5
5
  export default class NoteLayer extends BaseLayer {
6
- private noteAlpha?: number;
6
+ private animationAlpha?: number;
7
7
 
8
8
  apply() {
9
9
  this.ctx.save();
@@ -11,7 +11,7 @@ export default class NoteLayer extends BaseLayer {
11
11
  this.playData.notes.forEach(note => {
12
12
  this.ctx.save();
13
13
 
14
- this.setNoteAlpha(note);
14
+ this.animationAlpha = computeAnimationAlpha(note, this.options);
15
15
 
16
16
  this.setColor(note);
17
17
 
@@ -30,39 +30,8 @@ export default class NoteLayer extends BaseLayer {
30
30
  this.ctx.restore();
31
31
  }
32
32
 
33
- setNoteAlpha(note: NoteModel) {
34
- let alpha = 1;
35
- if (this.options.animationGlobalProgress) {
36
- alpha = 0;
37
- const animationKeyTimeChunks = note.animations.map(a => {
38
- const [startPercent, endPercent] = a.keyTimes;
39
- return [
40
- (this.options.playAnimationDuration / 100) * startPercent * 100,
41
- (this.options.playAnimationDuration / 100) * endPercent * 100
42
- ];
43
- });
44
- animationKeyTimeChunks.forEach(([start, end]) => {
45
- const fadeInDuration = 0.03;
46
- const fadeInStart = start - fadeInDuration;
47
- const fadeInEnd = start;
48
- const fadeOutStart = end;
49
- const fadeOutEnd = end + fadeInDuration;
50
- if (_.inRange(this.options.animationGlobalProgress, fadeInStart, fadeInEnd)) {
51
- alpha = (fadeInDuration - (fadeInEnd - this.options.animationGlobalProgress)) * 10;
52
- }
53
- if (_.inRange(this.options.animationGlobalProgress, start, end)) {
54
- alpha = 1;
55
- }
56
- if (_.inRange(this.options.animationGlobalProgress, fadeOutStart, fadeOutEnd)) {
57
- alpha = (fadeOutEnd - this.options.animationGlobalProgress) * 10;
58
- }
59
- });
60
- }
61
- this.noteAlpha = alpha;
62
- }
63
-
64
33
  setColor(note: NoteModel) {
65
- if (typeof this.noteAlpha === 'undefined') throw new Error('noteAlpha is not set');
34
+ if (typeof this.animationAlpha === 'undefined') throw new Error('animationAlpha is not set');
66
35
 
67
36
  const { alpha, blue, green, red } = note.color;
68
37
 
@@ -70,12 +39,12 @@ export default class NoteLayer extends BaseLayer {
70
39
  const g = Math.ceil(green * 255);
71
40
  const b = Math.ceil(blue * 255);
72
41
 
73
- this.ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${alpha * this.noteAlpha})`;
74
- this.ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${this.noteAlpha})`;
42
+ this.ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${alpha * this.animationAlpha})`;
43
+ this.ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${this.animationAlpha})`;
75
44
 
76
45
  if (this.options.noteSelectedId === note.id) {
77
46
  this.ctx.setLineDash([0.5, 0.5]);
78
- this.ctx.strokeStyle = `rgba(28, 127, 181, ${this.noteAlpha})`;
47
+ this.ctx.strokeStyle = `rgba(28, 127, 181, ${this.animationAlpha})`;
79
48
  }
80
49
  }
81
50
 
@@ -109,7 +78,7 @@ export default class NoteLayer extends BaseLayer {
109
78
  wordStartX += note.playerTokenRadius * 2;
110
79
  }
111
80
 
112
- this.ctx.fillStyle = `rgba(0, 0, 0, ${this.noteAlpha})`;
81
+ this.ctx.fillStyle = `rgba(0, 0, 0, ${this.animationAlpha})`;
113
82
  const wordText = matches.length ? word.text.replace(/(<player=(\d*)>)/gi, '') : word.text;
114
83
  this.ctx.fillText(wordText, wordStartX, 0);
115
84
  wordStartX += word.width;
@@ -130,7 +99,7 @@ export default class NoteLayer extends BaseLayer {
130
99
 
131
100
  if (note.font.underline || note.font.strikethrough) {
132
101
  this.ctx.lineWidth = 0.07 * note.fontSize;
133
- this.ctx.strokeStyle = `rgba(0, 0, 0, ${this.noteAlpha})`;
102
+ this.ctx.strokeStyle = `rgba(0, 0, 0, ${this.animationAlpha})`;
134
103
  this.ctx.beginPath();
135
104
  if (note.font.underline) {
136
105
  this.ctx.moveTo(0, note.fontSize - NoteModel.NOTE_LINE_HEIGHT_OFFSET / 2);
@@ -153,8 +122,8 @@ export default class NoteLayer extends BaseLayer {
153
122
  this.ctx.translate(x + note.playerTokenRadius, 0);
154
123
 
155
124
  this.ctx.beginPath();
156
- this.ctx.strokeStyle = `rgba(255, 255, 255, ${this.noteAlpha})`;
157
- this.ctx.fillStyle = `rgba(243, 111, 33, ${this.noteAlpha})`;
125
+ this.ctx.strokeStyle = `rgba(255, 255, 255, ${this.animationAlpha})`;
126
+ this.ctx.fillStyle = `rgba(243, 111, 33, ${this.animationAlpha})`;
158
127
  this.ctx.arc(0, note.playerTokenRadius, note.playerTokenRadius, 0, Math.PI * 2);
159
128
  this.ctx.fill();
160
129
  this.ctx.stroke();
@@ -163,7 +132,7 @@ export default class NoteLayer extends BaseLayer {
163
132
  this.ctx.font = `${playerFontSize}px Arial`;
164
133
  const textX = -this.ctx.measureText(label).width / 2;
165
134
  const textY = ((note.playerTokenRadius * 2 - playerFontSize) / 2) * (1 + NoteModel.NOTE_LINE_HEIGHT_OFFSET);
166
- this.ctx.fillStyle = `rgba(255, 255, 255, ${this.noteAlpha})`;
135
+ this.ctx.fillStyle = `rgba(255, 255, 255, ${this.animationAlpha})`;
167
136
  this.ctx.fillText(label, textX, textY);
168
137
 
169
138
  this.ctx.restore();
@@ -1,15 +1,18 @@
1
1
  import InternalBaseLayer from '../../base/InternalBaseLayer';
2
2
  import ShapeLayer from '../../ShapeLayer';
3
3
  import ShapeModel from '../../../models/ShapeModel';
4
+ import { computeAnimationAlpha } from '../../../helpers/animation';
4
5
 
5
6
  const DEBUG_WRAP_POINTS = false;
6
7
 
7
8
  export default class InternalShapeLayer extends InternalBaseLayer {
8
9
  protected readonly shape: ShapeModel;
10
+ protected animationAlpha: number;
9
11
 
10
12
  constructor(parentLayer: ShapeLayer, shape: ShapeModel) {
11
13
  super(parentLayer);
12
14
  this.shape = shape;
15
+ this.animationAlpha = computeAnimationAlpha(shape, parentLayer.options);
13
16
  }
14
17
 
15
18
  get staticData() {
@@ -40,8 +43,8 @@ export default class InternalShapeLayer extends InternalBaseLayer {
40
43
  const g = Math.ceil(green * 255);
41
44
  const b = Math.ceil(blue * 255);
42
45
 
43
- this.ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${alpha})`;
44
- this.ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${alpha ? 1 : 0})`;
46
+ this.ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${alpha * this.animationAlpha})`;
47
+ this.ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${alpha ? this.animationAlpha : 0})`;
45
48
  }
46
49
 
47
50
  drawShapeWrapPoints() {
@@ -489,8 +489,9 @@ export default class FrameModel {
489
489
  prepareCourtPoint(courtPoint: CourtPoint) {
490
490
  const { mirror } = this.play.options;
491
491
  const { width } = this.play.playData.court.courtRect.size;
492
+ const { origin } = this.play.playData.court.courtRect;
492
493
  return {
493
- x: mirror ? width - courtPoint.x : courtPoint.x,
494
+ x: mirror ? width + origin.x - (courtPoint.x - origin.x) : courtPoint.x,
494
495
  y: courtPoint.y
495
496
  };
496
497
  }
@@ -49,6 +49,10 @@ export default abstract class ShapeModel extends Model<ShapeData, ShapeData> {
49
49
  return this._getAttr('linePart');
50
50
  }
51
51
 
52
+ get animations() {
53
+ return this._getAttr('animations') || [];
54
+ }
55
+
52
56
  get rectWrapPointsPure() {
53
57
  const topLeft = { x: -this.outerCircleRadius / 2, y: -this.outerCircleRadius / 2 };
54
58
  const topRight = { x: this.outerCircleRadius / 2, y: -this.outerCircleRadius / 2 };
@@ -112,10 +112,13 @@ export interface Court {
112
112
 
113
113
  export type PlayerAnimationType = 'POSITION';
114
114
 
115
- export interface PlayerAnimation {
115
+ export interface Animation {
116
116
  id: string;
117
- type: PlayerAnimationType;
118
117
  keyTimes: number[];
118
+ }
119
+
120
+ export interface PlayerAnimation extends Animation {
121
+ type: PlayerAnimationType;
119
122
  lineParts: LinePart[];
120
123
  }
121
124
 
@@ -160,6 +163,7 @@ export interface Shape {
160
163
  angle?: number;
161
164
  showBorder?: boolean;
162
165
  linePart?: LinePart;
166
+ animations: Animation[];
163
167
  }
164
168
 
165
169
  export interface Note {
@@ -167,17 +171,12 @@ export interface Note {
167
171
  location: CourtPoint;
168
172
  displayModes: NoteDisplayModes;
169
173
  text: string;
170
- animations: NoteAnimation[];
174
+ animations: Animation[];
171
175
  font: NoteFont;
172
176
  color: Color;
173
177
  showBorder: boolean;
174
178
  }
175
179
 
176
- export interface NoteAnimation {
177
- id: string;
178
- keyTimes: number[];
179
- }
180
-
181
180
  export interface NoteFont {
182
181
  bold: boolean;
183
182
  italic: boolean;