Calibrate the scores of the proximity chars according to the distances.
+1 75 -1 27 +2 0 -2 0 +3 0 -3 0 +4 30 -4 48 +5 37 -5 27 +6 4 -6 35 +7 9 -7 18 Change-Id: I3c6ab06a0084c18ab595147c36c2ff4b1e961a7bmain
parent
ce9e4f926b
commit
a4c1f1c1fd
|
@ -118,7 +118,6 @@ bool Correction::initProcessState(const int outputIndex) {
|
||||||
mInputIndex = mCorrectionStates[outputIndex].mInputIndex;
|
mInputIndex = mCorrectionStates[outputIndex].mInputIndex;
|
||||||
mNeedsToTraverseAllNodes = mCorrectionStates[outputIndex].mNeedsToTraverseAllNodes;
|
mNeedsToTraverseAllNodes = mCorrectionStates[outputIndex].mNeedsToTraverseAllNodes;
|
||||||
|
|
||||||
mSumOfDistance = mCorrectionStates[outputIndex].mSumOfDistance;
|
|
||||||
mEquivalentCharCount = mCorrectionStates[outputIndex].mEquivalentCharCount;
|
mEquivalentCharCount = mCorrectionStates[outputIndex].mEquivalentCharCount;
|
||||||
mProximityCount = mCorrectionStates[outputIndex].mProximityCount;
|
mProximityCount = mCorrectionStates[outputIndex].mProximityCount;
|
||||||
mTransposedCount = mCorrectionStates[outputIndex].mTransposedCount;
|
mTransposedCount = mCorrectionStates[outputIndex].mTransposedCount;
|
||||||
|
@ -174,7 +173,6 @@ void Correction::incrementOutputIndex() {
|
||||||
mCorrectionStates[mOutputIndex].mInputIndex = mInputIndex;
|
mCorrectionStates[mOutputIndex].mInputIndex = mInputIndex;
|
||||||
mCorrectionStates[mOutputIndex].mNeedsToTraverseAllNodes = mNeedsToTraverseAllNodes;
|
mCorrectionStates[mOutputIndex].mNeedsToTraverseAllNodes = mNeedsToTraverseAllNodes;
|
||||||
|
|
||||||
mCorrectionStates[mOutputIndex].mSumOfDistance = mSumOfDistance;
|
|
||||||
mCorrectionStates[mOutputIndex].mEquivalentCharCount = mEquivalentCharCount;
|
mCorrectionStates[mOutputIndex].mEquivalentCharCount = mEquivalentCharCount;
|
||||||
mCorrectionStates[mOutputIndex].mProximityCount = mProximityCount;
|
mCorrectionStates[mOutputIndex].mProximityCount = mProximityCount;
|
||||||
mCorrectionStates[mOutputIndex].mTransposedCount = mTransposedCount;
|
mCorrectionStates[mOutputIndex].mTransposedCount = mTransposedCount;
|
||||||
|
@ -227,20 +225,26 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
||||||
// TODO: Change the limit if we'll allow two or more corrections
|
// TODO: Change the limit if we'll allow two or more corrections
|
||||||
const bool noCorrectionsHappenedSoFar = correctionCount == 0;
|
const bool noCorrectionsHappenedSoFar = correctionCount == 0;
|
||||||
const bool canTryCorrection = noCorrectionsHappenedSoFar;
|
const bool canTryCorrection = noCorrectionsHappenedSoFar;
|
||||||
|
int proximityIndex = 0;
|
||||||
|
mDistances[mOutputIndex] = NOT_A_DISTANCE;
|
||||||
|
|
||||||
if (mNeedsToTraverseAllNodes || isQuote(c)) {
|
if (mNeedsToTraverseAllNodes || isQuote(c)) {
|
||||||
bool incremented = false;
|
bool incremented = false;
|
||||||
if (mLastCharExceeded && mInputIndex == mInputLength - 1) {
|
if (mLastCharExceeded && mInputIndex == mInputLength - 1) {
|
||||||
// TODO: Do not check the proximity if EditDistance exceeds the threshold
|
// TODO: Do not check the proximity if EditDistance exceeds the threshold
|
||||||
const ProximityInfo::ProximityType matchId =
|
const ProximityInfo::ProximityType matchId =
|
||||||
mProximityInfo->getMatchedProximityId(mInputIndex, c, true);
|
mProximityInfo->getMatchedProximityId(mInputIndex, c, true, &proximityIndex);
|
||||||
if (isEquivalentChar(matchId)) {
|
if (isEquivalentChar(matchId)) {
|
||||||
mLastCharExceeded = false;
|
mLastCharExceeded = false;
|
||||||
--mExcessiveCount;
|
--mExcessiveCount;
|
||||||
|
mDistances[mOutputIndex] =
|
||||||
|
mProximityInfo->getNormalizedSquaredDistance(mInputIndex, 0);
|
||||||
} else if (matchId == ProximityInfo::NEAR_PROXIMITY_CHAR) {
|
} else if (matchId == ProximityInfo::NEAR_PROXIMITY_CHAR) {
|
||||||
mLastCharExceeded = false;
|
mLastCharExceeded = false;
|
||||||
--mExcessiveCount;
|
--mExcessiveCount;
|
||||||
++mProximityCount;
|
++mProximityCount;
|
||||||
|
mDistances[mOutputIndex] =
|
||||||
|
mProximityInfo->getNormalizedSquaredDistance(mInputIndex, proximityIndex);
|
||||||
}
|
}
|
||||||
incrementInputIndex();
|
incrementInputIndex();
|
||||||
incremented = true;
|
incremented = true;
|
||||||
|
@ -301,7 +305,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
||||||
const bool checkProximityChars = noCorrectionsHappenedSoFar || mProximityCount == 0;
|
const bool checkProximityChars = noCorrectionsHappenedSoFar || mProximityCount == 0;
|
||||||
ProximityInfo::ProximityType matchedProximityCharId = secondTransposing
|
ProximityInfo::ProximityType matchedProximityCharId = secondTransposing
|
||||||
? ProximityInfo::EQUIVALENT_CHAR
|
? ProximityInfo::EQUIVALENT_CHAR
|
||||||
: mProximityInfo->getMatchedProximityId(mInputIndex, c, checkProximityChars);
|
: mProximityInfo->getMatchedProximityId(
|
||||||
|
mInputIndex, c, checkProximityChars, &proximityIndex);
|
||||||
|
|
||||||
if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
|
if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
|
||||||
if (canTryCorrection && mOutputIndex > 0
|
if (canTryCorrection && mOutputIndex > 0
|
||||||
|
@ -323,8 +328,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
||||||
// Here, we are doing something equivalent to matchedProximityCharId,
|
// Here, we are doing something equivalent to matchedProximityCharId,
|
||||||
// but we already know that "excessive char correction" just happened
|
// but we already know that "excessive char correction" just happened
|
||||||
// so that we just need to check "mProximityCount == 0".
|
// so that we just need to check "mProximityCount == 0".
|
||||||
matchedProximityCharId =
|
matchedProximityCharId = mProximityInfo->getMatchedProximityId(
|
||||||
mProximityInfo->getMatchedProximityId(mInputIndex, c, mProximityCount == 0);
|
mInputIndex, c, mProximityCount == 0, &proximityIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,17 +404,12 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
||||||
} else if (isEquivalentChar(matchedProximityCharId)) {
|
} else if (isEquivalentChar(matchedProximityCharId)) {
|
||||||
mMatching = true;
|
mMatching = true;
|
||||||
++mEquivalentCharCount;
|
++mEquivalentCharCount;
|
||||||
if (mSumOfDistance != NOT_A_DISTANCE) {
|
mDistances[mOutputIndex] = mProximityInfo->getNormalizedSquaredDistance(mInputIndex, 0);
|
||||||
const int distance = mProximityInfo->getNormalizedSquaredDistance(mInputIndex);
|
|
||||||
if (distance != NOT_A_DISTANCE) {
|
|
||||||
mSumOfDistance += distance;
|
|
||||||
} else {
|
|
||||||
mSumOfDistance = NOT_A_DISTANCE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) {
|
} else if (ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) {
|
||||||
mProximityMatching = true;
|
mProximityMatching = true;
|
||||||
++mProximityCount;
|
++mProximityCount;
|
||||||
|
mDistances[mOutputIndex] =
|
||||||
|
mProximityInfo->getNormalizedSquaredDistance(mInputIndex, proximityIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
mWord[mOutputIndex] = c;
|
mWord[mOutputIndex] = c;
|
||||||
|
@ -583,8 +583,6 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
|
||||||
const int transposedCount = correction->mTransposedCount / 2;
|
const int transposedCount = correction->mTransposedCount / 2;
|
||||||
const int excessiveCount = correction->mExcessiveCount + correction->mTransposedCount % 2;
|
const int excessiveCount = correction->mExcessiveCount + correction->mTransposedCount % 2;
|
||||||
const int proximityMatchedCount = correction->mProximityCount;
|
const int proximityMatchedCount = correction->mProximityCount;
|
||||||
const int mSumOfDistance = correction->mSumOfDistance;
|
|
||||||
const int mEquivalentCharCount = correction->mEquivalentCharCount;
|
|
||||||
const bool lastCharExceeded = correction->mLastCharExceeded;
|
const bool lastCharExceeded = correction->mLastCharExceeded;
|
||||||
const bool useFullEditDistance = correction->mUseFullEditDistance;
|
const bool useFullEditDistance = correction->mUseFullEditDistance;
|
||||||
const int outputLength = outputIndex + 1;
|
const int outputLength = outputIndex + 1;
|
||||||
|
@ -684,6 +682,41 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Score calibration by touch coordinates is being done only for pure-fat finger typing error
|
||||||
|
// cases.
|
||||||
|
// TODO: Remove this constraint.
|
||||||
|
if (CALIBRATE_SCORE_BY_TOUCH_COORDINATES && proximityInfo->touchPositionCorrectionEnabled()
|
||||||
|
&& skippedCount == 0 && excessiveCount == 0 && transposedCount == 0) {
|
||||||
|
for (int i = 0; i < outputLength; ++i) {
|
||||||
|
const int squaredDistance = correction->mDistances[i];
|
||||||
|
if (i < adjustedProximityMatchedCount) {
|
||||||
|
multiplyIntCapped(typedLetterMultiplier, &finalFreq);
|
||||||
|
}
|
||||||
|
if (squaredDistance >= 0) {
|
||||||
|
// Promote or demote the score according to the distance from the sweet spot
|
||||||
|
static const float A = ZERO_DISTANCE_PROMOTION_RATE / 100.0f;
|
||||||
|
static const float B = 1.0f;
|
||||||
|
static const float C = 0.5f;
|
||||||
|
static const float R1 = NEUTRAL_SCORE_SQUARED_RADIUS;
|
||||||
|
static const float R2 = HALF_SCORE_SQUARED_RADIUS;
|
||||||
|
const float x = (float)squaredDistance
|
||||||
|
/ ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
|
||||||
|
const float factor = (x < R1)
|
||||||
|
? (A * (R1 - x) + B * x) / R1
|
||||||
|
: (B * (R2 - x) + C * (x - R1)) / (R2 - R1);
|
||||||
|
// factor is piecewise linear function like:
|
||||||
|
// A -_ .
|
||||||
|
// ^-_ .
|
||||||
|
// B \ .
|
||||||
|
// \ .
|
||||||
|
// C \ .
|
||||||
|
// 0 R1 R2
|
||||||
|
multiplyRate((int)(factor * 100), &finalFreq);
|
||||||
|
} else if (squaredDistance == PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO) {
|
||||||
|
multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Promotion for a word with proximity characters
|
// Promotion for a word with proximity characters
|
||||||
for (int i = 0; i < adjustedProximityMatchedCount; ++i) {
|
for (int i = 0; i < adjustedProximityMatchedCount; ++i) {
|
||||||
// A word with proximity corrections
|
// A word with proximity corrections
|
||||||
|
@ -693,38 +726,6 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
|
||||||
multiplyIntCapped(typedLetterMultiplier, &finalFreq);
|
multiplyIntCapped(typedLetterMultiplier, &finalFreq);
|
||||||
multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
|
multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CALIBRATE_SCORE_BY_TOUCH_COORDINATES
|
|
||||||
&& mEquivalentCharCount > 0 && mSumOfDistance != NOT_A_DISTANCE) {
|
|
||||||
// Let (x, y) be the coordinate of a user's touch, and let c be a key.
|
|
||||||
// Assuming users' touch distribution is gauss distribution, the conditional probability of
|
|
||||||
// the user touching (x, y) given he or she intends to hit c is:
|
|
||||||
// p(x, y | c) = exp(-(x - m_x) / (2 * s^2)) / (sqrt(2 * pi) * s)
|
|
||||||
// * exp(-(y - m_y) / (2 * s^2)) / (sqrt(2 * pi) * s)
|
|
||||||
// where (m_x, m_y) is a mean of touches of c, and s is a variance of touches of c.
|
|
||||||
// If user touches c1, c2, .., cn, the joint distribution is
|
|
||||||
// p(x1, y1 | c1) * p(x2, y2 | c2) * ... * p(xn, yn | cn)
|
|
||||||
// We consider the logarithm of this value, that is
|
|
||||||
// sum_i log p(x_i, y_i | c_i) + const
|
|
||||||
// = sum_i ((x_i - m_x)^2 + (y_i - m_y)^2) / (2 * s^2) + const
|
|
||||||
// Thus, we use the sum of squared distance as a score of the word.
|
|
||||||
static const int UPPER = WORDS_WITH_EQUIVALENT_CHAR_STRONGEST_PROMOTION_RATE;
|
|
||||||
static const int LOWER = WORDS_WITH_EQUIVALENT_CHAR_WEAKEST_DEMOTION_RATE;
|
|
||||||
static const int MIDDLE = 100;
|
|
||||||
static const int SHIFT = ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
|
|
||||||
const int expected = mEquivalentCharCount << SHIFT;
|
|
||||||
// factor is a function as described below:
|
|
||||||
// U\ .
|
|
||||||
// \ .
|
|
||||||
// M \ .
|
|
||||||
// \ .
|
|
||||||
// L \------- .
|
|
||||||
// 0 e
|
|
||||||
// (x-axis is mSumOfDistance, y-axis is rate,
|
|
||||||
// and e, U, M, L are expected, UPPER, MIDDLE, LOWER respectively.
|
|
||||||
const int factor =
|
|
||||||
max((UPPER * expected - (UPPER - MIDDLE) * mSumOfDistance) / expected, LOWER);
|
|
||||||
multiplyRate(factor, &finalFreq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const int errorCount = adjustedProximityMatchedCount > 0
|
const int errorCount = adjustedProximityMatchedCount > 0
|
||||||
|
|
|
@ -115,7 +115,11 @@ private:
|
||||||
int mMissingSpacePos;
|
int mMissingSpacePos;
|
||||||
int mTerminalInputIndex;
|
int mTerminalInputIndex;
|
||||||
int mTerminalOutputIndex;
|
int mTerminalOutputIndex;
|
||||||
|
|
||||||
|
// The following arrays are state buffer.
|
||||||
unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
|
unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
|
||||||
|
int mDistances[MAX_WORD_LENGTH_INTERNAL];
|
||||||
|
|
||||||
// Edit distance calculation requires a buffer with (N+1)^2 length for the input length N.
|
// Edit distance calculation requires a buffer with (N+1)^2 length for the input length N.
|
||||||
// Caveat: Do not create multiple tables per thread as this table eats up RAM a lot.
|
// Caveat: Do not create multiple tables per thread as this table eats up RAM a lot.
|
||||||
int mEditDistanceTable[(MAX_WORD_LENGTH_INTERNAL + 1) * (MAX_WORD_LENGTH_INTERNAL + 1)];
|
int mEditDistanceTable[(MAX_WORD_LENGTH_INTERNAL + 1) * (MAX_WORD_LENGTH_INTERNAL + 1)];
|
||||||
|
@ -128,7 +132,6 @@ private:
|
||||||
int mInputIndex;
|
int mInputIndex;
|
||||||
|
|
||||||
int mEquivalentCharCount;
|
int mEquivalentCharCount;
|
||||||
int mSumOfDistance;
|
|
||||||
int mProximityCount;
|
int mProximityCount;
|
||||||
int mExcessiveCount;
|
int mExcessiveCount;
|
||||||
int mTransposedCount;
|
int mTransposedCount;
|
||||||
|
|
|
@ -29,7 +29,6 @@ struct CorrectionState {
|
||||||
uint16_t mChildCount;
|
uint16_t mChildCount;
|
||||||
uint8_t mInputIndex;
|
uint8_t mInputIndex;
|
||||||
|
|
||||||
int32_t mSumOfDistance;
|
|
||||||
uint8_t mEquivalentCharCount;
|
uint8_t mEquivalentCharCount;
|
||||||
uint8_t mProximityCount;
|
uint8_t mProximityCount;
|
||||||
uint8_t mTransposedCount;
|
uint8_t mTransposedCount;
|
||||||
|
@ -65,7 +64,6 @@ inline static void initCorrectionState(CorrectionState *state, const int rootPos
|
||||||
state->mExcessivePos = -1;
|
state->mExcessivePos = -1;
|
||||||
state->mSkipPos = -1;
|
state->mSkipPos = -1;
|
||||||
|
|
||||||
state->mSumOfDistance = 0;
|
|
||||||
state->mEquivalentCharCount = 0;
|
state->mEquivalentCharCount = 0;
|
||||||
state->mProximityCount = 0;
|
state->mProximityCount = 0;
|
||||||
state->mTransposedCount = 0;
|
state->mTransposedCount = 0;
|
||||||
|
|
|
@ -163,6 +163,9 @@ static void dumpWord(const unsigned short* word, const int length) {
|
||||||
#define NOT_VALID_WORD -99
|
#define NOT_VALID_WORD -99
|
||||||
#define NOT_A_CHARACTER -1
|
#define NOT_A_CHARACTER -1
|
||||||
#define NOT_A_DISTANCE -1
|
#define NOT_A_DISTANCE -1
|
||||||
|
#define EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO -2
|
||||||
|
#define PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO -3
|
||||||
|
#define NOT_A_INDEX -1
|
||||||
|
|
||||||
#define KEYCODE_SPACE ' '
|
#define KEYCODE_SPACE ' '
|
||||||
|
|
||||||
|
@ -181,8 +184,6 @@ static void dumpWord(const unsigned short* word, const int length) {
|
||||||
#define WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE 75
|
#define WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE 75
|
||||||
#define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75
|
#define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75
|
||||||
#define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60
|
#define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60
|
||||||
#define WORDS_WITH_EQUIVALENT_CHAR_STRONGEST_PROMOTION_RATE 110
|
|
||||||
#define WORDS_WITH_EQUIVALENT_CHAR_WEAKEST_DEMOTION_RATE 90
|
|
||||||
#define FULL_MATCHED_WORDS_PROMOTION_RATE 120
|
#define FULL_MATCHED_WORDS_PROMOTION_RATE 120
|
||||||
#define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
|
#define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
|
||||||
#define WORDS_WITH_MATCH_SKIP_PROMOTION_RATE 105
|
#define WORDS_WITH_MATCH_SKIP_PROMOTION_RATE 105
|
||||||
|
@ -191,6 +192,9 @@ static void dumpWord(const unsigned short* word, const int length) {
|
||||||
#define INPUT_EXCEEDS_OUTPUT_DEMOTION_RATE 70
|
#define INPUT_EXCEEDS_OUTPUT_DEMOTION_RATE 70
|
||||||
#define FIRST_CHAR_DIFFERENT_DEMOTION_RATE 96
|
#define FIRST_CHAR_DIFFERENT_DEMOTION_RATE 96
|
||||||
#define TWO_WORDS_CAPITALIZED_DEMOTION_RATE 50
|
#define TWO_WORDS_CAPITALIZED_DEMOTION_RATE 50
|
||||||
|
#define ZERO_DISTANCE_PROMOTION_RATE 110
|
||||||
|
#define NEUTRAL_SCORE_SQUARED_RADIUS 8.0f
|
||||||
|
#define HALF_SCORE_SQUARED_RADIUS 32.0f
|
||||||
|
|
||||||
// This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java
|
// This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java
|
||||||
// This is only used for the size of array. Not to be used in c functions.
|
// This is only used for the size of array. Not to be used in c functions.
|
||||||
|
|
|
@ -44,13 +44,21 @@ ProximityInfo::ProximityInfo(const int maxProximityCharsSize, const int keyboard
|
||||||
CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
|
CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
|
||||||
CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
|
CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
|
||||||
KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)),
|
KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)),
|
||||||
mInputXCoordinates(NULL), mInputYCoordinates(NULL) {
|
HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates
|
||||||
|
&& keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
|
||||||
|
&& sweetSpotCenterYs && sweetSpotRadii),
|
||||||
|
mInputXCoordinates(NULL), mInputYCoordinates(NULL),
|
||||||
|
mTouchPositionCorrectionEnabled(false) {
|
||||||
const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
|
const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
|
||||||
mProximityCharsArray = new uint32_t[len];
|
mProximityCharsArray = new uint32_t[len];
|
||||||
|
mNormalizedSquaredDistances = new int[len];
|
||||||
if (DEBUG_PROXIMITY_INFO) {
|
if (DEBUG_PROXIMITY_INFO) {
|
||||||
LOGI("Create proximity info array %d", len);
|
LOGI("Create proximity info array %d", len);
|
||||||
}
|
}
|
||||||
memcpy(mProximityCharsArray, proximityCharsArray, len * sizeof(mProximityCharsArray[0]));
|
memcpy(mProximityCharsArray, proximityCharsArray, len * sizeof(mProximityCharsArray[0]));
|
||||||
|
for (int i = 0; i < len; ++i) {
|
||||||
|
mNormalizedSquaredDistances[i] = NOT_A_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
copyOrFillZero(mKeyXCoordinates, keyXCoordinates, KEY_COUNT * sizeof(mKeyXCoordinates[0]));
|
copyOrFillZero(mKeyXCoordinates, keyXCoordinates, KEY_COUNT * sizeof(mKeyXCoordinates[0]));
|
||||||
copyOrFillZero(mKeyYCoordinates, keyYCoordinates, KEY_COUNT * sizeof(mKeyYCoordinates[0]));
|
copyOrFillZero(mKeyYCoordinates, keyYCoordinates, KEY_COUNT * sizeof(mKeyYCoordinates[0]));
|
||||||
|
@ -79,6 +87,7 @@ void ProximityInfo::initializeCodeToKeyIndex() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProximityInfo::~ProximityInfo() {
|
ProximityInfo::~ProximityInfo() {
|
||||||
|
delete[] mNormalizedSquaredDistances;
|
||||||
delete[] mProximityCharsArray;
|
delete[] mProximityCharsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,52 +118,61 @@ void ProximityInfo::setInputParams(const int* inputCodes, const int inputLength,
|
||||||
mInputCodes = inputCodes;
|
mInputCodes = inputCodes;
|
||||||
mInputXCoordinates = xCoordinates;
|
mInputXCoordinates = xCoordinates;
|
||||||
mInputYCoordinates = yCoordinates;
|
mInputYCoordinates = yCoordinates;
|
||||||
|
mTouchPositionCorrectionEnabled =
|
||||||
|
HAS_TOUCH_POSITION_CORRECTION_DATA && xCoordinates && yCoordinates;
|
||||||
mInputLength = inputLength;
|
mInputLength = inputLength;
|
||||||
for (int i = 0; i < inputLength; ++i) {
|
for (int i = 0; i < inputLength; ++i) {
|
||||||
mPrimaryInputWord[i] = getPrimaryCharAt(i);
|
mPrimaryInputWord[i] = getPrimaryCharAt(i);
|
||||||
}
|
}
|
||||||
mPrimaryInputWord[inputLength] = 0;
|
mPrimaryInputWord[inputLength] = 0;
|
||||||
for (int i = 0; i < mInputLength; ++i) {
|
for (int i = 0; i < mInputLength; ++i) {
|
||||||
float normalizedSquaredDistance = calculateNormalizedSquaredDistance(i);
|
const int *proximityChars = getProximityCharsAt(i);
|
||||||
if (normalizedSquaredDistance >= 0.0f) {
|
for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE && proximityChars[j] > 0; ++j) {
|
||||||
mNormalizedSquaredDistance[i] =
|
const int currentChar = proximityChars[j];
|
||||||
(int)(normalizedSquaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
|
const int keyIndex = getKeyIndex(currentChar);
|
||||||
|
const float squaredDistance = calculateNormalizedSquaredDistance(keyIndex, i);
|
||||||
|
if (squaredDistance >= 0.0f) {
|
||||||
|
mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE + j] =
|
||||||
|
(int)(squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
|
||||||
} else {
|
} else {
|
||||||
mNormalizedSquaredDistance[i] = NOT_A_DISTANCE;
|
mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE + j] = (j == 0)
|
||||||
|
? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO
|
||||||
|
: PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float square(const float x) { return x * x; }
|
inline float square(const float x) { return x * x; }
|
||||||
|
|
||||||
float ProximityInfo::calculateNormalizedSquaredDistance(int index) const {
|
float ProximityInfo::calculateNormalizedSquaredDistance(
|
||||||
|
const int keyIndex, const int inputIndex) const {
|
||||||
static const float NOT_A_DISTANCE_FLOAT = -1.0f;
|
static const float NOT_A_DISTANCE_FLOAT = -1.0f;
|
||||||
if (KEY_COUNT == 0 || !mInputXCoordinates || !mInputYCoordinates) {
|
if (keyIndex == NOT_A_INDEX) {
|
||||||
// We do not have the coordinate data
|
|
||||||
return NOT_A_DISTANCE_FLOAT;
|
return NOT_A_DISTANCE_FLOAT;
|
||||||
}
|
}
|
||||||
const int currentChar = getPrimaryCharAt(index);
|
if (!hasSweetSpotData(keyIndex)) {
|
||||||
const unsigned short baseLowerC = Dictionary::toBaseLowerCase(currentChar);
|
|
||||||
if (baseLowerC > MAX_CHAR_CODE) {
|
|
||||||
return NOT_A_DISTANCE_FLOAT;
|
return NOT_A_DISTANCE_FLOAT;
|
||||||
}
|
}
|
||||||
const int keyIndex = mCodeToKeyIndex[baseLowerC];
|
const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(keyIndex, inputIndex);
|
||||||
if (keyIndex < 0) {
|
const float squaredRadius = square(mSweetSpotRadii[keyIndex]);
|
||||||
return NOT_A_DISTANCE_FLOAT;
|
|
||||||
}
|
|
||||||
const float radius = mSweetSpotRadii[keyIndex];
|
|
||||||
if (radius <= 0.0) {
|
|
||||||
// When there are no calibration data for a key,
|
|
||||||
// the radius of the key is assigned to zero.
|
|
||||||
return NOT_A_DISTANCE;
|
|
||||||
}
|
|
||||||
const float squaredRadius = square(radius);
|
|
||||||
const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(keyIndex, index);
|
|
||||||
return squaredDistance / squaredRadius;
|
return squaredDistance / squaredRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ProximityInfo::getKeyIndex(const int c) const {
|
||||||
|
if (KEY_COUNT == 0 || !mInputXCoordinates || !mInputYCoordinates) {
|
||||||
|
// We do not have the coordinate data
|
||||||
|
return NOT_A_INDEX;
|
||||||
|
}
|
||||||
|
const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c);
|
||||||
|
if (baseLowerC > MAX_CHAR_CODE) {
|
||||||
|
return NOT_A_INDEX;
|
||||||
|
}
|
||||||
|
return mCodeToKeyIndex[baseLowerC];
|
||||||
|
}
|
||||||
|
|
||||||
float ProximityInfo::calculateSquaredDistanceFromSweetSpotCenter(
|
float ProximityInfo::calculateSquaredDistanceFromSweetSpotCenter(
|
||||||
int keyIndex, int inputIndex) const {
|
const int keyIndex, const int inputIndex) const {
|
||||||
const float sweetSpotCenterX = mSweetSpotCenterXs[keyIndex];
|
const float sweetSpotCenterX = mSweetSpotCenterXs[keyIndex];
|
||||||
const float sweetSpotCenterY = mSweetSpotCenterYs[keyIndex];
|
const float sweetSpotCenterY = mSweetSpotCenterYs[keyIndex];
|
||||||
const float inputX = (float)mInputXCoordinates[inputIndex];
|
const float inputX = (float)mInputXCoordinates[inputIndex];
|
||||||
|
@ -202,11 +220,13 @@ bool ProximityInfo::existsAdjacentProximityChars(const int index) const {
|
||||||
// then the word contains at that position a character close to what the user
|
// then the word contains at that position a character close to what the user
|
||||||
// typed.
|
// typed.
|
||||||
// What the user typed is actually the first character of the array.
|
// What the user typed is actually the first character of the array.
|
||||||
|
// proximityIndex is a pointer to the variable where getMatchedProximityId returns
|
||||||
|
// the index of c in the proximity chars of the input index.
|
||||||
// Notice : accented characters do not have a proximity list, so they are alone
|
// Notice : accented characters do not have a proximity list, so they are alone
|
||||||
// in their list. The non-accented version of the character should be considered
|
// in their list. The non-accented version of the character should be considered
|
||||||
// "close", but not the other keys close to the non-accented version.
|
// "close", but not the other keys close to the non-accented version.
|
||||||
ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(
|
ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(const int index,
|
||||||
const int index, const unsigned short c, const bool checkProximityChars) const {
|
const unsigned short c, const bool checkProximityChars, int *proximityIndex) const {
|
||||||
const int *currentChars = getProximityCharsAt(index);
|
const int *currentChars = getProximityCharsAt(index);
|
||||||
const int firstChar = currentChars[0];
|
const int firstChar = currentChars[0];
|
||||||
const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c);
|
const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c);
|
||||||
|
@ -227,9 +247,14 @@ ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(
|
||||||
|
|
||||||
// Not an exact nor an accent-alike match: search the list of close keys
|
// Not an exact nor an accent-alike match: search the list of close keys
|
||||||
int j = 1;
|
int j = 1;
|
||||||
while (currentChars[j] > 0 && j < MAX_PROXIMITY_CHARS_SIZE) {
|
while (j < MAX_PROXIMITY_CHARS_SIZE && currentChars[j] > 0) {
|
||||||
const bool matched = (currentChars[j] == baseLowerC || currentChars[j] == c);
|
const bool matched = (currentChars[j] == baseLowerC || currentChars[j] == c);
|
||||||
if (matched) return NEAR_PROXIMITY_CHAR;
|
if (matched) {
|
||||||
|
if (proximityIndex) {
|
||||||
|
*proximityIndex = j;
|
||||||
|
}
|
||||||
|
return NEAR_PROXIMITY_CHAR;
|
||||||
|
}
|
||||||
++j;
|
++j;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ class Correction;
|
||||||
class ProximityInfo {
|
class ProximityInfo {
|
||||||
public:
|
public:
|
||||||
static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
|
static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
|
||||||
|
static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
|
||||||
|
1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
|
||||||
|
|
||||||
// Used as a return value for character comparison
|
// Used as a return value for character comparison
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -53,19 +55,20 @@ public:
|
||||||
unsigned short getPrimaryCharAt(const int index) const;
|
unsigned short getPrimaryCharAt(const int index) const;
|
||||||
bool existsCharInProximityAt(const int index, const int c) const;
|
bool existsCharInProximityAt(const int index, const int c) const;
|
||||||
bool existsAdjacentProximityChars(const int index) const;
|
bool existsAdjacentProximityChars(const int index) const;
|
||||||
ProximityType getMatchedProximityId(
|
ProximityType getMatchedProximityId(const int index, const unsigned short c,
|
||||||
const int index, const unsigned short c, const bool checkProximityChars) const;
|
const bool checkProximityChars, int *proximityIndex = NULL) const;
|
||||||
int getNormalizedSquaredDistance(int index) const {
|
int getNormalizedSquaredDistance(const int inputIndex, const int proximityIndex) const {
|
||||||
return mNormalizedSquaredDistance[index];
|
return mNormalizedSquaredDistances[inputIndex * MAX_PROXIMITY_CHARS_SIZE + proximityIndex];
|
||||||
}
|
}
|
||||||
bool sameAsTyped(const unsigned short *word, int length) const;
|
bool sameAsTyped(const unsigned short *word, int length) const;
|
||||||
const unsigned short* getPrimaryInputWord() const {
|
const unsigned short* getPrimaryInputWord() const {
|
||||||
return mPrimaryInputWord;
|
return mPrimaryInputWord;
|
||||||
}
|
}
|
||||||
|
bool touchPositionCorrectionEnabled() const {
|
||||||
|
return mTouchPositionCorrectionEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
|
|
||||||
1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
|
|
||||||
// The max number of the keys in one keyboard layout
|
// The max number of the keys in one keyboard layout
|
||||||
static const int MAX_KEY_COUNT_IN_A_KEYBOARD = 64;
|
static const int MAX_KEY_COUNT_IN_A_KEYBOARD = 64;
|
||||||
// The upper limit of the char code in mCodeToKeyIndex
|
// The upper limit of the char code in mCodeToKeyIndex
|
||||||
|
@ -73,8 +76,15 @@ private:
|
||||||
|
|
||||||
int getStartIndexFromCoordinates(const int x, const int y) const;
|
int getStartIndexFromCoordinates(const int x, const int y) const;
|
||||||
void initializeCodeToKeyIndex();
|
void initializeCodeToKeyIndex();
|
||||||
float calculateNormalizedSquaredDistance(int index) const;
|
float calculateNormalizedSquaredDistance(const int keyIndex, const int inputIndex) const;
|
||||||
float calculateSquaredDistanceFromSweetSpotCenter(int keyIndex, int inputIndex) const;
|
float calculateSquaredDistanceFromSweetSpotCenter(
|
||||||
|
const int keyIndex, const int inputIndex) const;
|
||||||
|
int getKeyIndex(const int c) const;
|
||||||
|
bool hasSweetSpotData(const int keyIndex) const {
|
||||||
|
// When there are no calibration data for a key,
|
||||||
|
// the radius of the key is assigned to zero.
|
||||||
|
return mSweetSpotRadii[keyIndex] > 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
const int MAX_PROXIMITY_CHARS_SIZE;
|
const int MAX_PROXIMITY_CHARS_SIZE;
|
||||||
const int KEYBOARD_WIDTH;
|
const int KEYBOARD_WIDTH;
|
||||||
|
@ -84,10 +94,13 @@ private:
|
||||||
const int CELL_WIDTH;
|
const int CELL_WIDTH;
|
||||||
const int CELL_HEIGHT;
|
const int CELL_HEIGHT;
|
||||||
const int KEY_COUNT;
|
const int KEY_COUNT;
|
||||||
|
const bool HAS_TOUCH_POSITION_CORRECTION_DATA;
|
||||||
const int *mInputCodes;
|
const int *mInputCodes;
|
||||||
const int *mInputXCoordinates;
|
const int *mInputXCoordinates;
|
||||||
const int *mInputYCoordinates;
|
const int *mInputYCoordinates;
|
||||||
|
bool mTouchPositionCorrectionEnabled;
|
||||||
uint32_t *mProximityCharsArray;
|
uint32_t *mProximityCharsArray;
|
||||||
|
int *mNormalizedSquaredDistances;
|
||||||
int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
||||||
int32_t mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
int32_t mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
||||||
int32_t mKeyWidths[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
int32_t mKeyWidths[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
||||||
|
@ -96,7 +109,6 @@ private:
|
||||||
float mSweetSpotCenterXs[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
float mSweetSpotCenterXs[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
||||||
float mSweetSpotCenterYs[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
float mSweetSpotCenterYs[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
||||||
float mSweetSpotRadii[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
float mSweetSpotRadii[MAX_KEY_COUNT_IN_A_KEYBOARD];
|
||||||
int mNormalizedSquaredDistance[MAX_WORD_LENGTH_INTERNAL];
|
|
||||||
int mInputLength;
|
int mInputLength;
|
||||||
unsigned short mPrimaryInputWord[MAX_WORD_LENGTH_INTERNAL];
|
unsigned short mPrimaryInputWord[MAX_WORD_LENGTH_INTERNAL];
|
||||||
int mCodeToKeyIndex[MAX_CHAR_CODE + 1];
|
int mCodeToKeyIndex[MAX_CHAR_CODE + 1];
|
||||||
|
|
Loading…
Reference in New Issue