@luceosports/play-rendering 2.0.2 → 2.0.4
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 +3 -3
- package/dist/play-rendering.js.map +1 -1
- package/dist/types/models/PlayModel.d.ts +1 -1
- package/dist/types/models/ShapeModels/index.d.ts +2 -2
- package/package.json +1 -1
- package/src/helpers/common.ts +1 -1
- package/src/layers/CourtLayer.ts +9 -8
- package/src/layers/LineLayer.ts +2 -2
- package/src/layers/NoteLayer.ts +5 -3
- package/src/layers/PlayerLayer.ts +23 -6
- package/src/layers/ShapeLayer.ts +2 -2
- package/src/layers/court/layers/BASEBALL/courtTypes/BASEBALL_HIGH_SCHOOL/layers/ADirtLayer.ts +2 -1
- package/src/layers/court/layers/BASEBALL/courtTypes/BASEBALL_HIGH_SCHOOL/layers/BaseLineLayer.ts +1 -1
- package/src/layers/court/layers/BASKETBALL/common/LaneMarkingNBATrait.ts +13 -1
- package/src/layers/court/layers/BASKETBALL/common/LaneMarkingNCAATrait.ts +13 -1
- package/src/layers/court/layers/BASKETBALL/courtTypes/BIG3/layers/Big3Layer.ts +1 -1
- package/src/layers/court/layers/FOOTBALL/layers/FieldNumberLayer.ts +1 -1
- package/src/layers/court/layers/LACROSSE/courtTypes/LACROSSE_US_W/layers/FanLineLayer.ts +2 -1
- package/src/layers/line/base/InternalLineLayer.ts +5 -5
- package/src/layers/line/layers/DribbleLineLayer.ts +4 -4
- package/src/layers/shape/base/InternalShapeLayer.ts +1 -1
- package/src/layers/shape/layers/line/DribbleLineShapeLayer.ts +1 -1
- package/src/layers/shape/layers/line/base/InternalLineShapeLayer.ts +1 -1
- package/src/math/Bezier.ts +7 -5
- package/src/math/LineDrawingMath.ts +15 -15
- package/src/models/AnimationModel.ts +11 -11
- package/src/models/Base/InternalFrameModel.ts +2 -2
- package/src/models/FrameModel.ts +18 -15
- package/src/models/LineModel.ts +2 -2
- package/src/models/NoteModel.ts +7 -7
- package/src/models/PlayModel.ts +3 -3
- package/src/models/PlayerModel.ts +1 -1
- package/src/models/ShapeModel.ts +3 -6
- package/src/models/ShapeModels/CircleShape.ts +4 -0
- package/src/models/ShapeModels/LineShape.ts +7 -2
- package/src/traits/DribbleLineTrait.ts +17 -2
- package/src/traits/LineDrawOperationsTrait.ts +31 -7
- package/tsconfig.json +2 -1
|
@@ -15,7 +15,7 @@ export type PlayModelOptions = {
|
|
|
15
15
|
linesSelectedIds: string[];
|
|
16
16
|
shapeSelectedId: string | null;
|
|
17
17
|
noteSelectedId: string | null;
|
|
18
|
-
playersHiddenPositions
|
|
18
|
+
playersHiddenPositions: string[];
|
|
19
19
|
background: string;
|
|
20
20
|
watermark: string | null;
|
|
21
21
|
mirror: boolean;
|
|
@@ -10,5 +10,5 @@ import LineShape from './LineShape';
|
|
|
10
10
|
import SquareShape from './SquareShape';
|
|
11
11
|
import StraightShape from './StraightShape';
|
|
12
12
|
import TriangleShape from './TriangleShape';
|
|
13
|
-
import
|
|
14
|
-
export { CutLineShape, DribbleLineShape, HandoffLineShape, PassLineShape, ScreenLineShape, CircleShape, ConeShape, FovShape, LineShape, SquareShape, StraightShape, TriangleShape,
|
|
13
|
+
import XmarkShape from './XmarkShape';
|
|
14
|
+
export { CutLineShape, DribbleLineShape, HandoffLineShape, PassLineShape, ScreenLineShape, CircleShape, ConeShape, FovShape, LineShape, SquareShape, StraightShape, TriangleShape, XmarkShape };
|
package/package.json
CHANGED
package/src/helpers/common.ts
CHANGED
|
@@ -25,7 +25,7 @@ export function shapeModelKeyFromImport(shapeModels: typeof ShapeModels, type: S
|
|
|
25
25
|
const prefix = type.replace('LINE.', '');
|
|
26
26
|
const suffix = type.indexOf('LINE.') >= 0 ? 'LineShape' : 'Shape';
|
|
27
27
|
return key.toLowerCase() === `${prefix}${suffix}`.toLowerCase();
|
|
28
|
-
});
|
|
28
|
+
}) as keyof typeof ShapeModels;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export async function loadImage(src: string | Buffer) {
|
package/src/layers/CourtLayer.ts
CHANGED
|
@@ -23,7 +23,7 @@ export default class CourtLayer extends BaseLayer {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
get background() {
|
|
26
|
-
return this.options.background || this.playData.sport;
|
|
26
|
+
return (this.options.background || this.playData.sport) as keyof typeof this.staticData.backgroundOptions;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
apply() {
|
|
@@ -58,8 +58,7 @@ export default class CourtLayer extends BaseLayer {
|
|
|
58
58
|
|
|
59
59
|
setBackground() {
|
|
60
60
|
if (this.staticData.backgroundOptions && this.staticData.backgroundOptions[this.background]) {
|
|
61
|
-
|
|
62
|
-
this.ctx.fillStyle = ptrn;
|
|
61
|
+
this.ctx.fillStyle = this.ctx.createPattern(this.staticData.backgroundOptions[this.background], 'repeat')!;
|
|
63
62
|
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
|
64
63
|
} else if (this.background.indexOf('#') === 0) {
|
|
65
64
|
this.ctx.save();
|
|
@@ -70,8 +69,10 @@ export default class CourtLayer extends BaseLayer {
|
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
setWatermark() {
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
const watermarkImage = this.options.watermark
|
|
73
|
+
? this.staticData.watermark[this.options.watermark as keyof typeof this.staticData.watermark]
|
|
74
|
+
: null;
|
|
75
|
+
if (watermarkImage) {
|
|
75
76
|
const aRatio = watermarkImage.width / watermarkImage.height;
|
|
76
77
|
const maxWatermarkLimit = this.frameWidth / 5;
|
|
77
78
|
const destination = {
|
|
@@ -92,11 +93,11 @@ export default class CourtLayer extends BaseLayer {
|
|
|
92
93
|
const dy = (this.playData.court.courtRect.size.height - fullCourtIndentY - 1) * this.scale - destination.height;
|
|
93
94
|
|
|
94
95
|
this.ctx.drawImage(
|
|
95
|
-
|
|
96
|
+
watermarkImage,
|
|
96
97
|
0,
|
|
97
98
|
0,
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
watermarkImage.width,
|
|
100
|
+
watermarkImage.height,
|
|
100
101
|
dx,
|
|
101
102
|
dy,
|
|
102
103
|
destination.width,
|
package/src/layers/LineLayer.ts
CHANGED
|
@@ -16,8 +16,8 @@ export default class LineLayer extends BaseLayer {
|
|
|
16
16
|
this.playData.lines.forEach(line => {
|
|
17
17
|
if (this.options.linesHiddenIds.indexOf(line.id) >= 0) return;
|
|
18
18
|
const layerKey = `${_.capitalize(line.type)}LineLayer`;
|
|
19
|
-
if (lineLayers[layerKey]) {
|
|
20
|
-
new lineLayers[layerKey](this, line).apply();
|
|
19
|
+
if (lineLayers[layerKey as keyof typeof lineLayers]) {
|
|
20
|
+
new lineLayers[layerKey as keyof typeof lineLayers](this, line).apply();
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
23
|
|
package/src/layers/NoteLayer.ts
CHANGED
|
@@ -3,7 +3,7 @@ import BaseLayer from './base/BaseLayer';
|
|
|
3
3
|
import NoteModel from '../models/NoteModel';
|
|
4
4
|
|
|
5
5
|
export default class NoteLayer extends BaseLayer {
|
|
6
|
-
private noteAlpha
|
|
6
|
+
private noteAlpha?: number;
|
|
7
7
|
|
|
8
8
|
apply() {
|
|
9
9
|
this.ctx.save();
|
|
@@ -62,6 +62,8 @@ export default class NoteLayer extends BaseLayer {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
setColor(note: NoteModel) {
|
|
65
|
+
if (typeof this.noteAlpha === 'undefined') throw new Error('noteAlpha is not set');
|
|
66
|
+
|
|
65
67
|
const { alpha, blue, green, red } = note.color;
|
|
66
68
|
|
|
67
69
|
const r = Math.ceil(red * 255);
|
|
@@ -82,7 +84,7 @@ export default class NoteLayer extends BaseLayer {
|
|
|
82
84
|
this.ctx.textBaseline = note.textBaseline;
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
drawBubble(note: NoteModel, { width, height }) {
|
|
87
|
+
drawBubble(note: NoteModel, { width, height }: { width: number; height: number }) {
|
|
86
88
|
this.ctx.lineWidth = this.courtTypeConstants.COURT_LINE_WIDTH;
|
|
87
89
|
this.ctx.beginPath();
|
|
88
90
|
this.ctx.rect(0, 0, width, height);
|
|
@@ -92,7 +94,7 @@ export default class NoteLayer extends BaseLayer {
|
|
|
92
94
|
}
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
drawText(note: NoteModel, lines) {
|
|
97
|
+
drawText(note: NoteModel, lines: { words: { text: string; width: number }[] }[]) {
|
|
96
98
|
lines.forEach(({ words }, index) => {
|
|
97
99
|
this.ctx.save();
|
|
98
100
|
|
|
@@ -106,14 +106,31 @@ export default class PlayerLayer extends BaseLayer {
|
|
|
106
106
|
const playerMapItem = this.options.playersMap.find(item => item.position === player.position);
|
|
107
107
|
const headshotImage = this.staticData.playerHeadshots.find(item => item.id === playerMapItem?.teamPlayerId);
|
|
108
108
|
if (headshotImage) {
|
|
109
|
+
const imageVertical = headshotImage.image.width < headshotImage.image.height;
|
|
110
|
+
const imageHorizontal = headshotImage.image.height < headshotImage.image.width;
|
|
111
|
+
|
|
112
|
+
const imageScale = imageVertical
|
|
113
|
+
? headshotImage.image.height / headshotImage.image.width
|
|
114
|
+
: imageHorizontal
|
|
115
|
+
? headshotImage.image.width / headshotImage.image.height
|
|
116
|
+
: 1;
|
|
117
|
+
|
|
118
|
+
const dw =
|
|
119
|
+
this.radius * 2 * (imageVertical ? headshotImage.image.width / headshotImage.image.height : 1) * imageScale;
|
|
120
|
+
const dh =
|
|
121
|
+
this.radius * 2 * (imageHorizontal ? headshotImage.image.height / headshotImage.image.width : 1) * imageScale;
|
|
122
|
+
|
|
123
|
+
const topLeftX = 0 - (imageHorizontal ? dw / 2 : this.radius);
|
|
124
|
+
const topLeftY = 0 - (imageVertical ? dh / 2 : this.radius);
|
|
125
|
+
|
|
109
126
|
this.ctx.save();
|
|
110
127
|
this.ctx.translate(x, y);
|
|
111
|
-
if (this.options.flipPlayerLabels
|
|
128
|
+
if (this.options.flipPlayerLabels) {
|
|
112
129
|
this.ctx.rotate(Math.PI);
|
|
113
130
|
}
|
|
114
131
|
this.ctx.arc(0, 0, this.radius, 0, Math.PI * 2, true);
|
|
115
132
|
this.ctx.clip();
|
|
116
|
-
this.ctx.drawImage(headshotImage.image,
|
|
133
|
+
this.ctx.drawImage(headshotImage.image, topLeftX, topLeftY, dw, dh);
|
|
117
134
|
this.ctx.restore();
|
|
118
135
|
return;
|
|
119
136
|
}
|
|
@@ -135,7 +152,7 @@ export default class PlayerLayer extends BaseLayer {
|
|
|
135
152
|
this.ctx.textAlign = 'center';
|
|
136
153
|
this.ctx.font = `${fontSize}px Helvetica`;
|
|
137
154
|
|
|
138
|
-
if (this.options.mirror
|
|
155
|
+
if (this.options.mirror) {
|
|
139
156
|
this.ctx.save();
|
|
140
157
|
this.ctx.translate(x * 2, 0);
|
|
141
158
|
this.ctx.scale(-1, 1);
|
|
@@ -144,7 +161,7 @@ export default class PlayerLayer extends BaseLayer {
|
|
|
144
161
|
let px = x;
|
|
145
162
|
let py = y;
|
|
146
163
|
|
|
147
|
-
if (this.options.flipPlayerLabels
|
|
164
|
+
if (this.options.flipPlayerLabels) {
|
|
148
165
|
px = 0;
|
|
149
166
|
py = 0;
|
|
150
167
|
this.ctx.save();
|
|
@@ -154,11 +171,11 @@ export default class PlayerLayer extends BaseLayer {
|
|
|
154
171
|
|
|
155
172
|
this.ctx.fillText(player.textLabel, px, py + textVerticalOffset);
|
|
156
173
|
|
|
157
|
-
if (this.options.flipPlayerLabels
|
|
174
|
+
if (this.options.flipPlayerLabels) {
|
|
158
175
|
this.ctx.restore();
|
|
159
176
|
}
|
|
160
177
|
|
|
161
|
-
if (this.options.mirror
|
|
178
|
+
if (this.options.mirror) {
|
|
162
179
|
this.ctx.restore();
|
|
163
180
|
}
|
|
164
181
|
}
|
package/src/layers/ShapeLayer.ts
CHANGED
|
@@ -11,8 +11,8 @@ export default class ShapeLayer extends BaseLayer {
|
|
|
11
11
|
? `${_.capitalize(shape.type.replace('LINE.', ''))}Line`
|
|
12
12
|
: _.capitalize(shape.type);
|
|
13
13
|
const layerKey = `${shapeType}ShapeLayer`;
|
|
14
|
-
if (shapeLayers[layerKey]) {
|
|
15
|
-
new shapeLayers[layerKey](this, shape).apply();
|
|
14
|
+
if (shapeLayers[layerKey as keyof typeof shapeLayers]) {
|
|
15
|
+
new shapeLayers[layerKey as keyof typeof shapeLayers](this, shape).apply();
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
18
|
|
package/src/layers/court/layers/BASEBALL/courtTypes/BASEBALL_HIGH_SCHOOL/layers/ADirtLayer.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
PITCHERS_PLATE_TO_PITCHING_MOUND_CENTER,
|
|
12
12
|
PITCHING_MOUND_DIA
|
|
13
13
|
} from '../constants';
|
|
14
|
+
import { CourtPoint } from '../../../../../../../types';
|
|
14
15
|
|
|
15
16
|
export default class ADirtLayer extends InternalCourtLayer {
|
|
16
17
|
original() {
|
|
@@ -119,6 +120,6 @@ export default class ADirtLayer extends InternalCourtLayer {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
function getArcPointForAngle(c, radius, angle) {
|
|
123
|
+
function getArcPointForAngle(c: CourtPoint, radius: number, angle: number) {
|
|
123
124
|
return { x: c.x + Math.cos(angle) * radius, y: c.y + Math.sin(angle) * radius };
|
|
124
125
|
}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import { CourtPoint, CourtTypeConstants } from '../../../../../types';
|
|
2
|
+
|
|
3
|
+
interface InheritedPropsAndMethods {
|
|
4
|
+
ctx: CanvasRenderingContext2D;
|
|
5
|
+
courtTypeConstants: CourtTypeConstants & Record<string, any>;
|
|
6
|
+
courtCenter: CourtPoint;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface LineMarkingNBATrait extends InheritedPropsAndMethods {
|
|
10
|
+
drawLogic: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
const COURT_INNER_MARK_OFFSET = 5;
|
|
2
14
|
|
|
3
15
|
export default {
|
|
@@ -31,4 +43,4 @@ export default {
|
|
|
31
43
|
this.ctx.lineTo(courtInnerMarkRightX - 0.5, 13.0);
|
|
32
44
|
this.ctx.stroke();
|
|
33
45
|
}
|
|
34
|
-
};
|
|
46
|
+
} as LineMarkingNBATrait;
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import { CourtPoint, CourtTypeConstants } from '../../../../../types';
|
|
2
|
+
|
|
3
|
+
interface InheritedPropsAndMethods {
|
|
4
|
+
ctx: CanvasRenderingContext2D;
|
|
5
|
+
courtTypeConstants: CourtTypeConstants & Record<string, any>;
|
|
6
|
+
courtCenter: CourtPoint;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface LineMarkingNCAATrait extends InheritedPropsAndMethods {
|
|
10
|
+
drawLogic: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
export default {
|
|
2
14
|
drawLogic() {
|
|
3
15
|
const courtMarkingWidth = 0.667;
|
|
@@ -27,4 +39,4 @@ export default {
|
|
|
27
39
|
this.ctx.fill();
|
|
28
40
|
this.ctx.stroke();
|
|
29
41
|
}
|
|
30
|
-
};
|
|
42
|
+
} as LineMarkingNCAATrait;
|
|
@@ -36,7 +36,7 @@ export default class Big3Layer extends InternalCourtLayer {
|
|
|
36
36
|
this.ctx.fill();
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
this.ctx.fillStyle = this.options.lineColor
|
|
39
|
+
this.ctx.fillStyle = this.options.lineColor!;
|
|
40
40
|
this.ctx.fillText('4', point.x - textHorizontalOffset, point.y + textVerticalOffset);
|
|
41
41
|
});
|
|
42
42
|
}
|
|
@@ -9,7 +9,7 @@ export default class FieldNumberLayer extends InternalCourtLayer {
|
|
|
9
9
|
|
|
10
10
|
const thinSpaceChar = String.fromCharCode(8202);
|
|
11
11
|
|
|
12
|
-
this.ctx.fillStyle = this.options.lineColor
|
|
12
|
+
this.ctx.fillStyle = this.options.lineColor!;
|
|
13
13
|
this.ctx.translate(x, y);
|
|
14
14
|
|
|
15
15
|
for (let i = 1; i < 10; i++) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import InternalCourtLayer from '../../../../../base/InternalCourtLayer';
|
|
2
|
+
import { CourtPoint } from '../../../../../../../types';
|
|
2
3
|
|
|
3
4
|
const ARC_8M_RADIUS = 11.59;
|
|
4
5
|
const ARC_12M_RADIUS = 15.97;
|
|
@@ -140,6 +141,6 @@ export default class CenterLineLayer extends InternalCourtLayer {
|
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
|
|
143
|
-
function getArcPointForAngle(c, radius, angle) {
|
|
144
|
+
function getArcPointForAngle(c: CourtPoint, radius: number, angle: number) {
|
|
144
145
|
return { x: c.x + Math.cos(angle) * radius, y: c.y + Math.sin(angle) * radius };
|
|
145
146
|
}
|
|
@@ -44,7 +44,7 @@ export default class InternalLineLayer extends InternalBaseLayer {
|
|
|
44
44
|
this.drawLineCap();
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
setColor(alphaOverride = null) {
|
|
47
|
+
setColor(alphaOverride: number | null = null) {
|
|
48
48
|
let lineForPlayerInPosition = false;
|
|
49
49
|
if (this.options.position) {
|
|
50
50
|
const { playerPositionOrigin, playerPositionTerminus } = this.line.originalData;
|
|
@@ -135,8 +135,8 @@ export default class InternalLineLayer extends InternalBaseLayer {
|
|
|
135
135
|
const [preLastLine] = linesForPlayerPosition;
|
|
136
136
|
if (preLastLine.lineParts.length) {
|
|
137
137
|
const lastLinePart = [...preLastLine.lineParts].pop();
|
|
138
|
-
const lastLinePartCp = [...lastLinePart
|
|
139
|
-
this.startMaskSettings = { centerPoint: lastLinePartCp
|
|
138
|
+
const lastLinePartCp = [...lastLinePart!.controlPoints].pop();
|
|
139
|
+
this.startMaskSettings = { centerPoint: lastLinePartCp!, radius: 3.0 }; // 1
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -146,11 +146,11 @@ export default class InternalLineLayer extends InternalBaseLayer {
|
|
|
146
146
|
if (this.line.type !== 'SCREEN') {
|
|
147
147
|
let radius = 3.7; // 0.7
|
|
148
148
|
const lastLinePart = [...this.line.getLineParts()].pop();
|
|
149
|
-
const lastLinePartCp = [...lastLinePart
|
|
149
|
+
const lastLinePartCp = [...lastLinePart!.controlPoints].pop();
|
|
150
150
|
if (['PASS', 'HANDOFF'].indexOf(this.line.type) >= 0) {
|
|
151
151
|
radius = 1.75;
|
|
152
152
|
}
|
|
153
|
-
this.endMaskSettings = { centerPoint: lastLinePartCp
|
|
153
|
+
this.endMaskSettings = { centerPoint: lastLinePartCp!, radius: radius };
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
|
|
@@ -2,7 +2,7 @@ import _ from 'lodash';
|
|
|
2
2
|
import { animationProgress } from '../../../helpers/common';
|
|
3
3
|
import ActionLineLayer from '../base/ActionLineLayer';
|
|
4
4
|
import DribbleLineTrait from '../../../traits/DribbleLineTrait';
|
|
5
|
-
import {
|
|
5
|
+
import { LinePart } from '../../../types';
|
|
6
6
|
import { LinePartAdjusted } from '../../../models/LineModel';
|
|
7
7
|
|
|
8
8
|
export default class DribbleLineLayer extends ActionLineLayer {
|
|
@@ -23,11 +23,11 @@ export default class DribbleLineLayer extends ActionLineLayer {
|
|
|
23
23
|
dribbleLineParts.forEach(lp => {
|
|
24
24
|
const { controlPoints, lpIndex } = lp;
|
|
25
25
|
const [firstPoint] = controlPoints;
|
|
26
|
-
let alpha = lineParts[lpIndex].alpha || this.line.color.alpha;
|
|
26
|
+
let alpha = lineParts[lpIndex!].alpha || this.line.color.alpha;
|
|
27
27
|
if (this.options.animationGlobalProgress) {
|
|
28
|
-
const [start, end] = this.line.animationKeyTimeChunks[lpIndex];
|
|
28
|
+
const [start, end] = this.line.animationKeyTimeChunks[lpIndex!];
|
|
29
29
|
if (_.inRange(this.options.animationGlobalProgress, start, end)) {
|
|
30
|
-
if (animationProgress(this.options.animationGlobalProgress, start, end) > firstPoint.time) {
|
|
30
|
+
if (animationProgress(this.options.animationGlobalProgress, start, end) > firstPoint.time!) {
|
|
31
31
|
alpha = 0.1;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -19,7 +19,7 @@ export default class DribbleLineShapeLayer extends InternalLineShapeLayer {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
getProcessedLinePaths() {
|
|
22
|
-
return this.convertLinePartsToDribble([this.shape.linePart]);
|
|
22
|
+
return this.convertLinePartsToDribble([...(this.shape.linePart ? [this.shape.linePart] : [])]);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
package/src/math/Bezier.ts
CHANGED
|
@@ -8,8 +8,8 @@ export default class Bezier {
|
|
|
8
8
|
constructor(
|
|
9
9
|
private a: CourtPoint,
|
|
10
10
|
private b: CourtPoint,
|
|
11
|
-
private c: CourtPoint = null,
|
|
12
|
-
private d: CourtPoint = null
|
|
11
|
+
private c: CourtPoint | null = null,
|
|
12
|
+
private d: CourtPoint | null = null
|
|
13
13
|
) {
|
|
14
14
|
this.len = 100;
|
|
15
15
|
this.arcLengths = new Array(this.len + 1);
|
|
@@ -42,6 +42,7 @@ export default class Bezier {
|
|
|
42
42
|
if (this.a && this.b && !this.c && !this.d) {
|
|
43
43
|
return this.linearBezierEquation(t, 'x');
|
|
44
44
|
}
|
|
45
|
+
throw new Error('invalid input params');
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
y(t: number) {
|
|
@@ -54,6 +55,7 @@ export default class Bezier {
|
|
|
54
55
|
if (this.a && this.b && !this.c && !this.d) {
|
|
55
56
|
return this.linearBezierEquation(t, 'y');
|
|
56
57
|
}
|
|
58
|
+
throw new Error('invalid input params');
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
map(u: number) {
|
|
@@ -94,15 +96,15 @@ export default class Bezier {
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
quadraticBezierEquation(t: number, axis: 'x' | 'y') {
|
|
97
|
-
return (1 - t) * (1 - t) * this.a[axis] + 2 * (1 - t) * t * this.b[axis] + t * t * this.c[axis];
|
|
99
|
+
return (1 - t) * (1 - t) * this.a[axis] + 2 * (1 - t) * t * this.b[axis] + t * t * this.c![axis];
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
cubicBezierEquation(t: number, axis: 'x' | 'y') {
|
|
101
103
|
return (
|
|
102
104
|
(1 - t) * (1 - t) * (1 - t) * this.a[axis] +
|
|
103
105
|
3 * ((1 - t) * (1 - t)) * t * this.b[axis] +
|
|
104
|
-
3 * (1 - t) * (t * t) * this.c[axis] +
|
|
105
|
-
t * t * t * this.d[axis]
|
|
106
|
+
3 * (1 - t) * (t * t) * this.c![axis] +
|
|
107
|
+
t * t * t * this.d![axis]
|
|
106
108
|
);
|
|
107
109
|
}
|
|
108
110
|
}
|
|
@@ -5,26 +5,26 @@ const sinusoidNumberOfPoints = 1000;
|
|
|
5
5
|
|
|
6
6
|
export function adjustedBezierCurveWithExclusionZones(
|
|
7
7
|
controlPoints: CourtPoint[],
|
|
8
|
-
startZoneRadius = null,
|
|
9
|
-
startAnchor = null,
|
|
10
|
-
endZoneRadius = null,
|
|
11
|
-
endAnchor = null
|
|
8
|
+
startZoneRadius: number | null = null,
|
|
9
|
+
startAnchor: CourtPoint | null = null,
|
|
10
|
+
endZoneRadius: number | null = null,
|
|
11
|
+
endAnchor: CourtPoint | null = null
|
|
12
12
|
) {
|
|
13
13
|
let curve = [...controlPoints];
|
|
14
14
|
if (startZoneRadius && startAnchor) {
|
|
15
15
|
const controlPointsMat = controlPointsMatFromPoints(controlPoints);
|
|
16
16
|
const tVal = firstTValForPointOnCurveOutsideCircle(controlPointsMat, startAnchor, startZoneRadius);
|
|
17
17
|
const splitCurves = splitBezierCurveAtTVal(controlPoints, tVal);
|
|
18
|
-
curve = [...splitCurves].pop();
|
|
18
|
+
curve = [...splitCurves!].pop() as CourtPoint[];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
if (endZoneRadius && endAnchor) {
|
|
22
|
-
const reverseControlPoints = [].concat(curve).reverse(); // reverse array without changing the original
|
|
22
|
+
const reverseControlPoints = ([] as CourtPoint[]).concat(curve).reverse(); // reverse array without changing the original
|
|
23
23
|
const reverseControlPointsMat = controlPointsMatFromPoints(reverseControlPoints);
|
|
24
24
|
const reverseTVal = firstTValForPointOnCurveOutsideCircle(reverseControlPointsMat, endAnchor, endZoneRadius);
|
|
25
25
|
const tVal = 1.0 - reverseTVal;
|
|
26
26
|
const splitCurves = splitBezierCurveAtTVal(curve, tVal);
|
|
27
|
-
curve = splitCurves[0];
|
|
27
|
+
curve = splitCurves![0];
|
|
28
28
|
}
|
|
29
29
|
return curve as LinePart['controlPoints'];
|
|
30
30
|
}
|
|
@@ -225,7 +225,7 @@ export function splitBezierCurveAtTVal(controlPoints: CourtPoint[], tVal: number
|
|
|
225
225
|
|
|
226
226
|
function linearPointAtTVal(controlPoints: CourtPoint[], tVal: number) {
|
|
227
227
|
const [firstControlPoint] = controlPoints;
|
|
228
|
-
const lastControlPoint = [...controlPoints].pop();
|
|
228
|
+
const lastControlPoint = [...controlPoints].pop() as CourtPoint;
|
|
229
229
|
const xt = firstControlPoint.x + (lastControlPoint.x - firstControlPoint.x) * tVal;
|
|
230
230
|
const yt = firstControlPoint.y + (lastControlPoint.y - firstControlPoint.y) * tVal;
|
|
231
231
|
return { y: yt, x: xt };
|
|
@@ -233,11 +233,11 @@ function linearPointAtTVal(controlPoints: CourtPoint[], tVal: number) {
|
|
|
233
233
|
|
|
234
234
|
function splitLinearBezierCurveAtTVal(controlPoints: CourtPoint[], tVal: number) {
|
|
235
235
|
// Comment from prev dev:
|
|
236
|
-
// this could be faster by doing calculations directly
|
|
237
|
-
const midPoint = linearPointAtTVal(controlPoints, tVal);
|
|
236
|
+
// this could be faster by doing calculations directly... but who cares
|
|
237
|
+
const midPoint: CourtPoint = linearPointAtTVal(controlPoints, tVal);
|
|
238
238
|
return [
|
|
239
239
|
[controlPoints[0], midPoint],
|
|
240
|
-
[midPoint, [...controlPoints].pop()]
|
|
240
|
+
[midPoint, [...controlPoints].pop() as CourtPoint]
|
|
241
241
|
];
|
|
242
242
|
}
|
|
243
243
|
|
|
@@ -275,12 +275,12 @@ function splitCubicCurveAtTVal(controlPoints: CourtPoint[], tVal: number) {
|
|
|
275
275
|
function firstTValForPointOnCurveOutsideCircle(controlPointsMat: Matrix, anchorPoint: CourtPoint, radius: number) {
|
|
276
276
|
const [numberOfRows] = controlPointsMat.size();
|
|
277
277
|
const helperMat = helperMatrixForDegree(numberOfRows - 1);
|
|
278
|
-
const pointsOnCurveMat = multiply(helperMat
|
|
278
|
+
const pointsOnCurveMat = multiply(helperMat!, controlPointsMat);
|
|
279
279
|
const distances = distancesFromPointToPoints(anchorPoint, pointsOnCurveMat);
|
|
280
280
|
|
|
281
281
|
// Comment from prev dev:
|
|
282
282
|
// We're assuming that point 0 is the closest to the anchor point... So we could use a binary search here to save a bit of time
|
|
283
|
-
// Chances are is that the tVal will actually be near the beginning however due to the way the function
|
|
283
|
+
// Chances are is that the tVal will actually be near the beginning however due to the way the function I used at this time
|
|
284
284
|
|
|
285
285
|
let indexOfPoint = distances.length - 1;
|
|
286
286
|
for (let i = 0; i < distances.length; i++) {
|
|
@@ -298,8 +298,8 @@ function distancesFromPointToPoints(anchorPoint: CourtPoint, pointsOnCurveMat: M
|
|
|
298
298
|
const xPointAdjustFromOrigin = -anchorPoint.x;
|
|
299
299
|
const yPointAdjustFromOrigin = -anchorPoint.y;
|
|
300
300
|
|
|
301
|
-
const adjustedXPoints = [];
|
|
302
|
-
const adjustedYPoints = [];
|
|
301
|
+
const adjustedXPoints: number[] = [];
|
|
302
|
+
const adjustedYPoints: number[] = [];
|
|
303
303
|
|
|
304
304
|
pointsOnCurveMat.forEach((value, [, col]) => {
|
|
305
305
|
switch (col) {
|
|
@@ -12,8 +12,8 @@ export default class AnimationModel {
|
|
|
12
12
|
private running: boolean;
|
|
13
13
|
private framesLoadTime: number[];
|
|
14
14
|
private timeStart: number | null;
|
|
15
|
-
private timeElapsed: number;
|
|
16
|
-
private timeElapsedSaved: number;
|
|
15
|
+
private timeElapsed: number | null;
|
|
16
|
+
private timeElapsedSaved: number | null;
|
|
17
17
|
|
|
18
18
|
constructor(private ctx: CanvasRenderingContext2D, play: PlayModel) {
|
|
19
19
|
this.play = _.cloneDeep(play);
|
|
@@ -35,7 +35,7 @@ export default class AnimationModel {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
get globalProgress() {
|
|
38
|
-
return (this.timeElapsed * 100) / (this.animationDuration * 1000) / 100;
|
|
38
|
+
return ((this.timeElapsed || 0) * 100) / (this.animationDuration * 1000) / 100;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
get currentPlayPhase() {
|
|
@@ -43,7 +43,7 @@ export default class AnimationModel {
|
|
|
43
43
|
return _.inRange(this.globalProgress, interval.min, interval.max);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
return parseInt(phaseInterval ? phaseInterval[0] : Object.keys(this.linesPhaseIntervals).pop());
|
|
46
|
+
return parseInt(phaseInterval ? phaseInterval[0] : Object.keys(this.linesPhaseIntervals).pop()!);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
get linesPhaseIntervals(): Record<number, { min: number; max: number }> {
|
|
@@ -52,7 +52,7 @@ export default class AnimationModel {
|
|
|
52
52
|
if (!result[line.phase]) result[line.phase] = { min: 0, max: 0 };
|
|
53
53
|
line.animations.forEach(({ keyTimes }) => {
|
|
54
54
|
const [from] = keyTimes;
|
|
55
|
-
const to = [...keyTimes].pop()
|
|
55
|
+
const to = [...keyTimes].pop()!;
|
|
56
56
|
if (from < result[line.phase].min || (!result[line.phase].min && line.phase > 1)) result[line.phase].min = from;
|
|
57
57
|
if (to > result[line.phase].max) result[line.phase].max = to;
|
|
58
58
|
});
|
|
@@ -61,9 +61,9 @@ export default class AnimationModel {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
get lastLineAnimationMax() {
|
|
64
|
-
return this.play.playData.lines.reduce((result, line) => {
|
|
64
|
+
return this.play.playData.lines.reduce<number>((result, line) => {
|
|
65
65
|
const lineModel = new LineModel(line);
|
|
66
|
-
return result > lineModel.lastAnimEndTime ? result : lineModel.lastAnimEndTime
|
|
66
|
+
return result > lineModel.lastAnimEndTime! ? result : lineModel.lastAnimEndTime!;
|
|
67
67
|
}, 0);
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -78,7 +78,7 @@ export default class AnimationModel {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
pause() {
|
|
81
|
-
cancelAnimationFrame(this.loopId);
|
|
81
|
+
cancelAnimationFrame(this.loopId!);
|
|
82
82
|
this.running = false;
|
|
83
83
|
this.timeStart = null;
|
|
84
84
|
this.timeElapsedSaved = this.timeElapsed;
|
|
@@ -117,7 +117,7 @@ export default class AnimationModel {
|
|
|
117
117
|
if (this.globalProgress >= this.lastLineAnimationMax) {
|
|
118
118
|
progressCallback(this.lastLineAnimationMax);
|
|
119
119
|
this.reset();
|
|
120
|
-
cancelAnimationFrame(this.loopId);
|
|
120
|
+
cancelAnimationFrame(this.loopId!);
|
|
121
121
|
setTimeout(() => {
|
|
122
122
|
finishCallback();
|
|
123
123
|
}, 1000);
|
|
@@ -128,10 +128,10 @@ export default class AnimationModel {
|
|
|
128
128
|
|
|
129
129
|
if (!this.timeStart) this.timeStart = timestamp;
|
|
130
130
|
|
|
131
|
-
const frameTime = timestamp - this.timeStart + this.timeElapsedSaved - this.timeElapsed;
|
|
131
|
+
const frameTime = timestamp - this.timeStart + (this.timeElapsedSaved || 0) - (this.timeElapsed || 0);
|
|
132
132
|
this.framesLoadTime.push(frameTime);
|
|
133
133
|
|
|
134
|
-
this.timeElapsed = timestamp - this.timeStart + this.timeElapsedSaved;
|
|
134
|
+
this.timeElapsed = timestamp - this.timeStart + (this.timeElapsedSaved || 0);
|
|
135
135
|
|
|
136
136
|
if (debug) {
|
|
137
137
|
console.log(
|
|
@@ -10,11 +10,11 @@ export default class Model<M extends Line | Player | Note | Shape, A extends Lin
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
_getAttrOriginal<K extends keyof M>(attr: K) {
|
|
13
|
-
return
|
|
13
|
+
return this.originalData[attr];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
_getAttr<K extends keyof A>(attr: K) {
|
|
17
|
-
return
|
|
17
|
+
return this.adjustedData[attr];
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
_setAttr<K extends keyof A>(attr: K, value: A[K]) {
|