refactor(web): third pass - relocates correction types & methods to input-processor

This commit is contained in:
Joshua A. Horton 2023-10-17 10:40:35 +07:00
parent 41dfda3089
commit 445f6bd868
8 changed files with 123 additions and 135 deletions

View file

@ -0,0 +1,111 @@
import { ActiveKey, ActiveKeyBase, ActiveLayer, ActiveLayout, ActiveRow, Codes } from "@keymanapp/keyboard-processor";
/**
* Defines correction-layout mappings for keys to be considered by
* the fat-finger algorithm and its related calculations, which are
* used to determine the "closest keys" for corrections.
*/
export interface CorrectionLayoutEntry {
/**
* The ID of the key corresponding to this entry.
*/
readonly keySpec: ActiveKeyBase;
/**
* Represents the center x coordinate of the key based on the coordinate system
* with the keyboard's layout bounding box mapped to a box from <0, 0> to <1, 1>.
*/
readonly centerX: number;
/**
* Represents the center y coordinate of the key based on the coordinate system
* with the keyboard's layout bounding box mapped to a box from <0, 0> to <1, 1>.
*/
readonly centerY: number;
/**
* Represents the key's width based on the coordinate system with the
* keyboard's layout bounding box mapped to a box from <0, 0> to <1, 1>.
*/
readonly width: number;
/**
* Represents the key's height based on the coordinate system with the
* keyboard's layout bounding box mapped to a box from <0, 0> to <1, 1>.
*/
readonly height: number;
}
export interface CorrectionLayout {
/**
* Defines the mappings of each key to be considered by a key-correction
* algorithm. The key's bounding box should be defined relative to its
* containers bounding box, with both mapped to a coordinate system from
* <0, 0> to <1, 1> - a unit square.
*/
keys: CorrectionLayoutEntry[];
/**
* The ratio of the keyboard's horizontal scale to its vertical scale.
* For a 400 x 200 keyboard, should be 2.
*/
kbdScaleRatio: number;
}
// Not compatible with subkeys - their layout data is only determined (presently) at runtime.
export class CorrectiveBaseKeyLayout implements CorrectionLayoutEntry {
readonly keySpec: ActiveKey;
readonly centerX: number;
readonly centerY: number;
readonly width: number;
readonly height: number;
constructor(layer: ActiveLayer, row: ActiveRow, key: ActiveKey) {
this.keySpec = key;
this.centerX = key.proportionalX;
this.centerY = row.proportionalY;
this.width = key.proportionalWidth;
this.height = layer.rowProportionalHeight;
}
}
/**
* Indicates whether or not the specified key should be considered as a valid
* key-correction target during fat-finger operations.
* @param key
* @returns `true` if valid, `false` if invalid.
*/
export function correctionKeyFilter(key: ActiveKeyBase): boolean {
// If the key lacks an ID, just skip it. Sometimes used for padding.
if(!key.baseKeyID) {
return false;
// Attempt to filter out known non-output keys.
// Results in a more optimized distribution.
} else if(Codes.isKnownOSKModifierKey(key.baseKeyID)) {
return false;
} else if(key.isPadding) { // to the user, blank / padding keys do not exist.
return false;
} else {
return true;
}
}
/**
* Builds the corrective layout object corresponding to the specified keyboard layer,
* as needed for use of our key-correction algorithms.
*
* @param layer The layer spec to reference for key corrections.
* @param kbdScaleRatio The ratio of the keyboard's horizontal scale to its vertical scale.
* For a 400 x 200 keyboard, should be 2.
*/
export function buildCorrectiveLayout(layer: ActiveLayer, kbdScaleRatio: number) {
return {
keys: layer.row.map((row) => {
return row.key.map((key) => new CorrectiveBaseKeyLayout(this, row, key));
// ... and flatten/merge the resulting arrays.
}).reduce((flattened, rowEntries) => flattened.concat(rowEntries), [])
.filter((entry) => correctionKeyFilter(entry.keySpec)),
kbdScaleRatio: kbdScaleRatio
};
}

View file

@ -54,7 +54,7 @@ export function keyTouchDistances(touchCoords: {x: number, y: number}, correctiv
distY += dy * entry.height;
const distance = distX * distX + distY * distY;
keyDists[entry.keySpec.coreID] = distance;
keyDists.set(entry.keySpec.coreID, distance);
});
return keyDists;
@ -81,14 +81,10 @@ export function distributionFromDistanceMap(squaredDistMap: Map<string, number>)
keyProbs.set(key, keyProbs.get(key) ?? 0 + entry);
}
for(let key of Object.keys(keyProbs)) {
keyProbs.set(key, keyProbs.get(key) / totalMass);
}
const list: {keyId: string, p: number}[] = [];
for(let key of keyProbs.keys()) {
list.push({keyId: key, p: keyProbs.get(key)});
list.push({keyId: key, p: keyProbs.get(key) / totalMass});
}
return list.sort(function(a, b) {

View file

@ -1,5 +1,7 @@
/// <reference path="../../../predictive-text/src/worker-interface.d.ts" />
export * from './corrections.js';
export * from './correctionLayout.js';
export { default as InputProcessor } from './text/inputProcessor.js';
export { default as ContextWindow } from './text/contextWindow.js';
export { default as ModelSpec } from './text/prediction/modelSpec.js';

View file

@ -2,6 +2,8 @@
"extends": "../tsconfig.kmw-main-base.json",
"compilerOptions": {
"baseUrl": "./",
// Necessary for ES5 iteration of Map.keys().
"downlevelIteration": true,
"outDir": "build/obj/",
"tsBuildInfoFile": "build/obj/tsconfig.tsbuildinfo",
"rootDir": "./src"

View file

@ -1,7 +1,6 @@
import Codes from "../text/codes.js";
import KeyEvent, { KeyEventSpec } from "../text/keyEvent.js";
import KeyMapping from "../text/keyMapping.js";
import type { KeyDistribution } from "../text/keyEvent.js";
import { Layouts } from "./defaultLayouts.js";
import type { LayoutKey, LayoutSubKey, LayoutRow, LayoutLayer, LayoutFormFactor, ButtonClass } from "./defaultLayouts.js";
import type Keyboard from "./keyboard.js";
@ -11,9 +10,6 @@ import TouchLayoutDefaultHint = TouchLayout.TouchLayoutDefaultHint;
import TouchLayoutFlick = TouchLayout.TouchLayoutFlick;
import { type DeviceSpec } from "@keymanapp/web-utils";
import { CorrectionLayout, CorrectionLayoutEntry } from "./correctionLayout.js";
import { distributionFromDistanceMap, keyTouchDistances } from "./corrections.js";
// TS 3.9 changed behavior of getters to make them
// non-enumerable by default. This broke our 'polyfill'
// functions which depended on enumeration to copy the
@ -38,23 +34,6 @@ interface AnalysisMetadata {
hasLongpresses: boolean;
}
// Not compatible with subkeys - their layout data is only determined (presently) at runtime.
class CorrectiveBaseKeyLayout implements CorrectionLayoutEntry {
readonly keySpec: ActiveKeyBase;
readonly centerX: number;
readonly centerY: number;
readonly width: number;
readonly height: number;
constructor(layer: ActiveLayer, row: ActiveRow, key: ActiveKey) {
this.keySpec = key;
this.centerX = key.proportionalX;
this.centerY = row.proportionalY;
this.width = key.proportionalWidth;
this.height = layer.rowProportionalHeight;
}
}
export class ActiveKeyBase {
static readonly DEFAULT_PAD=15; // Padding to left of key, in virtual units
static readonly DEFAULT_RIGHT_MARGIN=15; // Padding to right of right-most key, in virtual units
@ -608,53 +587,6 @@ export class ActiveLayer implements LayoutLayer {
return map;
}
/**
* Builds a sorted-order array of most likely keys to be intended for a given touch.
* @param touchCoords A proportional (x, y) coordinate of the touch within the keyboard's geometry.
* Should be within [0, 0] to [1, 1].
* @param kbdScaleRatio The ratio of the keyboard's horizontal scale to its vertical scale.
* For a 400 x 200 keyboard, should be 2.
*/
getTouchProbabilities(touchCoords: {x: number, y: number}, kbdScaleRatio: number): KeyDistribution {
const correctiveLayout = this.buildCorrectiveLayout(kbdScaleRatio);
const rawSqDistances = keyTouchDistances(touchCoords, correctiveLayout);
return distributionFromDistanceMap(rawSqDistances);
}
/**
* Builds the corrective layout object corresponding to this layer, as needed for use
* of our key-correction algorithms.
*
* @param kbdScaleRatio The ratio of the keyboard's horizontal scale to its vertical scale.
* For a 400 x 200 keyboard, should be 2.
*/
public buildCorrectiveLayout(kbdScaleRatio: number) {
return {
keys: this.row.map((row) => {
return row.key.map((key) => new CorrectiveBaseKeyLayout(this, row, key));
// ... and flatten/merge the resulting arrays.
}).reduce((flattened, rowEntries) => flattened.concat(rowEntries), [])
.filter((entry) => {
const key = entry.keySpec;
// If the key lacks an ID, just skip it. Sometimes used for padding.
if(!key.baseKeyID) {
return false;
// Attempt to filter out known non-output keys.
// Results in a more optimized distribution.
} else if(Codes.isKnownOSKModifierKey(key.baseKeyID)) {
return false;
} else if(key.isPadding) { // to the user, blank / padding keys do not exist.
return false;
} else {
return true;
}
}),
kbdScaleRatio: kbdScaleRatio
};
}
getKey(keyId: string) {
// Keys usually are specified in a "long form" prefixed with their layer's ID.
if(keyId.indexOf(this.id + '-') == 0) {

View file

@ -1,57 +0,0 @@
import { ActiveKeyBase } from "./activeLayout.js";
// This interface lives within the keyboard-processor package because
// fat-finger correction layout info was spec'd there in and before
// Keyman 16.0.
/**
* Defines correction-layout mappings for keys to be considered by
* the fat-finger algorithm and its related calculations, which are
* used to determine the "closest keys" for corrections.
*/
export interface CorrectionLayoutEntry {
/**
* The ID of the key corresponding to this entry.
*/
readonly keySpec: ActiveKeyBase;
/**
* Represents the center x coordinate of the key based on the coordinate system
* with the keyboard's layout bounding box mapped to a box from <0, 0> to <1, 1>.
*/
readonly centerX: number;
/**
* Represents the center y coordinate of the key based on the coordinate system
* with the keyboard's layout bounding box mapped to a box from <0, 0> to <1, 1>.
*/
readonly centerY: number;
/**
* Represents the key's width based on the coordinate system with the
* keyboard's layout bounding box mapped to a box from <0, 0> to <1, 1>.
*/
readonly width: number;
/**
* Represents the key's height based on the coordinate system with the
* keyboard's layout bounding box mapped to a box from <0, 0> to <1, 1>.
*/
readonly height: number;
}
export interface CorrectionLayout {
/**
* Defines the mappings of each key to be considered by a key-correction
* algorithm. The key's bounding box should be defined relative to its
* containers bounding box, with both mapped to a coordinate system from
* <0, 0> to <1, 1> - a unit square.
*/
keys: CorrectionLayoutEntry[];
/**
* The ratio of the keyboard's horizontal scale to its vertical scale.
* For a 400 x 200 keyboard, should be 2.
*/
kbdScaleRatio: number;
}

View file

@ -3,8 +3,6 @@
"extends": "../tsconfig.kmw-main-base.json",
"compilerOptions": {
"baseUrl": "./",
// Necessary for ES5 iteration of Map.keys().
"downlevelIteration": true,
"outDir": "build/obj/",
"tsBuildInfoFile": "build/obj/tsconfig.tsbuildinfo",
"rootDir": "./src"

View file

@ -15,6 +15,8 @@ import {
LayoutKey
} from '@keymanapp/keyboard-processor';
import { buildCorrectiveLayout, distributionFromDistanceMap, keyTouchDistances } from '@keymanapp/input-processor';
import {
GestureRecognizer,
GestureRecognizerConfiguration,
@ -38,7 +40,6 @@ import PendingGesture from './input/gestures/pendingGesture.interface.js';
import RealizedGesture from './input/gestures/realizedGesture.interface.js';
import { defaultFontSize, getFontSizeStyle } from './fontSizeUtils.js';
import PendingMultiTap, { PendingMultiTapState } from './input/gestures/browser/pendingMultiTap.js';
import InternalSubkeyPopup from './input/gestures/browser/subkeyPopup.js';
import InternalKeyTip from './input/gestures/browser/keytip.js';
import CommonConfiguration from './config/commonConfiguration.js';
@ -746,7 +747,10 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
}
let kbdAspectRatio = layerGroup.offsetWidth / this.kbdDiv.offsetHeight;
let baseKeyProbabilities = this.kbdLayout.getLayer(this.layerId).getTouchProbabilities(touchKbdPos, kbdAspectRatio);
const correctiveLayout = buildCorrectiveLayout(this.kbdLayout.getLayer(this.layerId), kbdAspectRatio);
const rawSqDistances = keyTouchDistances(touchKbdPos, correctiveLayout);
let baseKeyProbabilities = distributionFromDistanceMap(rawSqDistances);
if (!keySpec || !this.subkeyGesture || !this.subkeyGesture.baseKey.key) {
return baseKeyProbabilities;