mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-20 06:37:40 +00:00
refactor(web): third pass - relocates correction types & methods to input-processor
This commit is contained in:
parent
41dfda3089
commit
445f6bd868
8 changed files with 123 additions and 135 deletions
111
common/web/input-processor/src/correctionLayout.ts
Normal file
111
common/web/input-processor/src/correctionLayout.ts
Normal 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
|
||||
};
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue