@luceosports/play-rendering 2.0.2 → 2.0.3
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/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
package/src/models/FrameModel.ts
CHANGED
|
@@ -9,7 +9,7 @@ import ShapeLayer from '../layers/ShapeLayer';
|
|
|
9
9
|
import NoteLayer from '../layers/NoteLayer';
|
|
10
10
|
import LineControlPointLayer from '../layers/LineControlPointLayer';
|
|
11
11
|
import ShapeControlPointLayer from '../layers/ShapeControlPointLayer';
|
|
12
|
-
import LineModel from './LineModel';
|
|
12
|
+
import LineModel, { LinePartAdjusted } from './LineModel';
|
|
13
13
|
import PlayerModel from './PlayerModel';
|
|
14
14
|
import NoteModel from './NoteModel';
|
|
15
15
|
import * as ShapeModels from './ShapeModels';
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from '../constants';
|
|
23
23
|
|
|
24
24
|
import PlayModel, { PlayModelOptions, PlayStaticData } from './PlayModel';
|
|
25
|
-
import { Court, CourtPoint, SportType } from '../types';
|
|
25
|
+
import { Court, CourtPoint, ShapeType, SportType } from '../types';
|
|
26
26
|
import ShapeModel from './ShapeModel';
|
|
27
27
|
|
|
28
28
|
export type FrameData = {
|
|
@@ -56,11 +56,12 @@ type InferEntityFromObjectType<T> = T extends 'player'
|
|
|
56
56
|
: LineModel;
|
|
57
57
|
|
|
58
58
|
export default class FrameModel {
|
|
59
|
-
private ctx: CanvasRenderingContext2D;
|
|
59
|
+
private ctx: CanvasRenderingContext2D | null;
|
|
60
60
|
private animationGlobalProgressPrev: number;
|
|
61
61
|
|
|
62
62
|
constructor(private play: PlayModel, private phase = 1, private animationGlobalProgress = 0) {
|
|
63
63
|
this.animationGlobalProgressPrev = animationGlobalProgress;
|
|
64
|
+
this.ctx = null;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
get sport(): SportType {
|
|
@@ -109,9 +110,9 @@ export default class FrameModel {
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
get playAnimationDuration() {
|
|
112
|
-
return this.play.playData.lines.reduce((result, l) => {
|
|
113
|
+
return this.play.playData.lines.reduce<number>((result, l) => {
|
|
113
114
|
const line = new LineModel(l);
|
|
114
|
-
const lineLastAnimEndTime = line.lastAnimEndTime
|
|
115
|
+
const lineLastAnimEndTime = line.lastAnimEndTime!;
|
|
115
116
|
return result > lineLastAnimEndTime ? result : lineLastAnimEndTime;
|
|
116
117
|
}, 0);
|
|
117
118
|
}
|
|
@@ -128,10 +129,10 @@ export default class FrameModel {
|
|
|
128
129
|
if (!this.play.playData.shapes) return [];
|
|
129
130
|
return this.play.playData.shapes
|
|
130
131
|
.filter(s => {
|
|
131
|
-
return ShapeModels[transformShapeTypeToImportKey(s.type)] !== undefined;
|
|
132
|
+
return ShapeModels[transformShapeTypeToImportKey(s.type) as keyof typeof ShapeModels] !== undefined;
|
|
132
133
|
})
|
|
133
134
|
.map(s => {
|
|
134
|
-
return new ShapeModels[transformShapeTypeToImportKey(s.type)](s);
|
|
135
|
+
return new ShapeModels[transformShapeTypeToImportKey(s.type) as keyof typeof ShapeModels](s);
|
|
135
136
|
});
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -203,7 +204,7 @@ export default class FrameModel {
|
|
|
203
204
|
if (!player.animations.length) return player;
|
|
204
205
|
|
|
205
206
|
player.animations.forEach(animation => {
|
|
206
|
-
const keyTimesChunks = [];
|
|
207
|
+
const keyTimesChunks: [number, number][] = [];
|
|
207
208
|
animation.keyTimes.forEach((value, index) => {
|
|
208
209
|
if (index) keyTimesChunks.push([animation.keyTimes[index - 1], value]);
|
|
209
210
|
});
|
|
@@ -222,8 +223,8 @@ export default class FrameModel {
|
|
|
222
223
|
});
|
|
223
224
|
});
|
|
224
225
|
|
|
225
|
-
if (this.animationGlobalProgress >= player.lastAnimEndTime) {
|
|
226
|
-
player.location = player.lastAnimationLastLinePartControlPoint
|
|
226
|
+
if (this.animationGlobalProgress >= player.lastAnimEndTime!) {
|
|
227
|
+
player.location = player.lastAnimationLastLinePartControlPoint!;
|
|
227
228
|
}
|
|
228
229
|
|
|
229
230
|
return player;
|
|
@@ -237,10 +238,10 @@ export default class FrameModel {
|
|
|
237
238
|
const line = new LineModel(l);
|
|
238
239
|
|
|
239
240
|
line.setAnimationKeyTimesChunked(
|
|
240
|
-
this.play.playData.players.find(p => p.position === line.playerPositionOrigin)
|
|
241
|
+
this.play.playData.players.find(p => p.position === line.playerPositionOrigin)!
|
|
241
242
|
);
|
|
242
243
|
|
|
243
|
-
const linePartsAdjusted = [];
|
|
244
|
+
const linePartsAdjusted: LinePartAdjusted[] = [];
|
|
244
245
|
line.animationKeyTimeChunks.forEach(([start, end], index) => {
|
|
245
246
|
if (this.animationGlobalProgressPrev < end && this.animationGlobalProgress >= end) {
|
|
246
247
|
window.dispatchEvent(
|
|
@@ -254,7 +255,7 @@ export default class FrameModel {
|
|
|
254
255
|
const linePathSplitted = splitBezierCurveAtTVal(
|
|
255
256
|
line.getLineParts()[index].controlPoints,
|
|
256
257
|
this.animationProgress(start, end)
|
|
257
|
-
);
|
|
258
|
+
) as LinePartAdjusted['controlPoints'][];
|
|
258
259
|
linePartsAdjusted.push({ controlPoints: linePathSplitted[0], alpha: 0.1 });
|
|
259
260
|
linePartsAdjusted.push({ controlPoints: linePathSplitted[1], alpha: line.color.alpha });
|
|
260
261
|
} else if (this.animationGlobalProgress > end) {
|
|
@@ -265,7 +266,7 @@ export default class FrameModel {
|
|
|
265
266
|
});
|
|
266
267
|
line.setLinePartsAdjusted(linePartsAdjusted);
|
|
267
268
|
|
|
268
|
-
if (this.animationGlobalProgress > line.lastAnimEndTime) {
|
|
269
|
+
if (this.animationGlobalProgress > line.lastAnimEndTime!) {
|
|
269
270
|
line.color = { ...line.color, alpha: 0.1 }; // To adjust line cap opacity
|
|
270
271
|
}
|
|
271
272
|
|
|
@@ -292,7 +293,7 @@ export default class FrameModel {
|
|
|
292
293
|
}
|
|
293
294
|
|
|
294
295
|
get prevAnimationLines() {
|
|
295
|
-
return this.playDataLines.filter(l => this.animationGlobalProgress >= l.lastAnimEndTime);
|
|
296
|
+
return this.playDataLines.filter(l => this.animationGlobalProgress >= l.lastAnimEndTime!);
|
|
296
297
|
}
|
|
297
298
|
|
|
298
299
|
draw() {
|
|
@@ -319,6 +320,8 @@ export default class FrameModel {
|
|
|
319
320
|
}
|
|
320
321
|
|
|
321
322
|
_init() {
|
|
323
|
+
if (!this.ctx) throw new Error('Canvas context is not provided. Please use setContext() method.');
|
|
324
|
+
|
|
322
325
|
this.ctx.canvas.width = this.frameWidth;
|
|
323
326
|
this.ctx.canvas.height = this.frameHeight;
|
|
324
327
|
|
package/src/models/LineModel.ts
CHANGED
|
@@ -48,7 +48,7 @@ export default class LineModel extends Model<LineData, LineAdjusted> {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
setAnimationKeyTimesChunked(linePlayer: Player) {
|
|
51
|
-
let keyTimesChunks = [];
|
|
51
|
+
let keyTimesChunks: [number, number][] = [];
|
|
52
52
|
this.animations[0].keyTimes.forEach((value, index) => {
|
|
53
53
|
if (index) keyTimesChunks.push([this.animations[0].keyTimes[index - 1], value]);
|
|
54
54
|
});
|
|
@@ -142,6 +142,6 @@ export default class LineModel extends Model<LineData, LineAdjusted> {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
get lastLinePartControlPoint() {
|
|
145
|
-
return [...[...this.lineParts].pop()
|
|
145
|
+
return [...[...this.lineParts].pop()!.controlPoints].pop()!;
|
|
146
146
|
}
|
|
147
147
|
}
|
package/src/models/NoteModel.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Model from './Base/InternalFrameModel';
|
|
2
|
-
import { Note as NoteData } from '../types';
|
|
2
|
+
import { CourtPoint, Note as NoteData } from '../types';
|
|
3
3
|
|
|
4
4
|
const NOTE_GREY_COLOR = { red: 0.502, green: 0.502, blue: 0.502, alpha: 0.35 };
|
|
5
5
|
|
|
@@ -84,9 +84,9 @@ export default class NoteModel extends Model<NoteData, NoteData> {
|
|
|
84
84
|
return 'top';
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
get internalCtx() {
|
|
87
|
+
get internalCtx(): CanvasRenderingContext2D {
|
|
88
88
|
// used only for measureText feature to get a text width
|
|
89
|
-
return this.internalCanvas.getContext('2d')
|
|
89
|
+
return this.internalCanvas.getContext('2d')!;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
preProcessedTextForContext() {
|
|
@@ -108,13 +108,13 @@ export default class NoteModel extends Model<NoteData, NoteData> {
|
|
|
108
108
|
const fitWidth = NoteModel.NOTE_WRAP_MAX_WIDTH - NoteModel.NOTE_WRAP_PADDING * 2;
|
|
109
109
|
|
|
110
110
|
const lines = [];
|
|
111
|
-
const words = this.text.split(' ').reduce((result, word) => {
|
|
111
|
+
const words = this.text.split(' ').reduce<string[]>((result, word) => {
|
|
112
112
|
if (!word.match(/\n/)) {
|
|
113
113
|
result.push(word);
|
|
114
114
|
return result;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
const nlSegments = [];
|
|
117
|
+
const nlSegments: string[] = [];
|
|
118
118
|
let curSeg = '';
|
|
119
119
|
for (let i = 0; i < word.length; i++) {
|
|
120
120
|
const char = word[i];
|
|
@@ -164,7 +164,7 @@ export default class NoteModel extends Model<NoteData, NoteData> {
|
|
|
164
164
|
_getLineInfo(inputWords: string[]) {
|
|
165
165
|
let lineWidth = 0;
|
|
166
166
|
let hasLineBreak = false;
|
|
167
|
-
const wordsResult = [];
|
|
167
|
+
const wordsResult: { text: string; width: number }[] = [];
|
|
168
168
|
inputWords.forEach((w, i) => {
|
|
169
169
|
if (w === '\n') {
|
|
170
170
|
hasLineBreak = true;
|
|
@@ -186,7 +186,7 @@ export default class NoteModel extends Model<NoteData, NoteData> {
|
|
|
186
186
|
return { words: wordsResult, width: lineWidth, hasLineBreak };
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
noteWrapperContains({ x, y }) {
|
|
189
|
+
noteWrapperContains({ x, y }: CourtPoint) {
|
|
190
190
|
const { box } = this.preProcessedTextForContext();
|
|
191
191
|
|
|
192
192
|
const topLeft = this.location;
|
package/src/models/PlayModel.ts
CHANGED
|
@@ -58,7 +58,7 @@ export type PlayModelOptions = {
|
|
|
58
58
|
linesSelectedIds: string[];
|
|
59
59
|
shapeSelectedId: string | null;
|
|
60
60
|
noteSelectedId: string | null;
|
|
61
|
-
playersHiddenPositions
|
|
61
|
+
playersHiddenPositions: string[];
|
|
62
62
|
background: string;
|
|
63
63
|
watermark: string | null;
|
|
64
64
|
mirror: boolean;
|
|
@@ -83,13 +83,13 @@ export default class PlayModel {
|
|
|
83
83
|
public static playerHeadshots: PlayerHeadshotItem[] = [];
|
|
84
84
|
public static playerHats: readonly ImageConfigItem[];
|
|
85
85
|
public static shapes: readonly ImageConfigItem[];
|
|
86
|
-
public static watermark: { LuceoSports: HTMLImageElement; TeamLogo: HTMLImageElement };
|
|
86
|
+
public static watermark: { LuceoSports: HTMLImageElement; TeamLogo: HTMLImageElement | null };
|
|
87
87
|
public static backgroundOptions: Record<SportType, HTMLImageElement> & { Hardwood: HTMLImageElement };
|
|
88
88
|
|
|
89
89
|
constructor(data: PlayConstructData, options?: Partial<PlayModelOptions>) {
|
|
90
90
|
this.name = data.name;
|
|
91
91
|
this.options = useDefaults(options);
|
|
92
|
-
this.
|
|
92
|
+
this.playData = data.playData;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
static async init({ teamLogoPath = '' } = {}) {
|
|
@@ -64,7 +64,7 @@ export default class PlayerModel extends Model<PlayerData, PlayerData> {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
get lastAnimationLastLinePartControlPoint() {
|
|
67
|
-
return [...[...this.lastAnimation
|
|
67
|
+
return [...[...this.lastAnimation!.lineParts].pop()!.controlPoints].pop();
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
setPossession(prevPassLines: Line[]) {
|
package/src/models/ShapeModel.ts
CHANGED
|
@@ -8,7 +8,7 @@ const WRAP_POINT_TOP_RIGHT_INDEX = 1;
|
|
|
8
8
|
const WRAP_POINT_BOT_RIGHT_INDEX = 2;
|
|
9
9
|
const WRAP_POINT_BOT_LEFT_INDEX = 3;
|
|
10
10
|
|
|
11
|
-
export default class ShapeModel extends Model<ShapeData, ShapeData> {
|
|
11
|
+
export default abstract class ShapeModel extends Model<ShapeData, ShapeData> {
|
|
12
12
|
get id() {
|
|
13
13
|
return this._getAttr('id');
|
|
14
14
|
}
|
|
@@ -76,10 +76,7 @@ export default class ShapeModel extends Model<ShapeData, ShapeData> {
|
|
|
76
76
|
return +(aDistance * bDistance).toFixed(2);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
get shapeControlPoints()
|
|
80
|
-
// Override this function in a subclass if needed
|
|
81
|
-
return [];
|
|
82
|
-
}
|
|
79
|
+
abstract get shapeControlPoints(): CourtPoint[];
|
|
83
80
|
|
|
84
81
|
rectWrapperContains(point: CourtPoint) {
|
|
85
82
|
// https://stackoverflow.com/questions/17136084/checking-if-a-point-is-inside-a-rotated-rectangle
|
|
@@ -102,7 +99,7 @@ export default class ShapeModel extends Model<ShapeData, ShapeData> {
|
|
|
102
99
|
return trianglesAreaSum <= this.rectWrapAreaTranslated;
|
|
103
100
|
}
|
|
104
101
|
|
|
105
|
-
computeCenterForRect([topLeft, topRight, botRight, botLeft]) {
|
|
102
|
+
computeCenterForRect([topLeft, topRight, botRight, botLeft]: [CourtPoint, CourtPoint, CourtPoint, CourtPoint]) {
|
|
106
103
|
return {
|
|
107
104
|
x: (topLeft.x + botRight.x) / 2,
|
|
108
105
|
y: (topRight.y + botLeft.y) / 2
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import ShapeModel from '../ShapeModel';
|
|
2
2
|
import { rotatePoint } from '../../helpers/common';
|
|
3
3
|
import { bezierBoundingBox } from '../../math/LineDrawingMath';
|
|
4
|
+
import { CourtPoint } from '../../types';
|
|
4
5
|
|
|
5
6
|
export default class LineShape extends ShapeModel {
|
|
6
7
|
get rectWrapPointsPure() {
|
|
7
|
-
const { controlPoints } = this.linePart
|
|
8
|
+
const { controlPoints } = this.linePart!;
|
|
8
9
|
return bezierBoundingBox(controlPoints);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
get controlPointsTranslated() {
|
|
12
|
-
return this.linePart
|
|
13
|
+
return this.linePart!.controlPoints.map(point => {
|
|
13
14
|
const scaled = { x: point.x * this.scale.x, y: point.y * this.scale.y };
|
|
14
15
|
const rotated = rotatePoint(scaled.x, scaled.y, 0, 0, this.angle);
|
|
15
16
|
return { x: rotated.x + this.location.x, y: rotated.y + this.location.y };
|
|
16
17
|
});
|
|
17
18
|
}
|
|
19
|
+
|
|
20
|
+
get shapeControlPoints() {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
18
23
|
}
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import Bezier from '../math/Bezier';
|
|
2
|
-
import { LinePart } from '../types';
|
|
2
|
+
import { CourtPoint, LinePart } from '../types';
|
|
3
3
|
import { CourtPointAdjusted, LinePartAdjusted } from '../models/LineModel';
|
|
4
4
|
|
|
5
|
+
interface InheritedPropsAndMethods {}
|
|
6
|
+
|
|
7
|
+
interface DribbleLineTrait extends InheritedPropsAndMethods {
|
|
8
|
+
convertLinePartsToDribble: (lineParts: LinePart[]) => LinePartAdjusted[];
|
|
9
|
+
drawArrowLineCap: () => void;
|
|
10
|
+
drawPerpendicularLineCap: () => void;
|
|
11
|
+
drawPerpendicularLineAtCourtPoint: (point: CourtPoint, angle: number) => void;
|
|
12
|
+
handOffLineCap: (baseLineParts: LinePart[]) => void;
|
|
13
|
+
calcMidPoint: (point1: CourtPoint, point2: CourtPoint) => CourtPoint;
|
|
14
|
+
angleBetweenLastTwoPoints: () => number;
|
|
15
|
+
angleBetweenTwoPoints: (cpFrom: CourtPoint, cpTo: CourtPoint) => number;
|
|
16
|
+
arrowTipPoint: () => CourtPoint;
|
|
17
|
+
setLineOptions: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
5
20
|
export default {
|
|
6
21
|
convertLinePartsToDribble(lineParts: LinePart[]): LinePartAdjusted[] {
|
|
7
22
|
const processedLineParts: LinePartAdjusted[] = [];
|
|
@@ -56,4 +71,4 @@ export default {
|
|
|
56
71
|
|
|
57
72
|
return processedLineParts;
|
|
58
73
|
}
|
|
59
|
-
};
|
|
74
|
+
} as DribbleLineTrait;
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
import { CourtPoint, LinePart } from '../types';
|
|
2
|
-
import { LinePartAdjusted } from '../models/LineModel';
|
|
2
|
+
import LineModel, { LinePartAdjusted } from '../models/LineModel';
|
|
3
|
+
import ShapeModel from '../models/ShapeModel';
|
|
4
|
+
|
|
5
|
+
interface InheritedPropsAndMethods {
|
|
6
|
+
ctx: CanvasRenderingContext2D;
|
|
7
|
+
getProcessedLinePaths: () => LinePartAdjusted[];
|
|
8
|
+
setColor: (alpha?: number) => void;
|
|
9
|
+
line?: LineModel;
|
|
10
|
+
shape?: ShapeModel;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface LineDrawOperationsTrait extends InheritedPropsAndMethods {
|
|
14
|
+
drawLineFromControlPoints: () => void;
|
|
15
|
+
drawArrowLineCap: () => void;
|
|
16
|
+
drawPerpendicularLineCap: () => void;
|
|
17
|
+
drawPerpendicularLineAtCourtPoint: (point: CourtPoint, angle: number) => void;
|
|
18
|
+
handOffLineCap: (baseLineParts: LinePart[]) => void;
|
|
19
|
+
calcMidPoint: (point1: CourtPoint, point2: CourtPoint) => CourtPoint;
|
|
20
|
+
angleBetweenLastTwoPoints: () => number;
|
|
21
|
+
angleBetweenTwoPoints: (cpFrom: CourtPoint, cpTo: CourtPoint) => number;
|
|
22
|
+
arrowTipPoint: () => CourtPoint;
|
|
23
|
+
setLineOptions: () => void;
|
|
24
|
+
}
|
|
3
25
|
|
|
4
26
|
export default {
|
|
5
27
|
drawLineFromControlPoints() {
|
|
@@ -9,7 +31,7 @@ export default {
|
|
|
9
31
|
|
|
10
32
|
this.ctx.lineJoin = 'round';
|
|
11
33
|
|
|
12
|
-
|
|
34
|
+
this.getProcessedLinePaths().forEach(linePart => {
|
|
13
35
|
this.ctx.save();
|
|
14
36
|
|
|
15
37
|
if (linePart.alpha) {
|
|
@@ -23,7 +45,7 @@ export default {
|
|
|
23
45
|
|
|
24
46
|
if (cp.length === 2) {
|
|
25
47
|
// TODO refactor next line to avoid access to line/shape model
|
|
26
|
-
if ((this.line || this.shape).type === 'DRIBBLE') this.ctx.lineCap = 'round'; // fix last straight line cap segment
|
|
48
|
+
if ((this.line || this.shape || {}).type === 'DRIBBLE') this.ctx.lineCap = 'round'; // fix last straight line cap segment
|
|
27
49
|
this.ctx.lineTo(cp[1].x, cp[1].y);
|
|
28
50
|
}
|
|
29
51
|
if (cp.length === 3) {
|
|
@@ -41,7 +63,9 @@ export default {
|
|
|
41
63
|
this.ctx.restore();
|
|
42
64
|
},
|
|
43
65
|
|
|
44
|
-
drawArrowLineCap(
|
|
66
|
+
drawArrowLineCap(point?: CourtPoint) {
|
|
67
|
+
const arrowTipPoint = point || this.arrowTipPoint();
|
|
68
|
+
|
|
45
69
|
this.ctx.save();
|
|
46
70
|
|
|
47
71
|
const arrowTipLength = 1.4;
|
|
@@ -135,7 +159,7 @@ export default {
|
|
|
135
159
|
|
|
136
160
|
angleBetweenLastTwoPoints() {
|
|
137
161
|
const lastLinePart = [...this.getProcessedLinePaths()].pop();
|
|
138
|
-
const cp = lastLinePart
|
|
162
|
+
const cp = lastLinePart!.controlPoints;
|
|
139
163
|
|
|
140
164
|
const cpTo = cp[cp.length - 1];
|
|
141
165
|
const cpFrom = cp[cp.length - 2];
|
|
@@ -150,7 +174,7 @@ export default {
|
|
|
150
174
|
},
|
|
151
175
|
|
|
152
176
|
arrowTipPoint() {
|
|
153
|
-
const lastLinePart
|
|
177
|
+
const lastLinePart = [...this.getProcessedLinePaths()].pop() as LinePartAdjusted;
|
|
154
178
|
const cp = lastLinePart.controlPoints;
|
|
155
179
|
return cp[cp.length - 1];
|
|
156
180
|
},
|
|
@@ -158,4 +182,4 @@ export default {
|
|
|
158
182
|
setLineOptions() {
|
|
159
183
|
// Override this method in a subclass
|
|
160
184
|
}
|
|
161
|
-
};
|
|
185
|
+
} as LineDrawOperationsTrait;
|