Compare commits
2 Commits
f1f13bf1fd
...
16f374d927
Author | SHA1 | Date | |
---|---|---|---|
16f374d927 | |||
4481d5f3bc |
|
@ -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;
|
||||||
|
|
132
www/js/main.ts
132
www/js/main.ts
|
@ -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')!;
|
||||||
|
const r = new renderer.Renderer(canvas, ctx);
|
||||||
|
|
||||||
|
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?
|
// TODO Redo this on resize?
|
||||||
const w = canvas.offsetWidth;
|
const numSets = (1 + 2 * extraSets);
|
||||||
const h = canvas.offsetHeight;
|
setSizeInPx = zoom ? zoom * defaultSetSizeInPx : (setSizeInPx ?? defaultSetSizeInPx);
|
||||||
|
const w = setSizeInPx * (1 + 2 * extraLines);
|
||||||
|
const h = setSizeInPx * numSets;
|
||||||
const s = window.devicePixelRatio;
|
const s = window.devicePixelRatio;
|
||||||
|
|
||||||
|
canvasDiv.style.width = w + 'px';
|
||||||
|
canvasDiv.style.height = h + 'px';
|
||||||
|
|
||||||
canvas.width = w * s;
|
canvas.width = w * s;
|
||||||
canvas.height = h * s;
|
canvas.height = h * s;
|
||||||
|
|
||||||
ctx.translate(canvas.width / 2.0, canvas.height / 2.0);
|
ctx.translate(canvas.width / 2.0, canvas.height / 2.0);
|
||||||
const scale = 10;
|
const scale = numSets * setDistance;
|
||||||
ctx.scale(canvas.height / scale, -canvas.height / scale);
|
ctx.scale(canvas.height / scale, -canvas.height / scale);
|
||||||
const r = new renderer.Renderer(canvas, ctx);
|
r.extraLines = Math.ceil(extraLines) + 1;
|
||||||
r.extraLines = 3;
|
r.extraSets = Math.ceil(extraSets) + 1;
|
||||||
r.extraSets = 3;
|
if (r.animation) {
|
||||||
|
drawAtCurrentBeat();
|
||||||
|
} else {
|
||||||
r.clear();
|
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');
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user