Compare commits

...

2 Commits

3 changed files with 148 additions and 27 deletions

View File

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

View File

@ -7,6 +7,7 @@ import { LibFigureDance, LibFigureMove, Move } from "./libfigureMapper.js";
import { animateLowLevelMove, LowLevelMove } from "./lowLevelMove.js"; import { animateLowLevelMove, LowLevelMove } from "./lowLevelMove.js";
import { figureBeats, figureToHtml } from "./libfigure/define-figure.js"; import { figureBeats, figureToHtml } from "./libfigure/define-figure.js";
import { labelForBeats } from "./libfigure/dance.js"; import { labelForBeats } from "./libfigure/dance.js";
import { setDistance } from "./rendererConstants.js";
const body = document.querySelector('body')!; const body = document.querySelector('body')!;
@ -41,22 +42,52 @@ beatDisplay.className = 'beatDisplay';
beatDisplay.innerText = '0.0'; beatDisplay.innerText = '0.0';
const ctx = canvas.getContext('2d')!; 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); const r = new renderer.Renderer(canvas, ctx);
r.extraLines = 3;
r.extraSets = 3; const defaultSetSizeInPx = canvas.offsetHeight / 3;
r.clear(); 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'); const bpmSelector = document.createElement('input');
bpmSelector.type = 'number'; bpmSelector.type = 'number';
@ -198,15 +229,36 @@ function buildMovesList() {
} catch (e) { } catch (e) {
figureHtml = "libfigureError: " + e; figureHtml = "libfigureError: " + e;
} }
moveItem.innerHTML = "[" + labelForBeats(Math.floor(startBeat / 16) * 16) + " " const beatsDescription = document.createTextNode(
+ "(" + startBeat + "-" + currentBeat + ")] " "[" + labelForBeats(Math.floor(startBeat / 16) * 16) + " "
+ figureHtml; + "(" + 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'); moveItem.classList.add('move');
for (let beat = startBeat; beat < currentBeat; beat++) { for (let beat = startBeat; beat < currentBeat; beat++) {
moveItem.classList.add('moveForBeat_' + beat); moveItem.classList.add('moveForBeat_' + beat);
} }
if (startBeat === 0) moveItem.classList.add('currentMove'); if (startBeat === 0) moveItem.classList.add('currentMove');
moveItem.addEventListener('click', (ev) => { figureDescription.addEventListener('click', (ev) => {
setBeat(startBeat); setBeat(startBeat);
restartAnimation(false); restartAnimation(false);
}); });
@ -287,10 +339,68 @@ function playAnimation(bpm: number, start: number, end: number) {
anim(); 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. // Default dance is Two Hearts in Time by Isaac Banner. Selected arbitrarily.
const defaultDanceTitle = "Two Hearts in Time"; const defaultDanceTitle = "Two Hearts in Time";
wrapperDiv.appendChild(document.createElement('br'));
const danceList = document.createElement('select'); const danceList = document.createElement('select');
for (const [idx, dance] of danceLibrary.dances.entries()) { for (const [idx, dance] of danceLibrary.dances.entries()) {
const opt = document.createElement('option'); const opt = document.createElement('option');

View File

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