Skip to content

Commit

Permalink
Implement piano-style mapper
Browse files Browse the repository at this point in the history
ref #1
  • Loading branch information
frostburn committed Jan 3, 2024
1 parent 5ed8e65 commit 83146fa
Show file tree
Hide file tree
Showing 6 changed files with 486 additions and 338 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {describe, it, expect, vi} from 'vitest';
import {CoordinateKeyboardEvent, Keyboard} from '..';
import {CoordinateKeyboardEvent, Keyboard} from '../keyboard';

describe('Isomoprhic QWERTY keyboard', () => {
it('only triggers once on multiple repeats of an event', () => {
Expand Down
81 changes: 81 additions & 0 deletions src/__tests__/piano.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {describe, it, expect} from 'vitest';
import {pianoMap} from '../piano';
import {CODES_LAYER_1} from '../coordinates';

describe('Piano-style index mapper', () => {
it('maps a chromatic scale diatonically starting from KeyA', () => {
const {indexByCode, coordsByIndex} = pianoMap([
2, // C
1, // C#
2, // D
1, // D#
2, // E
2, // F
1, // F#
2, // G
1, // G#
2, // A
1, // A#
2, // B
2, // c
1, // c#
2, // d
1, // d#
2, // e
2, // f
1, // f#
2, // g
1, // g#
2, // a
1, // a#
2, // b
]);
const asdfRow = CODES_LAYER_1[2]
.slice(1)
.map(code => indexByCode.get(code!));
const qwertyRow = CODES_LAYER_1[1]
.slice(1)
.map(code => indexByCode.get(code!));
expect(coordsByIndex).toEqual([
[0, 2, 1],
[1, 1, 1],
[1, 2, 1],
[2, 1, 1],
[2, 2, 1],
[3, 2, 1],
[4, 1, 1],
[4, 2, 1],
[5, 1, 1],
[5, 2, 1],
[6, 1, 1],
[6, 2, 1],
[7, 2, 1],
[8, 1, 1],
[8, 2, 1],
[9, 1, 1],
[9, 2, 1],
[10, 2, 1],
[11, 1, 1],
[11, 2, 1],
undefined,
undefined,
undefined,
undefined,
]);
expect(asdfRow).toEqual([0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19]);
expect(qwertyRow).toEqual([
undefined,
1,
3,
undefined,
6,
8,
10,
undefined,
13,
15,
undefined,
18,
]);
});
});
155 changes: 155 additions & 0 deletions src/coordinates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
Split the keyboard into xy-planes along a z-coordinate for different contiguous regions of keys
*/

export type Coords3D = [number, number, number];

const ORIGIN_LAYER_0 = 0;
/**
* Key codes for the row consisting of Esc and FN keys.
*/
export const CODES_LAYER_0 = [
[
'Escape',
'F1',
'F2',
'F3',
'F4',
'F5',
'F6',
'F7',
'F8',
'F9',
'F10',
'F11',
'F12',
],
];

const ORIGIN_LAYER_1 = -1;
/**
* Key codes for the rows containing the digits, qwerty, asdf and zxcv.
*/
export const CODES_LAYER_1 = [
[
'Backquote',
'Digit1',
'Digit2',
'Digit3',
'Digit4',
'Digit5',
'Digit6',
'Digit7',
'Digit8',
'Digit9',
'Digit0',
'Minus',
'Equal',
],
[
null,
'KeyQ',
'KeyW',
'KeyE',
'KeyR',
'KeyT',
'KeyY',
'KeyU',
'KeyI',
'KeyO',
'KeyP',
'BracketLeft',
'BracketRight',
],
[
null,
'KeyA',
'KeyS',
'KeyD',
'KeyF',
'KeyG',
'KeyH',
'KeyJ',
'KeyK',
'KeyL',
'Semicolon',
'Quote',
'Backslash',
],
[
'IntlBackslash',
'KeyZ',
'KeyX',
'KeyC',
'KeyV',
'KeyB',
'KeyN',
'KeyM',
'Comma',
'Period',
'Slash',
],
];

const ORIGIN_LAYER_2 = 0;
/**
* Key codes for the cluster of keys with Page Up/Down.
*/
export const CODES_LAYER_2 = [
['Insert', 'Home', 'PageUp'],
['Delete', 'End', 'PageDown'],
];

const ORIGIN_LAYER_3 = 0;
/**
* Key codes for the numpad.
*/
export const CODES_LAYER_3 = [
['NumLock', 'NumpadDivide', 'NumpadMultiply', 'NumpadSubtract'],
['Numpad7', 'Numpad8', 'Numpad9', 'NumpadAdd'],
['Numpad4', 'Numpad5', 'Numpad6'],
['Numpad1', 'Numpad2', 'Numpad3', 'NumpadEnter'],
['Numpad0', null, 'NumpadDecimal'],
];

/**
* Mapping from key codes to coordinates of input device geometry.
*/
export const COORDS_BY_CODE: Map<string, Coords3D> = new Map();
CODES_LAYER_0.forEach((row, y) =>
row.forEach((code, x) => COORDS_BY_CODE.set(code, [ORIGIN_LAYER_0 + x, y, 0]))
);
CODES_LAYER_1.forEach((row, y) => {
row.forEach((code, x) => {
if (code !== null) {
COORDS_BY_CODE.set(code, [ORIGIN_LAYER_1 + x, y, 1]);
}
});
});
CODES_LAYER_2.forEach((row, y) =>
row.forEach((code, x) => COORDS_BY_CODE.set(code, [ORIGIN_LAYER_2 + x, y, 2]))
);
CODES_LAYER_3.forEach((row, y) => {
row.forEach((code, x) => {
if (code !== null) {
COORDS_BY_CODE.set(code, [ORIGIN_LAYER_3 + x, y, 3]);
}
});
});

const CODE_BY_COORDS: Record<string, string> = {};

for (const [code, xyz] of COORDS_BY_CODE) {
const key = xyz.join(',');
CODE_BY_COORDS[key] = code;
}

/**
* Map 3D coordinates to key codes.
* @param xyz 3D coordinates of the physical key.
* @returns Key code associated with the coordinates or `undefined` if there is no association.
*/
export function codeByCoords(xyz: Coords3D): string | undefined {
const key = xyz.join(',');
return CODE_BY_COORDS[key];
}
Loading

0 comments on commit 83146fa

Please sign in to comment.