proximity_info_state.cpp revision 2fa3693c264a4c150ac307d9bb7f6f8f18cc4ffc
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 31namespace latinime { 32 33int ProximityInfoState::getPrimaryOriginalCodePointAt(const int index) const { 34 const int primaryCodePoint = getPrimaryCodePointAt(index); 35 const int keyIndex = mProximityInfo->getKeyIndexOf(primaryCodePoint); 36 return mProximityInfo->getOriginalCodePointOf(keyIndex); 37} 38 39// TODO: Remove the dependency of "isGeometric" 40void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength, 41 const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize, 42 const int *const xCoordinates, const int *const yCoordinates, const int *const times, 43 const int *const pointerIds, const bool isGeometric) { 44 ASSERT(isGeometric || (inputSize < MAX_WORD_LENGTH)); 45 mIsContinuousSuggestionPossible = (mHasBeenUpdatedByGeometricInput != isGeometric) ? 46 false : ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible( 47 inputSize, xCoordinates, yCoordinates, times, mSampledInputSize, 48 &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledInputIndice); 49 if (DEBUG_DICT) { 50 AKLOGI("isContinuousSuggestionPossible = %s", 51 (mIsContinuousSuggestionPossible ? "true" : "false")); 52 } 53 54 mProximityInfo = proximityInfo; 55 mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData(); 56 mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare(); 57 mKeyCount = proximityInfo->getKeyCount(); 58 mCellHeight = proximityInfo->getCellHeight(); 59 mCellWidth = proximityInfo->getCellWidth(); 60 mGridHeight = proximityInfo->getGridWidth(); 61 mGridWidth = proximityInfo->getGridHeight(); 62 63 memset(mInputProximities, 0, sizeof(mInputProximities)); 64 65 if (!isGeometric && pointerId == 0) { 66 mProximityInfo->initializeProximities(inputCodes, xCoordinates, yCoordinates, 67 inputSize, mInputProximities); 68 } 69 70 /////////////////////// 71 // Setup touch points 72 int pushTouchPointStartIndex = 0; 73 int lastSavedInputSize = 0; 74 mMaxPointToKeyLength = maxPointToKeyLength; 75 mSampledInputSize = 0; 76 mMostProbableStringProbability = 0.0f; 77 78 if (mIsContinuousSuggestionPossible && mSampledInputIndice.size() > 1) { 79 // Just update difference. 80 // Previous two points are never skipped. Thus, we pop 2 input point data here. 81 pushTouchPointStartIndex = ProximityInfoStateUtils::trimLastTwoTouchPoints( 82 &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, 83 &mSampledInputIndice); 84 lastSavedInputSize = mSampledInputXs.size(); 85 } else { 86 // Clear all data. 87 mSampledInputXs.clear(); 88 mSampledInputYs.clear(); 89 mSampledTimes.clear(); 90 mSampledInputIndice.clear(); 91 mSampledLengthCache.clear(); 92 mSampledNormalizedSquaredLengthCache.clear(); 93 mSampledNearKeySets.clear(); 94 mSampledSearchKeySets.clear(); 95 mSpeedRates.clear(); 96 mBeelineSpeedPercentiles.clear(); 97 mCharProbabilities.clear(); 98 mDirections.clear(); 99 } 100 101 if (DEBUG_GEO_FULL) { 102 AKLOGI("Init ProximityInfoState: reused points = %d, last input size = %d", 103 pushTouchPointStartIndex, lastSavedInputSize); 104 } 105 106 if (xCoordinates && yCoordinates) { 107 mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(mProximityInfo, 108 mMaxPointToKeyLength, mInputProximities, xCoordinates, yCoordinates, times, 109 pointerIds, inputSize, isGeometric, pointerId, 110 pushTouchPointStartIndex, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, 111 &mSampledLengthCache, &mSampledInputIndice); 112 } 113 114 if (mSampledInputSize > 0 && isGeometric) { 115 mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(inputSize, xCoordinates, 116 yCoordinates, times, lastSavedInputSize, mSampledInputSize, &mSampledInputXs, 117 &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice, 118 &mSpeedRates, &mDirections); 119 ProximityInfoStateUtils::refreshBeelineSpeedRates(mProximityInfo->getMostCommonKeyWidth(), 120 mAverageSpeed, inputSize, xCoordinates, yCoordinates, times, mSampledInputSize, 121 &mSampledInputXs, &mSampledInputYs, &mSampledInputIndice, 122 &mBeelineSpeedPercentiles); 123 } 124 125 if (mSampledInputSize > 0) { 126 ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize, 127 lastSavedInputSize, isGeometric, &mSampledInputXs, &mSampledInputYs, 128 &mSampledNearKeySets, &mSampledNormalizedSquaredLengthCache); 129 if (isGeometric) { 130 // updates probabilities of skipping or mapping each key for all points. 131 ProximityInfoStateUtils::updateAlignPointProbabilities( 132 mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(), 133 mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize, 134 &mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache, 135 &mSampledNormalizedSquaredLengthCache, &mSampledNearKeySets, 136 &mCharProbabilities); 137 ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo, 138 mSampledInputSize, lastSavedInputSize, &mSampledLengthCache, 139 &mSampledNearKeySets, &mSampledSearchKeySets, 140 &mSampledSearchKeyVectors); 141 mMostProbableStringProbability = ProximityInfoStateUtils::getMostProbableString( 142 mProximityInfo, mSampledInputSize, &mCharProbabilities, mMostProbableString); 143 144 } 145 } 146 147 if (DEBUG_SAMPLING_POINTS) { 148 ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates, 149 mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates, 150 &mBeelineSpeedPercentiles); 151 } 152 // end 153 /////////////////////// 154 155 mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData 156 && xCoordinates && yCoordinates; 157 if (!isGeometric && pointerId == 0) { 158 ProximityInfoStateUtils::initPrimaryInputWord( 159 inputSize, mInputProximities, mPrimaryInputWord); 160 } 161 if (DEBUG_GEO_FULL) { 162 AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize); 163 } 164 mHasBeenUpdatedByGeometricInput = isGeometric; 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(mSampledNormalizedSquaredLengthCache[index], mMaxPointToKeyLength); 175 } 176 if (CharUtils::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 &mSampledNormalizedSquaredLengthCache, mProximityInfo->getKeyCount(), inputIndex, 187 keyId); 188} 189 190// In the following function, c is the current character of the dictionary word currently examined. 191// currentChars is an array containing the keys close to the character the user actually typed at 192// the same position. We want to see if c is in it: if so, then the word contains at that position 193// a character close to what the user typed. 194// What the user typed is actually the first character of the array. 195// proximityIndex is a pointer to the variable where getProximityType returns the index of c 196// in the proximity chars of the input index. 197// Notice : accented characters do not have a proximity list, so they are alone in their list. The 198// non-accented version of the character should be considered "close", but not the other keys close 199// to the non-accented version. 200ProximityType ProximityInfoState::getProximityType(const int index, const int codePoint, 201 const bool checkProximityChars, int *proximityIndex) const { 202 const int *currentCodePoints = getProximityCodePointsAt(index); 203 const int firstCodePoint = currentCodePoints[0]; 204 const int baseLowerC = CharUtils::toBaseLowerCase(codePoint); 205 206 // The first char in the array is what user typed. If it matches right away, that means the 207 // user typed that same char for this pos. 208 if (firstCodePoint == baseLowerC || firstCodePoint == codePoint) { 209 return MATCH_CHAR; 210 } 211 212 if (!checkProximityChars) return SUBSTITUTION_CHAR; 213 214 // If the non-accented, lowercased version of that first character matches c, then we have a 215 // non-accented version of the accented character the user typed. Treat it as a close char. 216 if (CharUtils::toBaseLowerCase(firstCodePoint) == baseLowerC) { 217 return PROXIMITY_CHAR; 218 } 219 220 // Not an exact nor an accent-alike match: search the list of close keys 221 int j = 1; 222 while (j < MAX_PROXIMITY_CHARS_SIZE 223 && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { 224 const bool matched = (currentCodePoints[j] == baseLowerC 225 || currentCodePoints[j] == codePoint); 226 if (matched) { 227 if (proximityIndex) { 228 *proximityIndex = j; 229 } 230 return PROXIMITY_CHAR; 231 } 232 ++j; 233 } 234 if (j < MAX_PROXIMITY_CHARS_SIZE 235 && currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { 236 ++j; 237 while (j < MAX_PROXIMITY_CHARS_SIZE 238 && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { 239 const bool matched = (currentCodePoints[j] == baseLowerC 240 || currentCodePoints[j] == codePoint); 241 if (matched) { 242 if (proximityIndex) { 243 *proximityIndex = j; 244 } 245 return ADDITIONAL_PROXIMITY_CHAR; 246 } 247 ++j; 248 } 249 } 250 // Was not included, signal this as a substitution character. 251 return SUBSTITUTION_CHAR; 252} 253 254ProximityType ProximityInfoState::getProximityTypeG(const int index, const int codePoint) const { 255 if (!isUsed()) { 256 return UNRELATED_CHAR; 257 } 258 const int lowerCodePoint = CharUtils::toLowerCase(codePoint); 259 const int baseLowerCodePoint = CharUtils::toBaseCodePoint(lowerCodePoint); 260 for (int i = 0; i < static_cast<int>(mSampledSearchKeyVectors[index].size()); ++i) { 261 if (mSampledSearchKeyVectors[index][i] == lowerCodePoint 262 || mSampledSearchKeyVectors[index][i] == baseLowerCodePoint) { 263 return MATCH_CHAR; 264 } 265 } 266 return UNRELATED_CHAR; 267} 268 269bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const { 270 ASSERT(keyId >= 0 && index >= 0 && index < mSampledInputSize); 271 return mSampledSearchKeySets[index].test(keyId); 272} 273 274float ProximityInfoState::getDirection(const int index0, const int index1) const { 275 return ProximityInfoStateUtils::getDirection( 276 &mSampledInputXs, &mSampledInputYs, index0, index1); 277} 278 279float ProximityInfoState::getMostProbableString(int *const codePointBuf) const { 280 memcpy(codePointBuf, mMostProbableString, sizeof(mMostProbableString)); 281 return mMostProbableStringProbability; 282} 283 284bool ProximityInfoState::hasSpaceProximity(const int index) const { 285 ASSERT(0 <= index && index < mSampledInputSize); 286 return mProximityInfo->hasSpaceProximity(getInputX(index), getInputY(index)); 287} 288 289// Returns a probability of mapping index to keyIndex. 290float ProximityInfoState::getProbability(const int index, const int keyIndex) const { 291 ASSERT(0 <= index && index < mSampledInputSize); 292 hash_map_compat<int, float>::const_iterator it = mCharProbabilities[index].find(keyIndex); 293 if (it != mCharProbabilities[index].end()) { 294 return it->second; 295 } 296 return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); 297} 298} // namespace latinime 299