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);
|
||||
}
|
||||
|
||||
abstract drawDebug(ctx: CanvasRenderingContext2D, progress: number);
|
||||
|
||||
positionAtFraction(progress: number) : DancerSetPosition {
|
||||
return {
|
||||
position: this.interpolateOffset(progress),
|
||||
rotation: this.interpolateRotation(progress),
|
||||
leftArmEnd: this.interpolateHandOffset(progress, Hand.Left),
|
||||
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 {
|
||||
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 {
|
||||
|
@ -206,6 +216,11 @@ export class TransitionAnimationSegment extends AnimationSegment {
|
|||
|
||||
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.
|
||||
|
@ -359,6 +374,25 @@ export class RotationAnimationSegment extends AnimationSegment {
|
|||
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
|
||||
|
@ -372,6 +406,7 @@ export class StepWideLinearAnimationSegment extends AnimationSegment {
|
|||
private readonly actualCenter: Offset;
|
||||
private readonly hands: Map<Hand, HandAnimation>;
|
||||
private readonly facing: "Start" | "Forward";
|
||||
private readonly midPoint: Offset;
|
||||
|
||||
constructor({ beats, startPosition, endPosition, distanceAtMidpoint, otherPath, hands, facing }: {
|
||||
beats: number;
|
||||
|
@ -450,6 +485,8 @@ t = -b/2a
|
|||
}
|
||||
|
||||
this.handTransitionProgress = 0.5 / beats;
|
||||
|
||||
this.midPoint = this.interpolateOffset(this.progressCenter ?? 0.5);
|
||||
}
|
||||
|
||||
interpolateOffset(progress: number): Offset {
|
||||
|
@ -512,6 +549,14 @@ t = -b/2a
|
|||
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 {
|
||||
|
@ -558,6 +603,15 @@ export class SlideAnimationSegment extends AnimationSegment {
|
|||
const { segment, segmentProgress } = this.selectSegment(progress);
|
||||
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 {
|
||||
|
@ -633,13 +687,17 @@ export class Animation {
|
|||
for (const [id, animationSegments] of this.segments.entries()) {
|
||||
const basePosition = positionAtBeat(animationSegments, beat);
|
||||
if (progression !== 0) {
|
||||
const progressionOffset = OffsetTimes(
|
||||
this.progression,
|
||||
progression * (id.coupleRole === CoupleRole.Ones ? 1 : -1));
|
||||
res.set(id, {
|
||||
...basePosition,
|
||||
position: OffsetPlus(
|
||||
basePosition.position,
|
||||
OffsetTimes(
|
||||
this.progression,
|
||||
progression * (id.coupleRole === CoupleRole.Ones ? 1 : -1)))
|
||||
progressionOffset),
|
||||
drawDebug: basePosition.drawDebug ?
|
||||
(ctx) => { ctx.translate(progressionOffset.x, progressionOffset.y); basePosition.drawDebug!(ctx); }
|
||||
: undefined
|
||||
});
|
||||
} else {
|
||||
res.set(id, basePosition);
|
||||
|
|
|
@ -1157,7 +1157,7 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
|
|||
beats: turnBeats,
|
||||
endPosition: {
|
||||
kind: PositionKind.Circle,
|
||||
which: endWhich.swapUpAndDown(),
|
||||
which: endWhich,
|
||||
facing: endWhich.facingAcross(),
|
||||
hands: prevEnd.hands,
|
||||
setOffset: prevEnd.setOffset,
|
||||
|
@ -1183,7 +1183,7 @@ function moveAsLowLevelMoves({ move, nextMove, startingPos, numProgessions }: {
|
|||
endPosition: {
|
||||
...startingPos,
|
||||
// TODO Does CourtesyTurn always end in same position?
|
||||
which: startPos.which.swapUpAndDown(),
|
||||
which: startPos.which,
|
||||
facing: startPos.which.facingAcross(),
|
||||
},
|
||||
movementPattern: {
|
||||
|
|
|
@ -89,6 +89,13 @@ function updateCanvasSettings(arg : ResetCanvasSetting) {
|
|||
|
||||
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');
|
||||
bpmSelector.type = 'number';
|
||||
bpmSelector.value = '180';
|
||||
|
@ -147,6 +154,12 @@ function drawAtCurrentBeat() {
|
|||
.classList.add('currentMove');
|
||||
}
|
||||
|
||||
debugRender.addEventListener('change', (ev) => {
|
||||
r.drawDebug = debugRender.checked;
|
||||
drawAtCurrentBeat();
|
||||
restartAnimation(false);
|
||||
})
|
||||
|
||||
beatSlider.addEventListener('input', (ev) => {
|
||||
drawAtCurrentBeat();
|
||||
beatDisplay.innerText = beatSlider.valueAsNumber.toFixed(1);
|
||||
|
@ -400,6 +413,10 @@ displaySettingsDiv.appendChild(document.createElement('br'));
|
|||
displaySettingsDiv.appendChild(extraLinesLabel);
|
||||
displaySettingsDiv.appendChild(extraLinesSelector);
|
||||
|
||||
displaySettingsDiv.appendChild(document.createElement('br'));
|
||||
displaySettingsDiv.appendChild(debugRender);
|
||||
displaySettingsDiv.appendChild(debugRenderLabel);
|
||||
|
||||
wrapperDiv.appendChild(displaySettingsDiv);
|
||||
|
||||
// Default dance is Two Hearts in Time by Isaac Banner. Selected arbitrarily.
|
||||
|
|
|
@ -44,6 +44,7 @@ export class Renderer {
|
|||
animation?: Animation;
|
||||
extraSets?: number;
|
||||
extraLines?: number;
|
||||
drawDebug: boolean = false;
|
||||
|
||||
constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
|
||||
this.canvas = canvas;
|
||||
|
@ -83,7 +84,7 @@ export class Renderer {
|
|||
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.translate(identity.relativeLine * lineDistance,
|
||||
|
@ -94,41 +95,50 @@ export class Renderer {
|
|||
};
|
||||
this.ctx.fillStyle = this.ctx.strokeStyle = colorForDancer(realIdentity);
|
||||
|
||||
this.ctx.translate(position.position.x, position.position.y);
|
||||
this.ctx.rotate(-degreesToRadians(position.rotation));
|
||||
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.rotate(-degreesToRadians(position.rotation));
|
||||
|
||||
this.drawDancerBody(realIdentity);
|
||||
this.drawDancerBody(realIdentity);
|
||||
|
||||
// Draw arms.
|
||||
this.ctx.lineWidth = 0.03;
|
||||
if (position.leftArmEnd) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(leftShoulder.x, leftShoulder.y);
|
||||
this.ctx.lineTo(position.leftArmEnd.x, position.leftArmEnd.y);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
if (position.rightArmEnd) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(rightShoulder.x, rightShoulder.y);
|
||||
this.ctx.lineTo(position.rightArmEnd.x, position.rightArmEnd.y);
|
||||
this.ctx.stroke();
|
||||
// Draw arms.
|
||||
this.ctx.lineWidth = 0.03;
|
||||
if (position.leftArmEnd) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(leftShoulder.x, leftShoulder.y);
|
||||
this.ctx.lineTo(position.leftArmEnd.x, position.leftArmEnd.y);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
if (position.rightArmEnd) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(rightShoulder.x, rightShoulder.y);
|
||||
this.ctx.lineTo(position.rightArmEnd.x, position.rightArmEnd.y);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
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 extraLines = this.extraLines ?? 0;
|
||||
for (var relativeLine = -extraLines; relativeLine <= extraLines; relativeLine++) {
|
||||
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--) {
|
||||
const beatToDraw = beat - i*incrementLength;
|
||||
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;
|
||||
leftArmEnd?: Offset;
|
||||
rightArmEnd?: Offset;
|
||||
drawDebug?: (CanvasRenderingContext2D) => void;
|
||||
}
|
||||
|
||||
export type DancersSetPositions = Map<DancerIdentity, DancerSetPosition>;
|
||||
|
|
Loading…
Reference in New Issue
Block a user