Compare commits

...

2 Commits

3 changed files with 148 additions and 27 deletions

View File

@ -12,6 +12,11 @@
#canvasDiv {
position: relative;
margin: 1.4em;
width: 40%;
height: 90vh;
top: 0;
left: 0;
float: left;
}
.hallLabel {
position: absolute;
@ -31,12 +36,13 @@
.hallLabel.left {
left: -1.3em;
top: 50%;
writing-mode: sideways-lr;
writing-mode: vertical-lr;
transform: rotate(180deg);
}
.hallLabel.right {
right: -1.5em;
top: 50%;
writing-mode: sideways-rl;
writing-mode: vertical-lr;
}
#debug {
display: none;

View File

@ -7,6 +7,7 @@ import { LibFigureDance, LibFigureMove, Move } from "./libfigureMapper.js";
import { animateLowLevelMove, LowLevelMove } from "./lowLevelMove.js";
import { figureBeats, figureToHtml } from "./libfigure/define-figure.js";
import { labelForBeats } from "./libfigure/dance.js";
import { setDistance } from "./rendererConstants.js";
const body = document.querySelector('body')!;
@ -41,22 +42,52 @@ beatDisplay.className = 'beatDisplay';
beatDisplay.innerText = '0.0';
const ctx = canvas.getContext('2d')!;
// TODO Redo this on resize?
const w = canvas.offsetWidth;
const h = canvas.offsetHeight;
const s = window.devicePixelRatio;
canvas.width = w * s;
canvas.height = h * s;
ctx.translate(canvas.width / 2.0, canvas.height / 2.0);
const scale = 10;
ctx.scale(canvas.height / scale, -canvas.height / scale);
const r = new renderer.Renderer(canvas, ctx);
r.extraLines = 3;
r.extraSets = 3;
r.clear();
const defaultSetSizeInPx = canvas.offsetHeight / 3;
type ResetCanvasSetting = { extraSets?: number; extraLines?: number; setSizeInPx?: number; zoom?: number; };
function resetCanvas({ extraSets, extraLines, setSizeInPx, zoom }: ResetCanvasSetting) {
ctx.resetTransform();
extraLines ??= 0;
extraSets ??= 0;
// TODO Redo this on resize?
const numSets = (1 + 2 * extraSets);
setSizeInPx = zoom ? zoom * defaultSetSizeInPx : (setSizeInPx ?? defaultSetSizeInPx);
const w = setSizeInPx * (1 + 2 * extraLines);
const h = setSizeInPx * numSets;
const s = window.devicePixelRatio;
canvasDiv.style.width = w + 'px';
canvasDiv.style.height = h + 'px';
canvas.width = w * s;
canvas.height = h * s;
ctx.translate(canvas.width / 2.0, canvas.height / 2.0);
const scale = numSets * setDistance;
ctx.scale(canvas.height / scale, -canvas.height / scale);
r.extraLines = Math.ceil(extraLines) + 1;
r.extraSets = Math.ceil(extraSets) + 1;
if (r.animation) {
drawAtCurrentBeat();
} else {
r.clear();
}
}
let canvasSetting: ResetCanvasSetting = {
extraLines: 0.6,
extraSets: 0.8,
zoom: 1,
};
function updateCanvasSettings(arg : ResetCanvasSetting) {
if (arg.extraLines !== undefined) canvasSetting.extraLines = arg.extraLines;
if (arg.extraSets !== undefined) canvasSetting.extraSets = arg.extraSets;
if (arg.zoom) canvasSetting.zoom = arg.zoom;
resetCanvas(canvasSetting);
}
updateCanvasSettings({});
const bpmSelector = document.createElement('input');
bpmSelector.type = 'number';
@ -198,15 +229,36 @@ function buildMovesList() {
} catch (e) {
figureHtml = "libfigureError: " + e;
}
moveItem.innerHTML = "[" + labelForBeats(Math.floor(startBeat / 16) * 16) + " "
+ "(" + startBeat + "-" + currentBeat + ")] "
+ figureHtml;
const beatsDescription = document.createTextNode(
"[" + labelForBeats(Math.floor(startBeat / 16) * 16) + " "
+ "(" + startBeat + "-" + currentBeat + ")] ");
const figureDescription = document.createElement('span');
figureDescription.innerHTML = figureHtml;
for (const { label, beat } of [
{ label: '<|', beat: startBeat },
{ label: '+1', beat: startBeat + 1 },
{ label: '½', beat: (startBeat + currentBeat) / 2 },
{ label: '-1', beat: currentBeat - 1 },
{ label: '|>', beat: currentBeat },
]) {
const jumpToButton = document.createElement('button');
jumpToButton.innerText = label;
jumpToButton.addEventListener('click', (ev) => {
setBeat(beat);
restartAnimation(false);
});
moveItem.appendChild(jumpToButton);
}
moveItem.appendChild(beatsDescription);
moveItem.appendChild(figureDescription);
moveItem.classList.add('move');
for (let beat = startBeat; beat < currentBeat; beat++) {
moveItem.classList.add('moveForBeat_' + beat);
}
if (startBeat === 0) moveItem.classList.add('currentMove');
moveItem.addEventListener('click', (ev) => {
figureDescription.addEventListener('click', (ev) => {
setBeat(startBeat);
restartAnimation(false);
});
@ -287,10 +339,68 @@ function playAnimation(bpm: number, start: number, end: number) {
anim();
}
const displaySettingsDiv = document.createElement('div');
displaySettingsDiv.id = 'displaySettings';
displaySettingsDiv.style.margin = '1em';
const zoomSelector = document.createElement('input');
zoomSelector.type = 'number';
zoomSelector.min = '10';
zoomSelector.step = '10';
zoomSelector.value = '100';
zoomSelector.id = 'zoom';
zoomSelector.style.width = '4em';
zoomSelector.addEventListener('input', (ev) => {
updateCanvasSettings({ zoom: zoomSelector.valueAsNumber / 100 });
})
const zoomLabel = document.createElement('label');
zoomLabel.innerText = '% zoom';
zoomLabel.htmlFor = 'zoom';
displaySettingsDiv.appendChild(zoomSelector);
displaySettingsDiv.appendChild(zoomLabel);
const extraSetsSelector = document.createElement('input');
extraSetsSelector.type = 'number';
extraSetsSelector.min = '0';
extraSetsSelector.step = '0.1';
extraSetsSelector.value = canvasSetting.extraSets!.toPrecision(1);
extraSetsSelector.id = 'extraSets';
extraSetsSelector.style.width = '3em';
extraSetsSelector.addEventListener('input', (ev) => {
updateCanvasSettings({ extraSets: extraSetsSelector.valueAsNumber });
})
const extraSetsLabel = document.createElement('label');
extraSetsLabel.innerText = '# extra sets: ';
extraSetsLabel.htmlFor = 'extraSets';
displaySettingsDiv.appendChild(document.createElement('br'));
displaySettingsDiv.appendChild(extraSetsLabel);
displaySettingsDiv.appendChild(extraSetsSelector);
const extraLinesSelector = document.createElement('input');
extraLinesSelector.type = 'number';
extraLinesSelector.min = '0';
extraLinesSelector.step = '0.1';
extraLinesSelector.value = canvasSetting.extraLines!.toPrecision(1);
extraLinesSelector.id = 'extraLines';
extraLinesSelector.style.width = '3em';
extraLinesSelector.addEventListener('input', (ev) => {
updateCanvasSettings({ extraLines: extraLinesSelector.valueAsNumber });
})
const extraLinesLabel = document.createElement('label');
extraLinesLabel.innerText = '# extra lines: ';
extraLinesLabel.htmlFor = 'extraLines';
displaySettingsDiv.appendChild(document.createElement('br'));
displaySettingsDiv.appendChild(extraLinesLabel);
displaySettingsDiv.appendChild(extraLinesSelector);
wrapperDiv.appendChild(displaySettingsDiv);
// Default dance is Two Hearts in Time by Isaac Banner. Selected arbitrarily.
const defaultDanceTitle = "Two Hearts in Time";
wrapperDiv.appendChild(document.createElement('br'));
const danceList = document.createElement('select');
for (const [idx, dance] of danceLibrary.dances.entries()) {
const opt = document.createElement('option');

View File

@ -24,7 +24,8 @@ function hueForDancer(identity: DancerIdentity): number {
function colorForDancer(identity: ExtendedDancerIdentity) : string {
const hue = hueForDancer(identity.setIdentity);
const sat = 100 - Math.abs(identity.relativeLine * 40);
const lum = 50 + identity.relativeSet * 20;
const unclampedLum = 50 + identity.relativeSet * 20;
const lum = unclampedLum < 10 ? 10 : unclampedLum > 90 ? 90 : unclampedLum;
return `hsl(${hue}, ${sat}%, ${lum}%)`;
}
@ -40,14 +41,13 @@ function colorForDancerLabel(identity: ExtendedDancerIdentity) : string {
export class Renderer {
canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
animation: Animation;
animation?: Animation;
extraSets?: number;
extraLines?: number;
constructor(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
this.canvas = canvas;
this.ctx = ctx;
this.animation = exampleAnimations.longLinesForwardAndBack;
}
drawDancerBody(identity: ExtendedDancerIdentity) {
@ -134,10 +134,14 @@ export class Renderer {
}
drawSetsAtBeat(beat: number) {
if (!this.animation) throw new Error("Attempted to render before setting animation.");
this.drawSets(this.animation.positionsAtBeat(beat));
}
drawSetsWithTrails(beat: number, progression?: number) {
if (!this.animation) throw new Error("Attempted to render before setting animation.");
this.clear();
const increments = 10;
const trailLengthInBeats = 1;
@ -174,7 +178,8 @@ export class Renderer {
clear() {
// TODO Right way to clear?
this.ctx.clearRect(-this.canvas.width, -this.canvas.height, 2*this.canvas.width, 2*this.canvas.height);
this.ctx.fillStyle = 'white';
this.ctx.fillRect(-this.canvas.width, -this.canvas.height, 2*this.canvas.width, 2*this.canvas.height);
const extraLines = this.extraLines ?? 0;
const extraSets = this.extraSets ?? 0;
@ -183,7 +188,7 @@ export class Renderer {
this.ctx.save();
const hue = (relativeLine + relativeSet) % 2 === 0 ? 60 : 170;
const sat = 100 - Math.abs(relativeLine * 40);
const lum = 90 + Math.abs(relativeSet) * 5;
const lum = Math.min(98, 90 + Math.abs(relativeSet) * 5);
this.ctx.fillStyle = `hsl(${hue}, ${sat}%, ${lum}%)`;
this.ctx.translate(relativeLine * lineDistance,
relativeSet * setDistance);