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