Home | History | Annotate | Download | only in src
      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 #include <cstring> // for memset() and memcpy()
     18 #include <sstream> // for debug prints
     19 #include <vector>
     20 
     21 #define LOG_TAG "LatinIME: proximity_info_state.cpp"
     22 
     23 #include "defines.h"
     24 #include "geometry_utils.h"
     25 #include "proximity_info.h"
     26 #include "proximity_info_state.h"
     27 #include "proximity_info_state_utils.h"
     28 
     29 namespace latinime {
     30 
     31 // TODO: Remove the dependency of "isGeometric"
     32 void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength,
     33         const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize,
     34         const int *const xCoordinates, const int *const yCoordinates, const int *const times,
     35         const int *const pointerIds, const bool isGeometric) {
     36     ASSERT(isGeometric || (inputSize < MAX_WORD_LENGTH));
     37     mIsContinuousSuggestionPossible =
     38             ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible(
     39                     inputSize, xCoordinates, yCoordinates, times, mSampledInputSize,
     40                     &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledInputIndice);
     41     if (DEBUG_DICT) {
     42         AKLOGI("isContinuousSuggestionPossible = %s",
     43                 (mIsContinuousSuggestionPossible ? "true" : "false"));
     44     }
     45 
     46     mProximityInfo = proximityInfo;
     47     mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData();
     48     mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare();
     49     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         mSampledNormalizedSquaredLengthCache.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, &mSampledNormalizedSquaredLengthCache);
    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                     &mSampledNormalizedSquaredLengthCache, &mSampledNearKeySets,
    133                     &mCharProbabilities);
    134             ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo,
    135                     mSampledInputSize, lastSavedInputSize, &mSampledLengthCache,
    136                     &mSampledNearKeySets, &mSampledSearchKeySets,
    137                     &mSampledSearchKeyVectors);
    138             mMostProbableStringProbability = ProximityInfoStateUtils::getMostProbableString(
    139                     mProximityInfo, mSampledInputSize, &mCharProbabilities, mMostProbableString);
    140 
    141         }
    142     }
    143 
    144     if (DEBUG_SAMPLING_POINTS) {
    145         ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates,
    146                 mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates,
    147                 &mBeelineSpeedPercentiles);
    148     }
    149     // end
    150     ///////////////////////
    151 
    152     mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData
    153             && xCoordinates && yCoordinates;
    154     if (!isGeometric && pointerId == 0) {
    155         ProximityInfoStateUtils::initPrimaryInputWord(
    156                 inputSize, mInputProximities, mPrimaryInputWord);
    157         if (mTouchPositionCorrectionEnabled) {
    158             ProximityInfoStateUtils::initNormalizedSquaredDistances(
    159                     mProximityInfo, inputSize, xCoordinates, yCoordinates, mInputProximities,
    160                     &mSampledInputXs, &mSampledInputYs, mNormalizedSquaredDistances);
    161         }
    162     }
    163     if (DEBUG_GEO_FULL) {
    164         AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize);
    165     }
    166 }
    167 
    168 // This function basically converts from a length to an edit distance. Accordingly, it's obviously
    169 // wrong to compare with mMaxPointToKeyLength.
    170 float ProximityInfoState::getPointToKeyLength(
    171         const int inputIndex, const int codePoint) const {
    172     const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
    173     if (keyId != NOT_AN_INDEX) {
    174         const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
    175         return min(mSampledNormalizedSquaredLengthCache[index], mMaxPointToKeyLength);
    176     }
    177     if (isIntentionalOmissionCodePoint(codePoint)) {
    178         return 0.0f;
    179     }
    180     // If the char is not a key on the keyboard then return the max length.
    181     return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
    182 }
    183 
    184 float ProximityInfoState::getPointToKeyByIdLength(
    185         const int inputIndex, const int keyId) const {
    186     return ProximityInfoStateUtils::getPointToKeyByIdLength(mMaxPointToKeyLength,
    187             &mSampledNormalizedSquaredLengthCache, mProximityInfo->getKeyCount(), inputIndex,
    188             keyId);
    189 }
    190 
    191 // In the following function, c is the current character of the dictionary word currently examined.
    192 // currentChars is an array containing the keys close to the character the user actually typed at
    193 // the same position. We want to see if c is in it: if so, then the word contains at that position
    194 // a character close to what the user typed.
    195 // What the user typed is actually the first character of the array.
    196 // proximityIndex is a pointer to the variable where getProximityType returns the index of c
    197 // in the proximity chars of the input index.
    198 // Notice : accented characters do not have a proximity list, so they are alone in their list. The
    199 // non-accented version of the character should be considered "close", but not the other keys close
    200 // to the non-accented version.
    201 ProximityType ProximityInfoState::getProximityType(const int index, const int codePoint,
    202         const bool checkProximityChars, int *proximityIndex) const {
    203     const int *currentCodePoints = getProximityCodePointsAt(index);
    204     const int firstCodePoint = currentCodePoints[0];
    205     const int baseLowerC = toBaseLowerCase(codePoint);
    206 
    207     // The first char in the array is what user typed. If it matches right away, that means the
    208     // user typed that same char for this pos.
    209     if (firstCodePoint == baseLowerC || firstCodePoint == codePoint) {
    210         return MATCH_CHAR;
    211     }
    212 
    213     if (!checkProximityChars) return SUBSTITUTION_CHAR;
    214 
    215     // If the non-accented, lowercased version of that first character matches c, then we have a
    216     // non-accented version of the accented character the user typed. Treat it as a close char.
    217     if (toBaseLowerCase(firstCodePoint) == baseLowerC) {
    218         return PROXIMITY_CHAR;
    219     }
    220 
    221     // Not an exact nor an accent-alike match: search the list of close keys
    222     int j = 1;
    223     while (j < MAX_PROXIMITY_CHARS_SIZE
    224             && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
    225         const bool matched = (currentCodePoints[j] == baseLowerC
    226                 || currentCodePoints[j] == codePoint);
    227         if (matched) {
    228             if (proximityIndex) {
    229                 *proximityIndex = j;
    230             }
    231             return PROXIMITY_CHAR;
    232         }
    233         ++j;
    234     }
    235     if (j < MAX_PROXIMITY_CHARS_SIZE
    236             && currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
    237         ++j;
    238         while (j < MAX_PROXIMITY_CHARS_SIZE
    239                 && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
    240             const bool matched = (currentCodePoints[j] == baseLowerC
    241                     || currentCodePoints[j] == codePoint);
    242             if (matched) {
    243                 if (proximityIndex) {
    244                     *proximityIndex = j;
    245                 }
    246                 return ADDITIONAL_PROXIMITY_CHAR;
    247             }
    248             ++j;
    249         }
    250     }
    251     // Was not included, signal this as a substitution character.
    252     return SUBSTITUTION_CHAR;
    253 }
    254 
    255 ProximityType ProximityInfoState::getProximityTypeG(const int index, const int codePoint) const {
    256     if (!isUsed()) {
    257         return UNRELATED_CHAR;
    258     }
    259     const int lowerCodePoint = toLowerCase(codePoint);
    260     const int baseLowerCodePoint = toBaseCodePoint(lowerCodePoint);
    261     for (int i = 0; i < static_cast<int>(mSampledSearchKeyVectors[index].size()); ++i) {
    262         if (mSampledSearchKeyVectors[index][i] == lowerCodePoint
    263                 || mSampledSearchKeyVectors[index][i] == baseLowerCodePoint) {
    264             return MATCH_CHAR;
    265         }
    266     }
    267     return UNRELATED_CHAR;
    268 }
    269 
    270 bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const {
    271     ASSERT(keyId >= 0 && index >= 0 && index < mSampledInputSize);
    272     return mSampledSearchKeySets[index].test(keyId);
    273 }
    274 
    275 float ProximityInfoState::getDirection(const int index0, const int index1) const {
    276     return ProximityInfoStateUtils::getDirection(
    277             &mSampledInputXs, &mSampledInputYs, index0, index1);
    278 }
    279 
    280 float ProximityInfoState::getLineToKeyDistance(
    281         const int from, const int to, const int keyId, const bool extend) const {
    282     if (from < 0 || from > mSampledInputSize - 1) {
    283         return 0.0f;
    284     }
    285     if (to < 0 || to > mSampledInputSize - 1) {
    286         return 0.0f;
    287     }
    288     const int x0 = mSampledInputXs[from];
    289     const int y0 = mSampledInputYs[from];
    290     const int x1 = mSampledInputXs[to];
    291     const int y1 = mSampledInputYs[to];
    292 
    293     const int keyX = mProximityInfo->getKeyCenterXOfKeyIdG(keyId);
    294     const int keyY = mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
    295 
    296     return ProximityInfoUtils::pointToLineSegSquaredDistanceFloat(
    297             keyX, keyY, x0, y0, x1, y1, extend);
    298 }
    299 
    300 float ProximityInfoState::getMostProbableString(int *const codePointBuf) const {
    301     memcpy(codePointBuf, mMostProbableString, sizeof(mMostProbableString));
    302     return mMostProbableStringProbability;
    303 }
    304 
    305 bool ProximityInfoState::hasSpaceProximity(const int index) const {
    306     ASSERT(0 <= index && index < mSampledInputSize);
    307     return mProximityInfo->hasSpaceProximity(getInputX(index), getInputY(index));
    308 }
    309 
    310 // Returns a probability of mapping index to keyIndex.
    311 float ProximityInfoState::getProbability(const int index, const int keyIndex) const {
    312     ASSERT(0 <= index && index < mSampledInputSize);
    313     hash_map_compat<int, float>::const_iterator it = mCharProbabilities[index].find(keyIndex);
    314     if (it != mCharProbabilities[index].end()) {
    315         return it->second;
    316     }
    317     return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
    318 }
    319 } // namespace latinime
    320