From 445f6bd868ee191d67e763e4e7fe1b184909b97e Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Tue, 17 Oct 2023 10:40:35 +0700 Subject: [PATCH] refactor(web): third pass - relocates correction types & methods to input-processor --- .../input-processor/src/correctionLayout.ts | 111 ++++++++++++++++++ .../src}/corrections.ts | 8 +- common/web/input-processor/src/index.ts | 2 + common/web/input-processor/tsconfig.json | 2 + .../src/keyboards/activeLayout.ts | 68 ----------- .../src/keyboards/correctionLayout.ts | 57 --------- common/web/keyboard-processor/tsconfig.json | 2 - web/src/engine/osk/src/visualKeyboard.ts | 8 +- 8 files changed, 123 insertions(+), 135 deletions(-) create mode 100644 common/web/input-processor/src/correctionLayout.ts rename common/web/{keyboard-processor/src/keyboards => input-processor/src}/corrections.ts (94%) delete mode 100644 common/web/keyboard-processor/src/keyboards/correctionLayout.ts diff --git a/common/web/input-processor/src/correctionLayout.ts b/common/web/input-processor/src/correctionLayout.ts new file mode 100644 index 0000000000..24ddce01f9 --- /dev/null +++ b/common/web/input-processor/src/correctionLayout.ts @@ -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 + }; +} \ No newline at end of file diff --git a/common/web/keyboard-processor/src/keyboards/corrections.ts b/common/web/input-processor/src/corrections.ts similarity index 94% rename from common/web/keyboard-processor/src/keyboards/corrections.ts rename to common/web/input-processor/src/corrections.ts index 502d644c0a..0fb2af4a92 100644 --- a/common/web/keyboard-processor/src/keyboards/corrections.ts +++ b/common/web/input-processor/src/corrections.ts @@ -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) 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) { diff --git a/common/web/input-processor/src/index.ts b/common/web/input-processor/src/index.ts index 360a660108..d50db0aa48 100644 --- a/common/web/input-processor/src/index.ts +++ b/common/web/input-processor/src/index.ts @@ -1,5 +1,7 @@ /// +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'; diff --git a/common/web/input-processor/tsconfig.json b/common/web/input-processor/tsconfig.json index b6716d28cc..4551a235dc 100644 --- a/common/web/input-processor/tsconfig.json +++ b/common/web/input-processor/tsconfig.json @@ -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" diff --git a/common/web/keyboard-processor/src/keyboards/activeLayout.ts b/common/web/keyboard-processor/src/keyboards/activeLayout.ts index f50f6d33e7..3ea7fa80b4 100644 --- a/common/web/keyboard-processor/src/keyboards/activeLayout.ts +++ b/common/web/keyboard-processor/src/keyboards/activeLayout.ts @@ -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) { diff --git a/common/web/keyboard-processor/src/keyboards/correctionLayout.ts b/common/web/keyboard-processor/src/keyboards/correctionLayout.ts deleted file mode 100644 index d661e766fd..0000000000 --- a/common/web/keyboard-processor/src/keyboards/correctionLayout.ts +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/common/web/keyboard-processor/tsconfig.json b/common/web/keyboard-processor/tsconfig.json index 2ca4f1eac6..d4c06e2aaa 100644 --- a/common/web/keyboard-processor/tsconfig.json +++ b/common/web/keyboard-processor/tsconfig.json @@ -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" diff --git a/web/src/engine/osk/src/visualKeyboard.ts b/web/src/engine/osk/src/visualKeyboard.ts index 9566b2e7b8..6c002f3bcd 100644 --- a/web/src/engine/osk/src/visualKeyboard.ts +++ b/web/src/engine/osk/src/visualKeyboard.ts @@ -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 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;