Compare commits
2 Commits
f1f13bf1fd
...
16f374d927
Author | SHA1 | Date | |
---|---|---|---|
16f374d927 | |||
4481d5f3bc |
|
@ -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;
|
||||
|
|
150
www/js/main.ts
150
www/js/main.ts
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user