diff options
Diffstat (limited to 'native/jni/src/proximity_info_state.cpp')
-rw-r--r-- | native/jni/src/proximity_info_state.cpp | 680 |
1 files changed, 48 insertions, 632 deletions
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp index 31b6e4baf..e720275d0 100644 --- a/native/jni/src/proximity_info_state.cpp +++ b/native/jni/src/proximity_info_state.cpp @@ -27,15 +27,7 @@ namespace latinime { -const int ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10; -const int ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR = - 1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2; -const float ProximityInfoState::NOT_A_DISTANCE_FLOAT = -1.0f; const int ProximityInfoState::NOT_A_CODE = -1; -const int ProximityInfoState::LOOKUP_RADIUS_PERCENTILE = 50; -const int ProximityInfoState::FIRST_POINT_TIME_OFFSET_MILLIS = 150; -const int ProximityInfoState::STRONG_DOUBLE_LETTER_TIME_MILLIS = 600; -const int ProximityInfoState::MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE = 5; void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength, const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize, @@ -65,10 +57,10 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi int pushTouchPointStartIndex = 0; int lastSavedInputSize = 0; mMaxPointToKeyLength = maxPointToKeyLength; - if (mIsContinuationPossible && mInputIndice.size() > 1) { + if (mIsContinuationPossible && mSampledInputIndice.size() > 1) { // Just update difference. // Two points prior is never skipped. Thus, we pop 2 input point data here. - pushTouchPointStartIndex = mInputIndice[mInputIndice.size() - 2]; + pushTouchPointStartIndex = mSampledInputIndice[mSampledInputIndice.size() - 2]; popInputData(); popInputData(); lastSavedInputSize = mSampledInputXs.size(); @@ -76,9 +68,9 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi // Clear all data. mSampledInputXs.clear(); mSampledInputYs.clear(); - mTimes.clear(); - mInputIndice.clear(); - mLengthCache.clear(); + mSampledTimes.clear(); + mSampledInputIndice.clear(); + mSampledLengthCache.clear(); mDistanceCache_G.clear(); mNearKeysVector.clear(); mSearchKeysVector.clear(); @@ -97,92 +89,42 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints( mProximityInfo->getMostCommonKeyWidth(), mProximityInfo, mMaxPointToKeyLength, mInputProximities, xCoordinates, yCoordinates, times, pointerIds, inputSize, - isGeometric, pointerId, pushTouchPointStartIndex, - &mSampledInputXs, &mSampledInputYs, &mTimes, &mLengthCache, &mInputIndice); + isGeometric, pointerId, pushTouchPointStartIndex, &mSampledInputXs, + &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice); } if (mSampledInputSize > 0 && isGeometric) { - refreshSpeedRates(inputSize, xCoordinates, yCoordinates, times, lastSavedInputSize); - refreshBeelineSpeedRates(inputSize, xCoordinates, yCoordinates, times); - } - - if (DEBUG_GEO_FULL) { - for (int i = 0; i < mSampledInputSize; ++i) { - AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, mSampledInputXs[i], - mSampledInputYs[i], mTimes[i]); - } + mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates( + inputSize, xCoordinates, yCoordinates, times, lastSavedInputSize, + mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, + &mSampledLengthCache, &mSampledInputIndice, &mSpeedRates, &mDirections); + ProximityInfoStateUtils::refreshBeelineSpeedRates( + mProximityInfo->getMostCommonKeyWidth(), mAverageSpeed, inputSize, + xCoordinates, yCoordinates, times, mSampledInputSize, &mSampledInputXs, + &mSampledInputYs, &mSampledInputIndice, &mBeelineSpeedPercentiles); } if (mSampledInputSize > 0) { - const int keyCount = mProximityInfo->getKeyCount(); - mNearKeysVector.resize(mSampledInputSize); - mSearchKeysVector.resize(mSampledInputSize); - mDistanceCache_G.resize(mSampledInputSize * keyCount); - for (int i = lastSavedInputSize; i < mSampledInputSize; ++i) { - mNearKeysVector[i].reset(); - mSearchKeysVector[i].reset(); - static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f; - for (int k = 0; k < keyCount; ++k) { - const int index = i * keyCount + k; - const int x = mSampledInputXs[i]; - const int y = mSampledInputYs[i]; - const float normalizedSquaredDistance = - mProximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y); - mDistanceCache_G[index] = normalizedSquaredDistance; - if (normalizedSquaredDistance < NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) { - mNearKeysVector[i][k] = true; - } - } - } + ProximityInfoStateUtils::initGeometricDistanceInfos( + mProximityInfo, mProximityInfo->getKeyCount(), + mSampledInputSize, lastSavedInputSize, &mSampledInputXs, &mSampledInputYs, + &mNearKeysVector, &mSearchKeysVector, &mDistanceCache_G); if (isGeometric) { // updates probabilities of skipping or mapping each key for all points. - updateAlignPointProbabilities(lastSavedInputSize); - - static const float READ_FORWORD_LENGTH_SCALE = 0.95f; - const int readForwordLength = static_cast<int>( - hypotf(mProximityInfo->getKeyboardWidth(), mProximityInfo->getKeyboardHeight()) - * READ_FORWORD_LENGTH_SCALE); - for (int i = 0; i < mSampledInputSize; ++i) { - if (i >= lastSavedInputSize) { - mSearchKeysVector[i].reset(); - } - for (int j = max(i, lastSavedInputSize); j < mSampledInputSize; ++j) { - if (mLengthCache[j] - mLengthCache[i] >= readForwordLength) { - break; - } - mSearchKeysVector[i] |= mNearKeysVector[j]; - } - } + ProximityInfoStateUtils::updateAlignPointProbabilities( + mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(), + mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize, + &mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache, + &mDistanceCache_G, &mNearKeysVector, &mCharProbabilities); + ProximityInfoStateUtils::updateSearchKeysVector(mProximityInfo, mSampledInputSize, + lastSavedInputSize, &mSampledLengthCache, &mNearKeysVector, &mSearchKeysVector); } } if (DEBUG_SAMPLING_POINTS) { - std::stringstream originalX, originalY, sampledX, sampledY; - for (int i = 0; i < inputSize; ++i) { - originalX << xCoordinates[i]; - originalY << yCoordinates[i]; - if (i != inputSize - 1) { - originalX << ";"; - originalY << ";"; - } - } - AKLOGI("===== sampled points ====="); - for (int i = 0; i < mSampledInputSize; ++i) { - if (isGeometric) { - AKLOGI("%d: x = %d, y = %d, time = %d, relative speed = %.4f, beeline speed = %d", - i, mSampledInputXs[i], mSampledInputYs[i], mTimes[i], mSpeedRates[i], - getBeelineSpeedPercentile(i)); - } - sampledX << mSampledInputXs[i]; - sampledY << mSampledInputYs[i]; - if (i != mSampledInputSize - 1) { - sampledX << ";"; - sampledY << ";"; - } - } - AKLOGI("original points:\n%s, %s,\nsampled points:\n%s, %s,\n", - originalX.str().c_str(), originalY.str().c_str(), sampledX.str().c_str(), - sampledY.str().c_str()); + ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates, + mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates, + &mBeelineSpeedPercentiles); } // end /////////////////////// @@ -192,200 +134,28 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData && xCoordinates && yCoordinates; if (!isGeometric && pointerId == 0) { - for (int i = 0; i < inputSize; ++i) { - mPrimaryInputWord[i] = getPrimaryCodePointAt(i); - } - - for (int i = 0; i < mSampledInputSize && mTouchPositionCorrectionEnabled; ++i) { - const int *proximityCodePoints = getProximityCodePointsAt(i); - const int primaryKey = proximityCodePoints[0]; - const int x = xCoordinates[i]; - const int y = yCoordinates[i]; - if (DEBUG_PROXIMITY_CHARS) { - int a = x + y + primaryKey; - a += 0; - AKLOGI("--- Primary = %c, x = %d, y = %d", primaryKey, x, y); - } - for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL && proximityCodePoints[j] > 0; - ++j) { - const int currentCodePoint = proximityCodePoints[j]; - const float squaredDistance = - hasInputCoordinates() ? calculateNormalizedSquaredDistance( - mProximityInfo->getKeyIndexOf(currentCodePoint), i) : - NOT_A_DISTANCE_FLOAT; - if (squaredDistance >= 0.0f) { - mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] = - (int) (squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR); - } else { - mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] = - (j == 0) ? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO : - PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO; - } - if (DEBUG_PROXIMITY_CHARS) { - AKLOGI("--- Proximity (%d) = %c", j, currentCodePoint); - } - } + ProximityInfoStateUtils::initPrimaryInputWord( + inputSize, mInputProximities, mPrimaryInputWord); + if (mTouchPositionCorrectionEnabled) { + ProximityInfoStateUtils::initNormalizedSquaredDistances( + mProximityInfo, inputSize, xCoordinates, yCoordinates, mInputProximities, + hasInputCoordinates(), &mSampledInputXs, &mSampledInputYs, + mNormalizedSquaredDistances); } } - if (DEBUG_GEO_FULL) { AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize); } } -void ProximityInfoState::refreshSpeedRates(const int inputSize, const int *const xCoordinates, - const int *const yCoordinates, const int *const times, const int lastSavedInputSize) { - // Relative speed calculation. - const int sumDuration = mTimes.back() - mTimes.front(); - const int sumLength = mLengthCache.back() - mLengthCache.front(); - mAverageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration); - mSpeedRates.resize(mSampledInputSize); - for (int i = lastSavedInputSize; i < mSampledInputSize; ++i) { - const int index = mInputIndice[i]; - int length = 0; - int duration = 0; - - // Calculate velocity by using distances and durations of - // NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and backward. - static const int NUM_POINTS_FOR_SPEED_CALCULATION = 2; - for (int j = index; j < min(inputSize - 1, index + NUM_POINTS_FOR_SPEED_CALCULATION); - ++j) { - if (i < mSampledInputSize - 1 && j >= mInputIndice[i + 1]) { - break; - } - length += getDistanceInt(xCoordinates[j], yCoordinates[j], - xCoordinates[j + 1], yCoordinates[j + 1]); - duration += times[j + 1] - times[j]; - } - for (int j = index - 1; j >= max(0, index - NUM_POINTS_FOR_SPEED_CALCULATION); --j) { - if (i > 0 && j < mInputIndice[i - 1]) { - break; - } - // TODO: use mLengthCache instead? - length += getDistanceInt(xCoordinates[j], yCoordinates[j], - xCoordinates[j + 1], yCoordinates[j + 1]); - duration += times[j + 1] - times[j]; - } - if (duration == 0 || sumDuration == 0) { - // Cannot calculate speed; thus, it gives an average value (1.0); - mSpeedRates[i] = 1.0f; - } else { - const float speed = static_cast<float>(length) / static_cast<float>(duration); - mSpeedRates[i] = speed / mAverageSpeed; - } - } - - // Direction calculation. - mDirections.resize(mSampledInputSize - 1); - for (int i = max(0, lastSavedInputSize - 1); i < mSampledInputSize - 1; ++i) { - mDirections[i] = getDirection(i, i + 1); - } -} - -static const int MAX_PERCENTILE = 100; -void ProximityInfoState::refreshBeelineSpeedRates(const int inputSize, - const int *const xCoordinates, const int *const yCoordinates, const int * times) { - if (DEBUG_SAMPLING_POINTS){ - AKLOGI("--- refresh beeline speed rates"); - } - mBeelineSpeedPercentiles.resize(mSampledInputSize); - for (int i = 0; i < mSampledInputSize; ++i) { - mBeelineSpeedPercentiles[i] = static_cast<int>(calculateBeelineSpeedRate( - i, inputSize, xCoordinates, yCoordinates, times) * MAX_PERCENTILE); - } -} - -float ProximityInfoState::calculateBeelineSpeedRate( - const int id, const int inputSize, const int *const xCoordinates, - const int *const yCoordinates, const int * times) const { - if (mSampledInputSize <= 0 || mAverageSpeed < 0.001f) { - if (DEBUG_SAMPLING_POINTS){ - AKLOGI("--- invalid state: cancel. size = %d, ave = %f", - mSampledInputSize, mAverageSpeed); - } - return 1.0f; - } - const int lookupRadius = - mProximityInfo->getMostCommonKeyWidth() * LOOKUP_RADIUS_PERCENTILE / MAX_PERCENTILE; - const int x0 = mSampledInputXs[id]; - const int y0 = mSampledInputYs[id]; - const int actualInputIndex = mInputIndice[id]; - int tempTime = 0; - int tempBeelineDistance = 0; - int start = actualInputIndex; - // lookup forward - while (start > 0 && tempBeelineDistance < lookupRadius) { - tempTime += times[start] - times[start - 1]; - --start; - tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[start], yCoordinates[start]); - } - // Exclusive unless this is an edge point - if (start > 0 && start < actualInputIndex) { - ++start; - } - tempTime= 0; - tempBeelineDistance = 0; - int end = actualInputIndex; - // lookup backward - while (end < (inputSize - 1) && tempBeelineDistance < lookupRadius) { - tempTime += times[end + 1] - times[end]; - ++end; - tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[end], yCoordinates[end]); - } - // Exclusive unless this is an edge point - if (end > actualInputIndex && end < (inputSize - 1)) { - --end; - } - - if (start >= end) { - if (DEBUG_DOUBLE_LETTER) { - AKLOGI("--- double letter: start == end %d", start); - } - return 1.0f; - } - - const int x2 = xCoordinates[start]; - const int y2 = yCoordinates[start]; - const int x3 = xCoordinates[end]; - const int y3 = yCoordinates[end]; - const int beelineDistance = getDistanceInt(x2, y2, x3, y3); - int adjustedStartTime = times[start]; - if (start == 0 && actualInputIndex == 0 && inputSize > 1) { - adjustedStartTime += FIRST_POINT_TIME_OFFSET_MILLIS; - } - int adjustedEndTime = times[end]; - if (end == (inputSize - 1) && inputSize > 1) { - adjustedEndTime -= FIRST_POINT_TIME_OFFSET_MILLIS; - } - const int time = adjustedEndTime - adjustedStartTime; - if (time <= 0) { - return 1.0f; - } - - if (time >= STRONG_DOUBLE_LETTER_TIME_MILLIS){ - return 0.0f; - } - if (DEBUG_DOUBLE_LETTER) { - AKLOGI("--- (%d, %d) double letter: start = %d, end = %d, dist = %d, time = %d, speed = %f," - " ave = %f, val = %f, start time = %d, end time = %d", - id, mInputIndice[id], start, end, beelineDistance, time, - (static_cast<float>(beelineDistance) / static_cast<float>(time)), mAverageSpeed, - ((static_cast<float>(beelineDistance) / static_cast<float>(time)) / mAverageSpeed), - adjustedStartTime, adjustedEndTime); - } - // Offset 1% - // TODO: Detect double letter more smartly - return 0.01f + static_cast<float>(beelineDistance) / static_cast<float>(time) / mAverageSpeed; -} - bool ProximityInfoState::checkAndReturnIsContinuationPossible(const int inputSize, const int *const xCoordinates, const int *const yCoordinates, const int *const times, const bool isGeometric) const { if (isGeometric) { for (int i = 0; i < mSampledInputSize; ++i) { - const int index = mInputIndice[i]; + const int index = mSampledInputIndice[i]; if (index > inputSize || xCoordinates[index] != mSampledInputXs[i] || - yCoordinates[index] != mSampledInputYs[i] || times[index] != mTimes[i]) { + yCoordinates[index] != mSampledInputYs[i] || times[index] != mSampledTimes[i]) { return false; } } @@ -404,26 +174,9 @@ bool ProximityInfoState::checkAndReturnIsContinuationPossible(const int inputSiz return true; } -float ProximityInfoState::calculateNormalizedSquaredDistance( - const int keyIndex, const int inputIndex) const { - if (keyIndex == NOT_AN_INDEX) { - return NOT_A_DISTANCE_FLOAT; - } - if (!mProximityInfo->hasSweetSpotData(keyIndex)) { - return NOT_A_DISTANCE_FLOAT; - } - if (NOT_A_COORDINATE == mSampledInputXs[inputIndex]) { - return NOT_A_DISTANCE_FLOAT; - } - const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter( - keyIndex, inputIndex); - const float squaredRadius = square(mProximityInfo->getSweetSpotRadiiAt(keyIndex)); - return squaredDistance / squaredRadius; -} - int ProximityInfoState::getDuration(const int index) const { if (index >= 0 && index < mSampledInputSize - 1) { - return mTimes[index + 1] - mTimes[index]; + return mSampledTimes[index + 1] - mSampledTimes[index]; } return 0; } @@ -450,16 +203,10 @@ float ProximityInfoState::getPointToKeyLength_G(const int inputIndex, const int } // TODO: Remove the "scale" parameter -// This function basically converts from a length to an edit distance. Accordingly, it's obviously -// wrong to compare with mMaxPointToKeyLength. float ProximityInfoState::getPointToKeyByIdLength( const int inputIndex, const int keyId, const float scale) const { - if (keyId != NOT_AN_INDEX) { - const int index = inputIndex * mProximityInfo->getKeyCount() + keyId; - return min(mDistanceCache_G[index] * scale, mMaxPointToKeyLength); - } - // If the char is not a key on the keyboard then return the max length. - return static_cast<float>(MAX_POINT_TO_KEY_LENGTH); + return ProximityInfoStateUtils::getPointToKeyByIdLength(mMaxPointToKeyLength, + &mDistanceCache_G, mProximityInfo->getKeyCount(), inputIndex, keyId, scale); } float ProximityInfoState::getPointToKeyByIdLength(const int inputIndex, const int keyId) const { @@ -498,7 +245,7 @@ ProximityType ProximityInfoState::getMatchedProximityId(const int index, const i // Not an exact nor an accent-alike match: search the list of close keys int j = 1; - while (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL + while (j < MAX_PROXIMITY_CHARS_SIZE && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { const bool matched = (currentCodePoints[j] == baseLowerC || currentCodePoints[j] == c); if (matched) { @@ -509,10 +256,10 @@ ProximityType ProximityInfoState::getMatchedProximityId(const int index, const i } ++j; } - if (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL + if (j < MAX_PROXIMITY_CHARS_SIZE && currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { ++j; - while (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL + while (j < MAX_PROXIMITY_CHARS_SIZE && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { const bool matched = (currentCodePoints[j] == baseLowerC || currentCodePoints[j] == c); if (matched) { @@ -533,15 +280,6 @@ int ProximityInfoState::getSpaceY() const { return mProximityInfo->getKeyCenterYOfKeyIdG(keyId); } -float ProximityInfoState::calculateSquaredDistanceFromSweetSpotCenter( - const int keyIndex, const int inputIndex) const { - const float sweetSpotCenterX = mProximityInfo->getSweetSpotCenterXAt(keyIndex); - const float sweetSpotCenterY = mProximityInfo->getSweetSpotCenterYAt(keyIndex); - const float inputX = static_cast<float>(mSampledInputXs[inputIndex]); - const float inputY = static_cast<float>(mSampledInputYs[inputIndex]); - return square(inputX - sweetSpotCenterX) + square(inputY - sweetSpotCenterY); -} - // Puts possible characters into filter and returns new filter size. int ProximityInfoState::getAllPossibleChars( const size_t index, int *const filter, const int filterSize) const { @@ -576,48 +314,13 @@ bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int k } void ProximityInfoState::popInputData() { - ProximityInfoStateUtils::popInputData(&mSampledInputXs, &mSampledInputYs, &mTimes, - &mLengthCache, &mInputIndice); + ProximityInfoStateUtils::popInputData(&mSampledInputXs, &mSampledInputYs, &mSampledTimes, + &mSampledLengthCache, &mSampledInputIndice); } float ProximityInfoState::getDirection(const int index0, const int index1) const { - if (index0 < 0 || index0 > mSampledInputSize - 1) { - return 0.0f; - } - if (index1 < 0 || index1 > mSampledInputSize - 1) { - return 0.0f; - } - const int x1 = mSampledInputXs[index0]; - const int y1 = mSampledInputYs[index0]; - const int x2 = mSampledInputXs[index1]; - const int y2 = mSampledInputYs[index1]; - return getAngle(x1, y1, x2, y2); -} - -float ProximityInfoState::getPointAngle(const int index) const { - if (index <= 0 || index >= mSampledInputSize - 1) { - return 0.0f; - } - const float previousDirection = getDirection(index - 1, index); - const float nextDirection = getDirection(index, index + 1); - const float directionDiff = getAngleDiff(previousDirection, nextDirection); - return directionDiff; -} - -float ProximityInfoState::getPointsAngle( - const int index0, const int index1, const int index2) const { - if (index0 < 0 || index0 > mSampledInputSize - 1) { - return 0.0f; - } - if (index1 < 0 || index1 > mSampledInputSize - 1) { - return 0.0f; - } - if (index2 < 0 || index2 > mSampledInputSize - 1) { - return 0.0f; - } - const float previousDirection = getDirection(index0, index1); - const float nextDirection = getDirection(index1, index2); - return getAngleDiff(previousDirection, nextDirection); + return ProximityInfoStateUtils::getDirection( + &mSampledInputXs, &mSampledInputYs, index0, index1); } float ProximityInfoState::getLineToKeyDistance( @@ -640,293 +343,6 @@ float ProximityInfoState::getLineToKeyDistance( keyX, keyY, x0, y0, x1, y1, extend); } -// Updates probabilities of aligning to some keys and skipping. -// Word suggestion should be based on this probabilities. -void ProximityInfoState::updateAlignPointProbabilities(const int start) { - static const float MIN_PROBABILITY = 0.000001f; - static const float MAX_SKIP_PROBABILITY = 0.95f; - static const float SKIP_FIRST_POINT_PROBABILITY = 0.01f; - static const float SKIP_LAST_POINT_PROBABILITY = 0.1f; - static const float MIN_SPEED_RATE_FOR_SKIP_PROBABILITY = 0.15f; - static const float SPEED_WEIGHT_FOR_SKIP_PROBABILITY = 0.9f; - static const float SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY = 0.6f; - static const float NEAREST_DISTANCE_WEIGHT = 0.5f; - static const float NEAREST_DISTANCE_BIAS = 0.5f; - static const float NEAREST_DISTANCE_WEIGHT_FOR_LAST = 0.6f; - static const float NEAREST_DISTANCE_BIAS_FOR_LAST = 0.4f; - - static const float ANGLE_WEIGHT = 0.90f; - static const float DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 60.0f / 180.0f; - static const float SKIP_DEEP_CORNER_PROBABILITY = 0.1f; - static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 30.0f / 180.0f; - static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f; - static const float SKIP_CORNER_PROBABILITY = 0.4f; - static const float SPEED_MARGIN = 0.1f; - static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f; - - const int keyCount = mProximityInfo->getKeyCount(); - mCharProbabilities.resize(mSampledInputSize); - // Calculates probabilities of using a point as a correlated point with the character - // for each point. - for (int i = start; i < mSampledInputSize; ++i) { - mCharProbabilities[i].clear(); - // First, calculates skip probability. Starts form MIN_SKIP_PROBABILITY. - // Note that all values that are multiplied to this probability should be in [0.0, 1.0]; - float skipProbability = MAX_SKIP_PROBABILITY; - - const float currentAngle = getPointAngle(i); - const float speedRate = getSpeedRate(i); - - float nearestKeyDistance = static_cast<float>(MAX_POINT_TO_KEY_LENGTH); - for (int j = 0; j < keyCount; ++j) { - if (mNearKeysVector[i].test(j)) { - const float distance = getPointToKeyByIdLength(i, j); - if (distance < nearestKeyDistance) { - nearestKeyDistance = distance; - } - } - } - - if (i == 0) { - skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT - + NEAREST_DISTANCE_BIAS); - // Promote the first point - skipProbability *= SKIP_FIRST_POINT_PROBABILITY; - } else if (i == mSampledInputSize - 1) { - skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT_FOR_LAST - + NEAREST_DISTANCE_BIAS_FOR_LAST); - // Promote the last point - skipProbability *= SKIP_LAST_POINT_PROBABILITY; - } else { - // If the current speed is relatively slower than adjacent keys, we promote this point. - if (getSpeedRate(i - 1) - SPEED_MARGIN > speedRate - && speedRate < getSpeedRate(i + 1) - SPEED_MARGIN) { - if (currentAngle < CORNER_ANGLE_THRESHOLD) { - skipProbability *= min(1.0f, speedRate - * SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY); - } else { - // If the angle is small enough, we promote this point more. (e.g. pit vs put) - skipProbability *= min(1.0f, speedRate * SPEED_WEIGHT_FOR_SKIP_PROBABILITY - + MIN_SPEED_RATE_FOR_SKIP_PROBABILITY); - } - } - - skipProbability *= min(1.0f, speedRate * nearestKeyDistance * - NEAREST_DISTANCE_WEIGHT + NEAREST_DISTANCE_BIAS); - - // Adjusts skip probability by a rate depending on angle. - // ANGLE_RATE of skipProbability is adjusted by current angle. - skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ANGLE_WEIGHT - + (1.0f - ANGLE_WEIGHT); - if (currentAngle > DEEP_CORNER_ANGLE_THRESHOLD) { - skipProbability *= SKIP_DEEP_CORNER_PROBABILITY; - } - // We assume the angle of this point is the angle for point[i], point[i - 2] - // and point[i - 3]. The reason why we don't use the angle for point[i], point[i - 1] - // and point[i - 2] is this angle can be more affected by the noise. - const float prevAngle = getPointsAngle(i, i - 2, i - 3); - if (i >= 3 && prevAngle < STRAIGHT_ANGLE_THRESHOLD - && currentAngle > CORNER_ANGLE_THRESHOLD) { - skipProbability *= SKIP_CORNER_PROBABILITY; - } - } - - // probabilities must be in [0.0, MAX_SKIP_PROBABILITY]; - ASSERT(skipProbability >= 0.0f); - ASSERT(skipProbability <= MAX_SKIP_PROBABILITY); - mCharProbabilities[i][NOT_AN_INDEX] = skipProbability; - - // Second, calculates key probabilities by dividing the rest probability - // (1.0f - skipProbability). - const float inputCharProbability = 1.0f - skipProbability; - - // TODO: The variance is critical for accuracy; thus, adjusting these parameter by machine - // learning or something would be efficient. - static const float SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION = 0.3f; - static const float MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION = 0.25f; - static const float SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION = 0.5f; - static const float MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION = 0.15f; - static const float MIN_STANDERD_DIVIATION = 0.37f; - - const float speedxAngleRate = min(speedRate * currentAngle / M_PI_F - * SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION, - MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION); - const float speedxNearestKeyDistanceRate = min(speedRate * nearestKeyDistance - * SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION, - MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION); - const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate + MIN_STANDERD_DIVIATION; - - ProximityInfoUtils::NormalDistribution - distribution(CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma); - static const float PREV_DISTANCE_WEIGHT = 0.5f; - static const float NEXT_DISTANCE_WEIGHT = 0.6f; - // Summing up probability densities of all near keys. - float sumOfProbabilityDensities = 0.0f; - for (int j = 0; j < keyCount; ++j) { - if (mNearKeysVector[i].test(j)) { - float distance = sqrtf(getPointToKeyByIdLength(i, j)); - if (i == 0 && i != mSampledInputSize - 1) { - // For the first point, weighted average of distances from first point and the - // next point to the key is used as a point to key distance. - const float nextDistance = sqrtf(getPointToKeyByIdLength(i + 1, j)); - if (nextDistance < distance) { - // The distance of the first point tends to bigger than continuing - // points because the first touch by the user can be sloppy. - // So we promote the first point if the distance of that point is larger - // than the distance of the next point. - distance = (distance + nextDistance * NEXT_DISTANCE_WEIGHT) - / (1.0f + NEXT_DISTANCE_WEIGHT); - } - } else if (i != 0 && i == mSampledInputSize - 1) { - // For the first point, weighted average of distances from last point and - // the previous point to the key is used as a point to key distance. - const float previousDistance = sqrtf(getPointToKeyByIdLength(i - 1, j)); - if (previousDistance < distance) { - // The distance of the last point tends to bigger than continuing points - // because the last touch by the user can be sloppy. So we promote the - // last point if the distance of that point is larger than the distance of - // the previous point. - distance = (distance + previousDistance * PREV_DISTANCE_WEIGHT) - / (1.0f + PREV_DISTANCE_WEIGHT); - } - } - // TODO: Promote the first point when the extended line from the next input is near - // from a key. Also, promote the last point as well. - sumOfProbabilityDensities += distribution.getProbabilityDensity(distance); - } - } - - // Split the probability of an input point to keys that are close to the input point. - for (int j = 0; j < keyCount; ++j) { - if (mNearKeysVector[i].test(j)) { - float distance = sqrtf(getPointToKeyByIdLength(i, j)); - if (i == 0 && i != mSampledInputSize - 1) { - // For the first point, weighted average of distances from the first point and - // the next point to the key is used as a point to key distance. - const float prevDistance = sqrtf(getPointToKeyByIdLength(i + 1, j)); - if (prevDistance < distance) { - distance = (distance + prevDistance * NEXT_DISTANCE_WEIGHT) - / (1.0f + NEXT_DISTANCE_WEIGHT); - } - } else if (i != 0 && i == mSampledInputSize - 1) { - // For the first point, weighted average of distances from last point and - // the previous point to the key is used as a point to key distance. - const float prevDistance = sqrtf(getPointToKeyByIdLength(i - 1, j)); - if (prevDistance < distance) { - distance = (distance + prevDistance * PREV_DISTANCE_WEIGHT) - / (1.0f + PREV_DISTANCE_WEIGHT); - } - } - const float probabilityDensity = distribution.getProbabilityDensity(distance); - const float probability = inputCharProbability * probabilityDensity - / sumOfProbabilityDensities; - mCharProbabilities[i][j] = probability; - } - } - } - - - if (DEBUG_POINTS_PROBABILITY) { - for (int i = 0; i < mSampledInputSize; ++i) { - std::stringstream sstream; - sstream << i << ", "; - sstream << "(" << mSampledInputXs[i] << ", " << mSampledInputYs[i] << "), "; - sstream << "Speed: "<< getSpeedRate(i) << ", "; - sstream << "Angle: "<< getPointAngle(i) << ", \n"; - - for (hash_map_compat<int, float>::iterator it = mCharProbabilities[i].begin(); - it != mCharProbabilities[i].end(); ++it) { - if (it->first == NOT_AN_INDEX) { - sstream << it->first - << "(skip):" - << it->second - << "\n"; - } else { - sstream << it->first - << "(" - << static_cast<char>(mProximityInfo->getCodePointOf(it->first)) - << "):" - << it->second - << "\n"; - } - } - AKLOGI("%s", sstream.str().c_str()); - } - } - - // Decrease key probabilities of points which don't have the highest probability of that key - // among nearby points. Probabilities of the first point and the last point are not suppressed. - for (int i = max(start, 1); i < mSampledInputSize; ++i) { - for (int j = i + 1; j < mSampledInputSize; ++j) { - if (!suppressCharProbabilities(i, j)) { - break; - } - } - for (int j = i - 1; j >= max(start, 0); --j) { - if (!suppressCharProbabilities(i, j)) { - break; - } - } - } - - // Converting from raw probabilities to log probabilities to calculate spatial distance. - for (int i = start; i < mSampledInputSize; ++i) { - for (int j = 0; j < keyCount; ++j) { - hash_map_compat<int, float>::iterator it = mCharProbabilities[i].find(j); - if (it == mCharProbabilities[i].end()){ - mNearKeysVector[i].reset(j); - } else if(it->second < MIN_PROBABILITY) { - // Erases from near keys vector because it has very low probability. - mNearKeysVector[i].reset(j); - mCharProbabilities[i].erase(j); - } else { - it->second = -logf(it->second); - } - } - mCharProbabilities[i][NOT_AN_INDEX] = -logf(mCharProbabilities[i][NOT_AN_INDEX]); - } -} - -// Decreases char probabilities of index0 by checking probabilities of a near point (index1) and -// increases char probabilities of index1 by checking probabilities of index0. -bool ProximityInfoState::suppressCharProbabilities(const int index0, const int index1) { - ASSERT(0 <= index0 && index0 < mSampledInputSize); - ASSERT(0 <= index1 && index1 < mSampledInputSize); - - static const float SUPPRESSION_LENGTH_WEIGHT = 1.5f; - static const float MIN_SUPPRESSION_RATE = 0.1f; - static const float SUPPRESSION_WEIGHT = 0.5f; - static const float SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN = 0.1f; - static const float SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN = 0.3f; - - const float keyWidthFloat = static_cast<float>(mProximityInfo->getMostCommonKeyWidth()); - const float diff = fabsf(static_cast<float>(mLengthCache[index0] - mLengthCache[index1])); - if (diff > keyWidthFloat * SUPPRESSION_LENGTH_WEIGHT) { - return false; - } - const float suppressionRate = MIN_SUPPRESSION_RATE - + diff / keyWidthFloat / SUPPRESSION_LENGTH_WEIGHT * SUPPRESSION_WEIGHT; - for (hash_map_compat<int, float>::iterator it = mCharProbabilities[index0].begin(); - it != mCharProbabilities[index0].end(); ++it) { - hash_map_compat<int, float>::iterator it2 = mCharProbabilities[index1].find(it->first); - if (it2 != mCharProbabilities[index1].end() && it->second < it2->second) { - const float newProbability = it->second * suppressionRate; - const float suppression = it->second - newProbability; - it->second = newProbability; - // mCharProbabilities[index0][NOT_AN_INDEX] is the probability of skipping this point. - mCharProbabilities[index0][NOT_AN_INDEX] += suppression; - - // Add the probability of the same key nearby index1 - const float probabilityGain = min(suppression * SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN, - mCharProbabilities[index1][NOT_AN_INDEX] - * SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN); - it2->second += probabilityGain; - mCharProbabilities[index1][NOT_AN_INDEX] -= probabilityGain; - } - } - return true; -} - // Get a word that is detected by tracing the most probable string into codePointBuf and // returns probability of generating the word. float ProximityInfoState::getMostProbableString(int *const codePointBuf) const { |