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