proximity_info_state.cpp revision a1d84bcf8ffd031c135b6f3f8c94b6732071849b
1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/* 2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Copyright (C) 2012 The Android Open Source Project 3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * you may not use this file except in compliance with the License. 6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * You may obtain a copy of the License at 7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and 14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License. 15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <cstring> // for memset() and memcpy() 18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <sstream> // for debug prints 19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <vector> 20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#define LOG_TAG "LatinIME: proximity_info_state.cpp" 22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 23f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes#include "defines.h" 24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include "geometry_utils.h" 25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include "proximity_info.h" 26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include "proximity_info_state.h" 27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include "proximity_info_state_utils.h" 28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 29f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughesnamespace latinime { 30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project// TODO: Remove the dependency of "isGeometric" 32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectvoid ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength, 33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize, 34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project const int *const xCoordinates, const int *const yCoordinates, const int *const times, 35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project const int *const pointerIds, const bool isGeometric) { 36f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes ASSERT(isGeometric || (inputSize < MAX_WORD_LENGTH)); 37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mIsContinuousSuggestionPossible = 38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible( 39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project inputSize, xCoordinates, yCoordinates, times, mSampledInputSize, 40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledInputIndice); 41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (DEBUG_DICT) { 42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project AKLOGI("isContinuousSuggestionPossible = %s", 43f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes (mIsContinuousSuggestionPossible ? "true" : "false")); 44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mProximityInfo = proximityInfo; 47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData(); 48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare(); 49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mKeyCount = proximityInfo->getKeyCount(); 50 mCellHeight = proximityInfo->getCellHeight(); 51 mCellWidth = proximityInfo->getCellWidth(); 52 mGridHeight = proximityInfo->getGridWidth(); 53 mGridWidth = proximityInfo->getGridHeight(); 54 55 memset(mInputProximities, 0, sizeof(mInputProximities)); 56 57 if (!isGeometric && pointerId == 0) { 58 mProximityInfo->initializeProximities(inputCodes, xCoordinates, yCoordinates, 59 inputSize, mInputProximities); 60 } 61 62 /////////////////////// 63 // Setup touch points 64 int pushTouchPointStartIndex = 0; 65 int lastSavedInputSize = 0; 66 mMaxPointToKeyLength = maxPointToKeyLength; 67 mSampledInputSize = 0; 68 mMostProbableStringProbability = 0.0f; 69 70 if (mIsContinuousSuggestionPossible && mSampledInputIndice.size() > 1) { 71 // Just update difference. 72 // Previous two points are never skipped. Thus, we pop 2 input point data here. 73 pushTouchPointStartIndex = ProximityInfoStateUtils::trimLastTwoTouchPoints( 74 &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, 75 &mSampledInputIndice); 76 lastSavedInputSize = mSampledInputXs.size(); 77 } else { 78 // Clear all data. 79 mSampledInputXs.clear(); 80 mSampledInputYs.clear(); 81 mSampledTimes.clear(); 82 mSampledInputIndice.clear(); 83 mSampledLengthCache.clear(); 84 mSampledDistanceCache_G.clear(); 85 mSampledNearKeySets.clear(); 86 mSampledSearchKeySets.clear(); 87 mSpeedRates.clear(); 88 mBeelineSpeedPercentiles.clear(); 89 mCharProbabilities.clear(); 90 mDirections.clear(); 91 } 92 93 if (DEBUG_GEO_FULL) { 94 AKLOGI("Init ProximityInfoState: reused points = %d, last input size = %d", 95 pushTouchPointStartIndex, lastSavedInputSize); 96 } 97 98 // TODO: Remove the dependency of "isGeometric" 99 const float verticalSweetSpotScale = isGeometric 100 ? ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G 101 : ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE; 102 103 if (xCoordinates && yCoordinates) { 104 mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(mProximityInfo, 105 mMaxPointToKeyLength, mInputProximities, xCoordinates, yCoordinates, times, 106 pointerIds, verticalSweetSpotScale, inputSize, isGeometric, pointerId, 107 pushTouchPointStartIndex, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, 108 &mSampledLengthCache, &mSampledInputIndice); 109 } 110 111 if (mSampledInputSize > 0 && isGeometric) { 112 mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(inputSize, xCoordinates, 113 yCoordinates, times, lastSavedInputSize, mSampledInputSize, &mSampledInputXs, 114 &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice, 115 &mSpeedRates, &mDirections); 116 ProximityInfoStateUtils::refreshBeelineSpeedRates(mProximityInfo->getMostCommonKeyWidth(), 117 mAverageSpeed, inputSize, xCoordinates, yCoordinates, times, mSampledInputSize, 118 &mSampledInputXs, &mSampledInputYs, &mSampledInputIndice, 119 &mBeelineSpeedPercentiles); 120 } 121 122 if (mSampledInputSize > 0) { 123 ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize, 124 lastSavedInputSize, verticalSweetSpotScale, &mSampledInputXs, &mSampledInputYs, 125 &mSampledNearKeySets, &mSampledDistanceCache_G); 126 if (isGeometric) { 127 // updates probabilities of skipping or mapping each key for all points. 128 ProximityInfoStateUtils::updateAlignPointProbabilities( 129 mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(), 130 mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize, 131 &mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache, 132 &mSampledDistanceCache_G, &mSampledNearKeySets, &mCharProbabilities); 133 ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo, 134 mSampledInputSize, lastSavedInputSize, &mSampledLengthCache, 135 &mSampledNearKeySets, &mSampledSearchKeySets, 136 &mSampledSearchKeyVectors); 137 mMostProbableStringProbability = ProximityInfoStateUtils::getMostProbableString( 138 mProximityInfo, mSampledInputSize, &mCharProbabilities, mMostProbableString); 139 140 } 141 } 142 143 if (DEBUG_SAMPLING_POINTS) { 144 ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates, 145 mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates, 146 &mBeelineSpeedPercentiles); 147 } 148 // end 149 /////////////////////// 150 151 mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData 152 && xCoordinates && yCoordinates; 153 if (!isGeometric && pointerId == 0) { 154 ProximityInfoStateUtils::initPrimaryInputWord( 155 inputSize, mInputProximities, mPrimaryInputWord); 156 if (mTouchPositionCorrectionEnabled) { 157 ProximityInfoStateUtils::initNormalizedSquaredDistances( 158 mProximityInfo, inputSize, xCoordinates, yCoordinates, mInputProximities, 159 &mSampledInputXs, &mSampledInputYs, mNormalizedSquaredDistances); 160 } 161 } 162 if (DEBUG_GEO_FULL) { 163 AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize); 164 } 165} 166 167// This function basically converts from a length to an edit distance. Accordingly, it's obviously 168// wrong to compare with mMaxPointToKeyLength. 169float ProximityInfoState::getPointToKeyLength( 170 const int inputIndex, const int codePoint) const { 171 const int keyId = mProximityInfo->getKeyIndexOf(codePoint); 172 if (keyId != NOT_AN_INDEX) { 173 const int index = inputIndex * mProximityInfo->getKeyCount() + keyId; 174 return min(mSampledDistanceCache_G[index], mMaxPointToKeyLength); 175 } 176 if (isIntentionalOmissionCodePoint(codePoint)) { 177 return 0.0f; 178 } 179 // If the char is not a key on the keyboard then return the max length. 180 return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); 181} 182 183float ProximityInfoState::getPointToKeyByIdLength( 184 const int inputIndex, const int keyId) const { 185 return ProximityInfoStateUtils::getPointToKeyByIdLength(mMaxPointToKeyLength, 186 &mSampledDistanceCache_G, mProximityInfo->getKeyCount(), inputIndex, keyId); 187} 188 189// In the following function, c is the current character of the dictionary word currently examined. 190// currentChars is an array containing the keys close to the character the user actually typed at 191// the same position. We want to see if c is in it: if so, then the word contains at that position 192// a character close to what the user typed. 193// What the user typed is actually the first character of the array. 194// proximityIndex is a pointer to the variable where getProximityType returns the index of c 195// in the proximity chars of the input index. 196// Notice : accented characters do not have a proximity list, so they are alone in their list. The 197// non-accented version of the character should be considered "close", but not the other keys close 198// to the non-accented version. 199ProximityType ProximityInfoState::getProximityType(const int index, const int codePoint, 200 const bool checkProximityChars, int *proximityIndex) const { 201 const int *currentCodePoints = getProximityCodePointsAt(index); 202 const int firstCodePoint = currentCodePoints[0]; 203 const int baseLowerC = toBaseLowerCase(codePoint); 204 205 // The first char in the array is what user typed. If it matches right away, that means the 206 // user typed that same char for this pos. 207 if (firstCodePoint == baseLowerC || firstCodePoint == codePoint) { 208 return MATCH_CHAR; 209 } 210 211 if (!checkProximityChars) return SUBSTITUTION_CHAR; 212 213 // If the non-accented, lowercased version of that first character matches c, then we have a 214 // non-accented version of the accented character the user typed. Treat it as a close char. 215 if (toBaseLowerCase(firstCodePoint) == baseLowerC) { 216 return PROXIMITY_CHAR; 217 } 218 219 // Not an exact nor an accent-alike match: search the list of close keys 220 int j = 1; 221 while (j < MAX_PROXIMITY_CHARS_SIZE 222 && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { 223 const bool matched = (currentCodePoints[j] == baseLowerC 224 || currentCodePoints[j] == codePoint); 225 if (matched) { 226 if (proximityIndex) { 227 *proximityIndex = j; 228 } 229 return PROXIMITY_CHAR; 230 } 231 ++j; 232 } 233 if (j < MAX_PROXIMITY_CHARS_SIZE 234 && currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { 235 ++j; 236 while (j < MAX_PROXIMITY_CHARS_SIZE 237 && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { 238 const bool matched = (currentCodePoints[j] == baseLowerC 239 || currentCodePoints[j] == codePoint); 240 if (matched) { 241 if (proximityIndex) { 242 *proximityIndex = j; 243 } 244 return ADDITIONAL_PROXIMITY_CHAR; 245 } 246 ++j; 247 } 248 } 249 // Was not included, signal this as a substitution character. 250 return SUBSTITUTION_CHAR; 251} 252 253ProximityType ProximityInfoState::getProximityTypeG(const int index, const int codePoint) const { 254 if (!isUsed()) { 255 return UNRELATED_CHAR; 256 } 257 const int lowerCodePoint = toLowerCase(codePoint); 258 const int baseLowerCodePoint = toBaseCodePoint(lowerCodePoint); 259 for (int i = 0; i < static_cast<int>(mSampledSearchKeyVectors[index].size()); ++i) { 260 if (mSampledSearchKeyVectors[index][i] == lowerCodePoint 261 || mSampledSearchKeyVectors[index][i] == baseLowerCodePoint) { 262 return MATCH_CHAR; 263 } 264 } 265 return UNRELATED_CHAR; 266} 267 268bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const { 269 ASSERT(keyId >= 0 && index >= 0 && index < mSampledInputSize); 270 return mSampledSearchKeySets[index].test(keyId); 271} 272 273float ProximityInfoState::getDirection(const int index0, const int index1) const { 274 return ProximityInfoStateUtils::getDirection( 275 &mSampledInputXs, &mSampledInputYs, index0, index1); 276} 277 278float ProximityInfoState::getLineToKeyDistance( 279 const int from, const int to, const int keyId, const bool extend) const { 280 if (from < 0 || from > mSampledInputSize - 1) { 281 return 0.0f; 282 } 283 if (to < 0 || to > mSampledInputSize - 1) { 284 return 0.0f; 285 } 286 const int x0 = mSampledInputXs[from]; 287 const int y0 = mSampledInputYs[from]; 288 const int x1 = mSampledInputXs[to]; 289 const int y1 = mSampledInputYs[to]; 290 291 const int keyX = mProximityInfo->getKeyCenterXOfKeyIdG(keyId); 292 const int keyY = mProximityInfo->getKeyCenterYOfKeyIdG(keyId); 293 294 return ProximityInfoUtils::pointToLineSegSquaredDistanceFloat( 295 keyX, keyY, x0, y0, x1, y1, extend); 296} 297 298float ProximityInfoState::getMostProbableString(int *const codePointBuf) const { 299 memcpy(codePointBuf, mMostProbableString, sizeof(mMostProbableString)); 300 return mMostProbableStringProbability; 301} 302 303bool ProximityInfoState::hasSpaceProximity(const int index) const { 304 ASSERT(0 <= index && index < mSampledInputSize); 305 return mProximityInfo->hasSpaceProximity(getInputX(index), getInputY(index)); 306} 307 308// Returns a probability of mapping index to keyIndex. 309float ProximityInfoState::getProbability(const int index, const int keyIndex) const { 310 ASSERT(0 <= index && index < mSampledInputSize); 311 hash_map_compat<int, float>::const_iterator it = mCharProbabilities[index].find(keyIndex); 312 if (it != mCharProbabilities[index].end()) { 313 return it->second; 314 } 315 return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); 316} 317} // namespace latinime 318