Compare commits
2 Commits
b6f2451920
...
7c82d8f542
Author | SHA1 | Date | |
---|---|---|---|
7c82d8f542 | |||
e1cc9da24c |
|
@ -43,12 +43,15 @@ export abstract class AnimationSegment {
|
||||||
return interpolateLinear(progress, this.startPosition.rotation, this.endPosition.rotation);
|
return interpolateLinear(progress, this.startPosition.rotation, this.endPosition.rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract drawDebug(ctx: CanvasRenderingContext2D, progress: number);
|
||||||
|
|
||||||
positionAtFraction(progress: number) : DancerSetPosition {
|
positionAtFraction(progress: number) : DancerSetPosition {
|
||||||
return {
|
return {
|
||||||
position: this.interpolateOffset(progress),
|
position: this.interpolateOffset(progress),
|
||||||
rotation: this.interpolateRotation(progress),
|
rotation: this.interpolateRotation(progress),
|
||||||
leftArmEnd: this.interpolateHandOffset(progress, Hand.Left),
|
leftArmEnd: this.interpolateHandOffset(progress, Hand.Left),
|
||||||
rightArmEnd: this.interpolateHandOffset(progress, Hand.Right),
|
rightArmEnd: this.interpolateHandOffset(progress, Hand.Right),
|
||||||
|
drawDebug: (ctx) => this.drawDebug(ctx, progress),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +87,13 @@ export class LinearAnimationSegment extends AnimationSegment {
|
||||||
override interpolateOffset(progress: number): Offset {
|
override interpolateOffset(progress: number): Offset {
|
||||||
return interpolateLinearOffset(progress, this.startPosition.position, this.endPosition.position);
|
return interpolateLinearOffset(progress, this.startPosition.position, this.endPosition.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override drawDebug(ctx: CanvasRenderingContext2D, progress: number) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(this.startPosition.position.x, this.startPosition.position.y);
|
||||||
|
ctx.lineTo(this.endPosition.position.x, this.endPosition.position.y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AnimationTransitionFlags {
|
export interface AnimationTransitionFlags {
|
||||||
|
@ -206,6 +216,11 @@ export class TransitionAnimationSegment extends AnimationSegment {
|
||||||
|
|
||||||
return actualHandOffset;
|
return actualHandOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override drawDebug(ctx: CanvasRenderingContext2D, progress: number) {
|
||||||
|
// TODO display transition somehow?
|
||||||
|
this.actualAnimation.drawDebug(ctx, progress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Not sure this really belongs here instead of on some Semantic* type.
|
// TODO Not sure this really belongs here instead of on some Semantic* type.
|
||||||
|
@ -359,6 +374,25 @@ export class RotationAnimationSegment extends AnimationSegment {
|
||||||
return hand === Hand.Left ? leftShoulder : rightShoulder;
|
return hand === Hand.Left ? leftShoulder : rightShoulder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override drawDebug(ctx: CanvasRenderingContext2D, progress: number) {
|
||||||
|
ctx.beginPath();
|
||||||
|
const distance = this.closer?.middleDistance ?? this.endDistance;
|
||||||
|
let start: number, end: number;
|
||||||
|
if (Math.abs(this.endRotation - this.startRotation) >= 360) {
|
||||||
|
start = 0;
|
||||||
|
end = 2 * Math.PI;
|
||||||
|
} else {
|
||||||
|
start = degreesToRadians(this.startRotation);
|
||||||
|
end = degreesToRadians(this.endRotation);
|
||||||
|
}
|
||||||
|
ctx.ellipse(this.center.x, this.center.y,
|
||||||
|
this.xRadius * distance, this.yRadius * distance,
|
||||||
|
0,
|
||||||
|
start, end,
|
||||||
|
this.endRotation < this.startRotation);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For "PassBy" and related moves where stepping straight forward would walk into the
|
// For "PassBy" and related moves where stepping straight forward would walk into the
|
||||||
|
@ -372,6 +406,7 @@ export class StepWideLinearAnimationSegment extends AnimationSegment {
|
||||||
private readonly actualCenter: Offset;
|
private readonly actualCenter: Offset;
|
||||||
private readonly hands: Map<Hand, HandAnimation>;
|
private readonly hands: Map<Hand, HandAnimation>;
|
||||||
private readonly facing: "Start" | "Forward";
|
private readonly facing: "Start" | "Forward";
|
||||||
|
private readonly midPoint: Offset;
|
||||||
|
|
||||||
constructor({ beats, startPosition, endPosition, distanceAtMidpoint, otherPath, hands, facing }: {
|
constructor({ beats, startPosition, endPosition, distanceAtMidpoint, otherPath, hands, facing }: {
|
||||||
beats: number;
|
beats: number;
|
||||||
|
@ -450,6 +485,8 @@ t = -b/2a
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handTransitionProgress = 0.5 / beats;
|
this.handTransitionProgress = 0.5 / beats;
|
||||||
|
|
||||||
|
this.midPoint = this.interpolateOffset(this.progressCenter ?? 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
interpolateOffset(progress: number): Offset {
|
interpolateOffset(progress: number): Offset {
|
||||||
|
@ -512,6 +549,14 @@ t = -b/2a
|
||||||
return hand.shoulderPosition();
|
return hand.shoulderPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override drawDebug(ctx: CanvasRenderingContext2D, progress: number) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(this.startPosition.position.x, this.startPosition.position.y);
|
||||||
|
ctx.lineTo(this.midPoint.x, this.midPoint.y);
|
||||||
|
ctx.lineTo(this.endPosition.position.x, this.endPosition.position.y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SlideAnimationSegment extends AnimationSegment {
|
export class SlideAnimationSegment extends AnimationSegment {
|
||||||
|
@ -558,6 +603,15 @@ export class SlideAnimationSegment extends AnimationSegment {
|
||||||
const { segment, segmentProgress } = this.selectSegment(progress);
|
const { segment, segmentProgress } = this.selectSegment(progress);
|
||||||
return segment.interpolateRotation(segmentProgress);
|
return segment.interpolateRotation(segmentProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override drawDebug(ctx: CanvasRenderingContext2D, progress: number) {
|
||||||
|
// TODO Right way to handle the slide part?
|
||||||
|
const slide = OffsetTimes(this.slideAmount, progress);
|
||||||
|
ctx.translate(slide.x, slide.y);
|
||||||
|
|
||||||
|
const { segment, segmentProgress } = this.selectSegment(progress);
|
||||||
|
segment.drawDebug(ctx, segmentProgress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Animation {
|
export class Animation {
|
||||||
|
@ -633,13 +687,17 @@ export class Animation {
|
||||||
for (const [id, animationSegments] of this.segments.entries()) {
|
for (const [id, animationSegments] of this.segments.entries()) {
|
||||||
const basePosition = positionAtBeat(animationSegments, beat);
|
const basePosition = positionAtBeat(animationSegments, beat);
|
||||||
if (progression !== 0) {
|
if (progression !== 0) {
|
||||||
|
const progressionOffset = OffsetTimes(
|
||||||
|
this.progression,
|
||||||
|
progression * (id.coupleRole === CoupleRole.Ones ? 1 : -1));
|
||||||
res.set(id, {
|
res.set(id, {
|
||||||
...basePosition,
|
...basePosition,
|
||||||
position: OffsetPlus(
|
position: OffsetPlus(
|
||||||
basePosition.position,
|
basePosition.position,
|
||||||
OffsetTimes(
|
progressionOffset),
|
||||||
this.progression,
|
drawDebug: basePosition.drawDebug ?
|
||||||
progression * (id.coupleRole === CoupleRole.Ones ? 1 : -1)))
|
(ctx) => { ctx.translate(progressionOffset.x, progressionOffset.y); basePosition.drawDebug!(ctx); }
|
||||||
|
: undefined
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.set(id, basePosition);
|
res.set(id, basePosition);
|
||||||
|
|
|
@ -1157,7 +1157,7 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
|
||||||
beats: turnBeats,
|
beats: turnBeats,
|
||||||
endPosition: {
|
endPosition: {
|
||||||
kind: PositionKind.Circle,
|
kind: PositionKind.Circle,
|
||||||
which: endWhich.swapUpAndDown(),
|
which: endWhich,
|
||||||
facing: endWhich.facingAcross(),
|
facing: endWhich.facingAcross(),
|
||||||
hands: prevEnd.hands,
|
hands: prevEnd.hands,
|
||||||
setOffset: prevEnd.setOffset,
|
setOffset: prevEnd.setOffset,
|
||||||
|
@ -1183,7 +1183,7 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
|
||||||
endPosition: {
|
endPosition: {
|
||||||
...startingPos,
|
...startingPos,
|
||||||
// TODO Does CourtesyTurn always end in same position?
|
// TODO Does CourtesyTurn always end in same position?
|
||||||
which: startPos.which.swapUpAndDown(),
|
which: startPos.which,
|
||||||
facing: startPos.which.facingAcross(),
|
facing: startPos.which.facingAcross(),
|
||||||
},
|
},
|
||||||
movementPattern: {
|
movementPattern: {
|
||||||
|
|
|
@ -89,6 +89,13 @@ function updateCanvasSettings(arg : ResetCanvasSetting) {
|
||||||
|
|
||||||
updateCanvasSettings({});
|
updateCanvasSettings({});
|
||||||
|
|
||||||
|
const debugRender = document.createElement('input');
|
||||||
|
debugRender.type = 'checkbox';
|
||||||
|
debugRender.checked = r.drawDebug;
|
||||||
|
const debugRenderLabel = document.createElement('label');
|
||||||
|
debugRenderLabel.htmlFor = debugRender.id = 'debugRender';
|
||||||
|
debugRenderLabel.innerText = 'Debug display';
|
||||||
|
|
||||||
const bpmSelector = document.createElement('input');
|
const bpmSelector = document.createElement('input');
|
||||||
bpmSelector.type = 'number';
|
bpmSelector.type = 'number';
|
||||||
bpmSelector.value = '180';
|
bpmSelector.value = '180';
|
||||||
|
@ -147,6 +154,12 @@ function drawAtCurrentBeat() {
|
||||||
.classList.add('currentMove');
|
.classList.add('currentMove');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugRender.addEventListener('change', (ev) => {
|
||||||
|
r.drawDebug = debugRender.checked;
|
||||||
|
drawAtCurrentBeat();
|
||||||
|
restartAnimation(false);
|
||||||
|
})
|
||||||
|
|
||||||
beatSlider.addEventListener('input', (ev) => {
|
beatSlider.addEventListener('input', (ev) => {
|
||||||
drawAtCurrentBeat();
|
drawAtCurrentBeat();
|
||||||
beatDisplay.innerText = beatSlider.valueAsNumber.toFixed(1);
|
beatDisplay.innerText = beatSlider.valueAsNumber.toFixed(1);
|
||||||
|
@ -400,6 +413,10 @@ displaySettingsDiv.appendChild(document.createElement('br'));
|
||||||
displaySettingsDiv.appendChild(extraLinesLabel);
|
displaySettingsDiv.appendChild(extraLinesLabel);
|
||||||
displaySettingsDiv.appendChild(extraLinesSelector);
|
displaySettingsDiv.appendChild(extraLinesSelector);
|
||||||
|
|
||||||
|
displaySettingsDiv.appendChild(document.createElement('br'));
|
||||||
|
displaySettingsDiv.appendChild(debugRender);
|
||||||
|
displaySettingsDiv.appendChild(debugRenderLabel);
|
||||||
|
|
||||||
wrapperDiv.appendChild(displaySettingsDiv);
|
wrapperDiv.appendChild(displaySettingsDiv);
|
||||||
|
|
||||||
// Default dance is Two Hearts in Time by Isaac Banner. Selected arbitrarily.
|
// Default dance is Two Hearts in Time by Isaac Banner. Selected arbitrarily.
|
||||||
|
|
|
@ -44,6 +44,7 @@ export class Renderer {
|
||||||
animation?: Animation;
|
animation?: Animation;
|
||||||
extraSets?: number;
|
extraSets?: number;
|
||||||
extraLines?: number;
|
extraLines?: number;
|
||||||
|
drawDebug: boolean = false;
|
||||||
|
|
||||||
constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
|
constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
|
@ -83,7 +84,7 @@ export class Renderer {
|
||||||
this.ctx.fillStyle = backupFillStyle;
|
this.ctx.fillStyle = backupFillStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawDancer(position: DancerSetPosition, identity: ExtendedDancerIdentity, offsetSets: number) {
|
drawDancer(position: DancerSetPosition, identity: ExtendedDancerIdentity, offsetSets: number, drawDebug: boolean) {
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
|
|
||||||
this.ctx.translate(identity.relativeLine * lineDistance,
|
this.ctx.translate(identity.relativeLine * lineDistance,
|
||||||
|
@ -94,6 +95,14 @@ export class Renderer {
|
||||||
};
|
};
|
||||||
this.ctx.fillStyle = this.ctx.strokeStyle = colorForDancer(realIdentity);
|
this.ctx.fillStyle = this.ctx.strokeStyle = colorForDancer(realIdentity);
|
||||||
|
|
||||||
|
if (drawDebug) {
|
||||||
|
if (this.drawDebug && position.drawDebug) {
|
||||||
|
this.ctx.save();
|
||||||
|
this.ctx.lineWidth = 0.05;
|
||||||
|
position.drawDebug(this.ctx);
|
||||||
|
this.ctx.restore();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
this.ctx.translate(position.position.x, position.position.y);
|
this.ctx.translate(position.position.x, position.position.y);
|
||||||
this.ctx.rotate(-degreesToRadians(position.rotation));
|
this.ctx.rotate(-degreesToRadians(position.rotation));
|
||||||
|
|
||||||
|
@ -113,22 +122,23 @@ export class Renderer {
|
||||||
this.ctx.lineTo(position.rightArmEnd.x, position.rightArmEnd.y);
|
this.ctx.lineTo(position.rightArmEnd.x, position.rightArmEnd.y);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
drawSet(positions: DancersSetPositions, relativeSet: number, relativeLine: number, offsetSets: number) {
|
drawSet(positions: DancersSetPositions, relativeSet: number, relativeLine: number, offsetSets: number, drawDebug: boolean) {
|
||||||
for (const entry of positions.entries()) {
|
for (const entry of positions.entries()) {
|
||||||
this.drawDancer(entry[1], { setIdentity: entry[0], relativeLine, relativeSet }, offsetSets);
|
this.drawDancer(entry[1], { setIdentity: entry[0], relativeLine, relativeSet }, offsetSets, drawDebug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawSets(positions: DancersSetPositions, offsetSets?: number) {
|
drawSets(positions: DancersSetPositions, offsetSets?: number, drawDebug?: boolean) {
|
||||||
const extraSets = this.extraSets ?? 0;
|
const extraSets = this.extraSets ?? 0;
|
||||||
const extraLines = this.extraLines ?? 0;
|
const extraLines = this.extraLines ?? 0;
|
||||||
for (var relativeLine = -extraLines; relativeLine <= extraLines; relativeLine++) {
|
for (var relativeLine = -extraLines; relativeLine <= extraLines; relativeLine++) {
|
||||||
for (var relativeSet = -extraSets; relativeSet <= extraSets; relativeSet++) {
|
for (var relativeSet = -extraSets; relativeSet <= extraSets; relativeSet++) {
|
||||||
this.drawSet(positions, relativeSet, relativeLine, offsetSets ?? 0);
|
this.drawSet(positions, relativeSet, relativeLine, offsetSets ?? 0, !!drawDebug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +163,9 @@ export class Renderer {
|
||||||
for (var i = increments; i >= 0; i--) {
|
for (var i = increments; i >= 0; i--) {
|
||||||
const beatToDraw = beat - i*incrementLength;
|
const beatToDraw = beat - i*incrementLength;
|
||||||
this.ctx.globalAlpha = i == 0 ? 1 : (1 - i/increments)*0.3;
|
this.ctx.globalAlpha = i == 0 ? 1 : (1 - i/increments)*0.3;
|
||||||
this.drawSets(this.animation.positionsAtBeat(beatToDraw, progression % 2), offsetSets);
|
const positions = this.animation.positionsAtBeat(beatToDraw, progression % 2);
|
||||||
|
if (this.drawDebug) this.drawSets(positions, offsetSets, true);
|
||||||
|
this.drawSets(positions, offsetSets, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ export interface DancerSetPosition {
|
||||||
rotation: number;
|
rotation: number;
|
||||||
leftArmEnd?: Offset;
|
leftArmEnd?: Offset;
|
||||||
rightArmEnd?: Offset;
|
rightArmEnd?: Offset;
|
||||||
|
drawDebug?: (CanvasRenderingContext2D) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DancersSetPositions = Map<DancerIdentity, DancerSetPosition>;
|
export type DancersSetPositions = Map<DancerIdentity, DancerSetPosition>;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user