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()
     18 #include <stdint.h>
     19 
     20 #define LOG_TAG "LatinIME: proximity_info_state.cpp"
     21 
     22 #include "defines.h"
     23 #include "geometry_utils.h"
     24 #include "proximity_info.h"
     25 #include "proximity_info_state.h"
     26 
     27 namespace latinime {
     28 
     29 const int ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
     30 const int ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
     31         1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
     32 const float ProximityInfoState::NOT_A_DISTANCE_FLOAT = -1.0f;
     33 const int ProximityInfoState::NOT_A_CODE = -1;
     34 
     35 void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength,
     36         const ProximityInfo *proximityInfo, const int32_t *const inputCodes, const int inputSize,
     37         const int *const xCoordinates, const int *const yCoordinates, const int *const times,
     38         const int *const pointerIds, const bool isGeometric) {
     39 
     40     if (isGeometric) {
     41         mIsContinuationPossible = checkAndReturnIsContinuationPossible(
     42                 inputSize, xCoordinates, yCoordinates, times);
     43     } else {
     44         mIsContinuationPossible = false;
     45     }
     46 
     47     mProximityInfo = proximityInfo;
     48     mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData();
     49     mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare();
     50     mLocaleStr = proximityInfo->getLocaleStr();
     51     mKeyCount = proximityInfo->getKeyCount();
     52     mCellHeight = proximityInfo->getCellHeight();
     53     mCellWidth = proximityInfo->getCellWidth();
     54     mGridHeight = proximityInfo->getGridWidth();
     55     mGridWidth = proximityInfo->getGridHeight();
     56 
     57     memset(mInputCodes, 0, sizeof(mInputCodes));
     58 
     59     if (!isGeometric && pointerId == 0) {
     60         // Initialize
     61         // - mInputCodes
     62         // - mNormalizedSquaredDistances
     63         // TODO: Merge
     64         for (int i = 0; i < inputSize; ++i) {
     65             const int32_t primaryKey = inputCodes[i];
     66             const int x = xCoordinates[i];
     67             const int y = yCoordinates[i];
     68             int *proximities = &mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL];
     69             mProximityInfo->calculateNearbyKeyCodes(x, y, primaryKey, proximities);
     70         }
     71 
     72         if (DEBUG_PROXIMITY_CHARS) {
     73             for (int i = 0; i < inputSize; ++i) {
     74                 AKLOGI("---");
     75                 for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL; ++j) {
     76                     int icc = mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j];
     77                     int icfjc = inputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j];
     78                     icc += 0;
     79                     icfjc += 0;
     80                     AKLOGI("--- (%d)%c,%c", i, icc, icfjc); AKLOGI("--- A<%d>,B<%d>", icc, icfjc);
     81                 }
     82             }
     83         }
     84     }
     85 
     86     ///////////////////////
     87     // Setup touch points
     88     int pushTouchPointStartIndex = 0;
     89     int lastSavedInputSize = 0;
     90     mMaxPointToKeyLength = maxPointToKeyLength;
     91     if (mIsContinuationPossible && mInputIndice.size() > 1) {
     92         // Just update difference.
     93         // Two points prior is never skipped. Thus, we pop 2 input point data here.
     94         pushTouchPointStartIndex = mInputIndice[mInputIndice.size() - 2];
     95         popInputData();
     96         popInputData();
     97         lastSavedInputSize = mInputXs.size();
     98     } else {
     99         // Clear all data.
    100         mInputXs.clear();
    101         mInputYs.clear();
    102         mTimes.clear();
    103         mInputIndice.clear();
    104         mLengthCache.clear();
    105         mDistanceCache.clear();
    106         mNearKeysVector.clear();
    107         mRelativeSpeeds.clear();
    108     }
    109     if (DEBUG_GEO_FULL) {
    110         AKLOGI("Init ProximityInfoState: reused points =  %d, last input size = %d",
    111                 pushTouchPointStartIndex, lastSavedInputSize);
    112     }
    113     mInputSize = 0;
    114 
    115     if (xCoordinates && yCoordinates) {
    116         const bool proximityOnly = !isGeometric && (xCoordinates[0] < 0 || yCoordinates[0] < 0);
    117         int lastInputIndex = pushTouchPointStartIndex;
    118         for (int i = lastInputIndex; i < inputSize; ++i) {
    119             const int pid = pointerIds ? pointerIds[i] : 0;
    120             if (pointerId == pid) {
    121                 lastInputIndex = i;
    122             }
    123         }
    124         if (DEBUG_GEO_FULL) {
    125             AKLOGI("Init ProximityInfoState: last input index = %d", lastInputIndex);
    126         }
    127         // Working space to save near keys distances for current, prev and prevprev input point.
    128         NearKeysDistanceMap nearKeysDistances[3];
    129         // These pointers are swapped for each inputs points.
    130         NearKeysDistanceMap *currentNearKeysDistances = &nearKeysDistances[0];
    131         NearKeysDistanceMap *prevNearKeysDistances = &nearKeysDistances[1];
    132         NearKeysDistanceMap *prevPrevNearKeysDistances = &nearKeysDistances[2];
    133 
    134         for (int i = pushTouchPointStartIndex; i <= lastInputIndex; ++i) {
    135             // Assuming pointerId == 0 if pointerIds is null.
    136             const int pid = pointerIds ? pointerIds[i] : 0;
    137             if (DEBUG_GEO_FULL) {
    138                 AKLOGI("Init ProximityInfoState: (%d)PID = %d", i, pid);
    139             }
    140             if (pointerId == pid) {
    141                 const int c = isGeometric ? NOT_A_COORDINATE : getPrimaryCharAt(i);
    142                 const int x = proximityOnly ? NOT_A_COORDINATE : xCoordinates[i];
    143                 const int y = proximityOnly ? NOT_A_COORDINATE : yCoordinates[i];
    144                 const int time = times ? times[i] : -1;
    145                 if (pushTouchPoint(i, c, x, y, time, isGeometric /* do sampling */,
    146                         i == lastInputIndex, currentNearKeysDistances, prevNearKeysDistances,
    147                         prevPrevNearKeysDistances)) {
    148                     // Previous point information was popped.
    149                     NearKeysDistanceMap *tmp = prevNearKeysDistances;
    150                     prevNearKeysDistances = currentNearKeysDistances;
    151                     currentNearKeysDistances = tmp;
    152                 } else {
    153                     NearKeysDistanceMap *tmp = prevPrevNearKeysDistances;
    154                     prevPrevNearKeysDistances = prevNearKeysDistances;
    155                     prevNearKeysDistances = currentNearKeysDistances;
    156                     currentNearKeysDistances = tmp;
    157                 }
    158             }
    159         }
    160         mInputSize = mInputXs.size();
    161     }
    162 
    163     if (mInputSize > 0 && isGeometric) {
    164         int sumDuration = mTimes.back() - mTimes.front();
    165         int sumLength = mLengthCache.back() - mLengthCache.front();
    166         float averageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration);
    167         mRelativeSpeeds.resize(mInputSize);
    168         for (int i = lastSavedInputSize; i < mInputSize; ++i) {
    169             const int index = mInputIndice[i];
    170             int length = 0;
    171             int duration = 0;
    172             if (index == 0 && index < inputSize - 1) {
    173                 length = getDistanceInt(xCoordinates[index], yCoordinates[index],
    174                         xCoordinates[index + 1], yCoordinates[index + 1]);
    175                 duration = times[index + 1] - times[index];
    176             } else if (index == inputSize - 1 && index > 0) {
    177                 length = getDistanceInt(xCoordinates[index - 1], yCoordinates[index - 1],
    178                         xCoordinates[index], yCoordinates[index]);
    179                 duration = times[index] - times[index - 1];
    180             } else if (0 < index && index < inputSize - 1) {
    181                 length = getDistanceInt(xCoordinates[index - 1], yCoordinates[index - 1],
    182                         xCoordinates[index], yCoordinates[index])
    183                         + getDistanceInt(xCoordinates[index], yCoordinates[index],
    184                                 xCoordinates[index + 1], yCoordinates[index + 1]);
    185                 duration = times[index + 1] - times[index - 1];
    186             } else {
    187                 length = 0;
    188                 duration = 1;
    189             }
    190             const float speed = static_cast<float>(length) / static_cast<float>(duration);
    191             mRelativeSpeeds[i] = speed / averageSpeed;
    192         }
    193     }
    194 
    195     if (mInputSize > 0) {
    196         const int keyCount = mProximityInfo->getKeyCount();
    197         mNearKeysVector.resize(mInputSize);
    198         mDistanceCache.resize(mInputSize * keyCount);
    199         for (int i = lastSavedInputSize; i < mInputSize; ++i) {
    200             mNearKeysVector[i].reset();
    201             static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f;
    202             for (int k = 0; k < keyCount; ++k) {
    203                 const int index = i * keyCount + k;
    204                 const int x = mInputXs[i];
    205                 const int y = mInputYs[i];
    206                 const float normalizedSquaredDistance =
    207                         mProximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y);
    208                 mDistanceCache[index] = normalizedSquaredDistance;
    209                 if (normalizedSquaredDistance < NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) {
    210                     mNearKeysVector[i].set(k, 1);
    211                 }
    212             }
    213         }
    214 
    215         static const float READ_FORWORD_LENGTH_SCALE = 0.95f;
    216         const int readForwordLength = static_cast<int>(
    217                 hypotf(mProximityInfo->getKeyboardWidth(), mProximityInfo->getKeyboardHeight())
    218                 * READ_FORWORD_LENGTH_SCALE);
    219         for (int i = 0; i < mInputSize; ++i) {
    220             if (DEBUG_GEO_FULL) {
    221                 AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, mInputXs[i], mInputYs[i],
    222                         mTimes[i]);
    223             }
    224             for (int j = max(i + 1, lastSavedInputSize); j < mInputSize; ++j) {
    225                 if (mLengthCache[j] - mLengthCache[i] >= readForwordLength) {
    226                     break;
    227                 }
    228                 mNearKeysVector[i] |= mNearKeysVector[j];
    229             }
    230         }
    231     }
    232 
    233     // end
    234     ///////////////////////
    235 
    236     memset(mNormalizedSquaredDistances, NOT_A_DISTANCE, sizeof(mNormalizedSquaredDistances));
    237     memset(mPrimaryInputWord, 0, sizeof(mPrimaryInputWord));
    238     mTouchPositionCorrectionEnabled = mInputSize > 0 && mHasTouchPositionCorrectionData
    239             && xCoordinates && yCoordinates;
    240     if (!isGeometric && pointerId == 0) {
    241         for (int i = 0; i < inputSize; ++i) {
    242             mPrimaryInputWord[i] = getPrimaryCharAt(i);
    243         }
    244 
    245         for (int i = 0; i < mInputSize && mTouchPositionCorrectionEnabled; ++i) {
    246             const int *proximityChars = getProximityCharsAt(i);
    247             const int primaryKey = proximityChars[0];
    248             const int x = xCoordinates[i];
    249             const int y = yCoordinates[i];
    250             if (DEBUG_PROXIMITY_CHARS) {
    251                 int a = x + y + primaryKey;
    252                 a += 0;
    253                 AKLOGI("--- Primary = %c, x = %d, y = %d", primaryKey, x, y);
    254             }
    255             for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL && proximityChars[j] > 0; ++j) {
    256                 const int currentChar = proximityChars[j];
    257                 const float squaredDistance =
    258                         hasInputCoordinates() ? calculateNormalizedSquaredDistance(
    259                                 mProximityInfo->getKeyIndexOf(currentChar), i) :
    260                                 NOT_A_DISTANCE_FLOAT;
    261                 if (squaredDistance >= 0.0f) {
    262                     mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] =
    263                             (int) (squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
    264                 } else {
    265                     mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] =
    266                             (j == 0) ? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO :
    267                                     PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO;
    268                 }
    269                 if (DEBUG_PROXIMITY_CHARS) {
    270                     AKLOGI("--- Proximity (%d) = %c", j, currentChar);
    271                 }
    272             }
    273         }
    274     }
    275 
    276     if (DEBUG_GEO_FULL) {
    277         AKLOGI("ProximityState init finished: %d points out of %d", mInputSize, inputSize);
    278     }
    279 }
    280 
    281 bool ProximityInfoState::checkAndReturnIsContinuationPossible(const int inputSize,
    282         const int *const xCoordinates, const int *const yCoordinates, const int *const times) {
    283     for (int i = 0; i < mInputSize; ++i) {
    284         const int index = mInputIndice[i];
    285         if (index > inputSize || xCoordinates[index] != mInputXs[i] ||
    286                 yCoordinates[index] != mInputYs[i] || times[index] != mTimes[i]) {
    287             return false;
    288         }
    289     }
    290     return true;
    291 }
    292 
    293 // Calculating point to key distance for all near keys and returning the distance between
    294 // the given point and the nearest key position.
    295 float ProximityInfoState::updateNearKeysDistances(const int x, const int y,
    296         NearKeysDistanceMap *const currentNearKeysDistances) {
    297     static const float NEAR_KEY_THRESHOLD = 4.0f;
    298 
    299     currentNearKeysDistances->clear();
    300     const int keyCount = mProximityInfo->getKeyCount();
    301     float nearestKeyDistance = mMaxPointToKeyLength;
    302     for (int k = 0; k < keyCount; ++k) {
    303         const float dist = mProximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y);
    304         if (dist < NEAR_KEY_THRESHOLD) {
    305             currentNearKeysDistances->insert(std::pair<int, float>(k, dist));
    306         }
    307         if (nearestKeyDistance > dist) {
    308             nearestKeyDistance = dist;
    309         }
    310     }
    311     return nearestKeyDistance;
    312 }
    313 
    314 // Check if previous point is at local minimum position to near keys.
    315 bool ProximityInfoState::isPrevLocalMin(const NearKeysDistanceMap *const currentNearKeysDistances,
    316         const NearKeysDistanceMap *const prevNearKeysDistances,
    317         const NearKeysDistanceMap *const prevPrevNearKeysDistances) const {
    318     static const float MARGIN = 0.01f;
    319 
    320     for (NearKeysDistanceMap::const_iterator it = prevNearKeysDistances->begin();
    321         it != prevNearKeysDistances->end(); ++it) {
    322         NearKeysDistanceMap::const_iterator itPP = prevPrevNearKeysDistances->find(it->first);
    323         NearKeysDistanceMap::const_iterator itC = currentNearKeysDistances->find(it->first);
    324         if ((itPP == prevPrevNearKeysDistances->end() || itPP->second > it->second + MARGIN)
    325                 && (itC == currentNearKeysDistances->end() || itC->second > it->second + MARGIN)) {
    326             return true;
    327         }
    328     }
    329     return false;
    330 }
    331 
    332 // Calculating a point score that indicates usefulness of the point.
    333 float ProximityInfoState::getPointScore(
    334         const int x, const int y, const int time, const bool lastPoint, const float nearest,
    335         const NearKeysDistanceMap *const currentNearKeysDistances,
    336         const NearKeysDistanceMap *const prevNearKeysDistances,
    337         const NearKeysDistanceMap *const prevPrevNearKeysDistances) const {
    338     static const int DISTANCE_BASE_SCALE = 100;
    339     static const int SAVE_DISTANCE_SCALE = 200;
    340     static const int SKIP_DISTANCE_SCALE = 25;
    341     static const int CHECK_LOCALMIN_DISTANCE_THRESHOLD_SCALE = 40;
    342     static const int STRAIGHT_SKIP_DISTANCE_THRESHOLD_SCALE = 50;
    343     static const int CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 27;
    344     static const float SAVE_DISTANCE_SCORE = 2.0f;
    345     static const float SKIP_DISTANCE_SCORE = -1.0f;
    346     static const float CHECK_LOCALMIN_DISTANCE_SCORE = -1.0f;
    347     static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F / 36.0f;
    348     static const float STRAIGHT_SKIP_NEAREST_DISTANCE_THRESHOLD = 0.5f;
    349     static const float STRAIGHT_SKIP_SCORE = -1.0f;
    350     static const float CORNER_ANGLE_THRESHOLD = M_PI_F / 2.0f;
    351     static const float CORNER_SCORE = 1.0f;
    352 
    353     const std::size_t size = mInputXs.size();
    354     if (size <= 1) {
    355         return 0.0f;
    356     }
    357     const int baseSampleRate = mProximityInfo->getMostCommonKeyWidth();
    358     const int distNext = getDistanceInt(x, y, mInputXs.back(), mInputYs.back())
    359             * DISTANCE_BASE_SCALE;
    360     const int distPrev = getDistanceInt(mInputXs.back(), mInputYs.back(),
    361             mInputXs[size - 2], mInputYs[size - 2]) * DISTANCE_BASE_SCALE;
    362     float score = 0.0f;
    363 
    364     // Sum of distances
    365     if (distPrev + distNext > baseSampleRate * SAVE_DISTANCE_SCALE) {
    366         score +=  SAVE_DISTANCE_SCORE;
    367     }
    368     // Distance
    369     if (distPrev < baseSampleRate * SKIP_DISTANCE_SCALE) {
    370         score += SKIP_DISTANCE_SCORE;
    371     }
    372     // Location
    373     if (distPrev < baseSampleRate * CHECK_LOCALMIN_DISTANCE_THRESHOLD_SCALE) {
    374         if (!isPrevLocalMin(currentNearKeysDistances, prevNearKeysDistances,
    375             prevPrevNearKeysDistances)) {
    376             score += CHECK_LOCALMIN_DISTANCE_SCORE;
    377         }
    378     }
    379     // Angle
    380     const float angle1 = getAngle(x, y, mInputXs.back(), mInputYs.back());
    381     const float angle2 = getAngle(mInputXs.back(), mInputYs.back(),
    382             mInputXs[size - 2], mInputYs[size - 2]);
    383     const float angleDiff = getAngleDiff(angle1, angle2);
    384     // Skip straight
    385     if (nearest > STRAIGHT_SKIP_NEAREST_DISTANCE_THRESHOLD
    386             && distPrev < baseSampleRate * STRAIGHT_SKIP_DISTANCE_THRESHOLD_SCALE
    387             && angleDiff < STRAIGHT_ANGLE_THRESHOLD) {
    388         score += STRAIGHT_SKIP_SCORE;
    389     }
    390     // Save corner
    391     if (distPrev > baseSampleRate * CORNER_CHECK_DISTANCE_THRESHOLD_SCALE
    392             && angleDiff > CORNER_ANGLE_THRESHOLD) {
    393         score += CORNER_SCORE;
    394     }
    395     return score;
    396 }
    397 
    398 // Sampling touch point and pushing information to vectors.
    399 // Returning if previous point is popped or not.
    400 bool ProximityInfoState::pushTouchPoint(const int inputIndex, const int nodeChar, int x, int y,
    401         const int time, const bool sample, const bool isLastPoint,
    402         NearKeysDistanceMap *const currentNearKeysDistances,
    403         const NearKeysDistanceMap *const prevNearKeysDistances,
    404         const NearKeysDistanceMap *const prevPrevNearKeysDistances) {
    405     static const float LAST_POINT_SKIP_DISTANCE_SCALE = 0.25f;
    406 
    407     size_t size = mInputXs.size();
    408     bool popped = false;
    409     if (nodeChar < 0 && sample) {
    410         const float nearest = updateNearKeysDistances(x, y, currentNearKeysDistances);
    411         const float score = getPointScore(x, y, time, isLastPoint, nearest,
    412                 currentNearKeysDistances, prevNearKeysDistances, prevPrevNearKeysDistances);
    413         if (score < 0) {
    414             // Pop previous point because it would be useless.
    415             popInputData();
    416             size = mInputXs.size();
    417             popped = true;
    418         } else {
    419             popped = false;
    420         }
    421         // Check if the last point should be skipped.
    422         if (isLastPoint) {
    423             if (size > 0 && getDistanceFloat(x, y, mInputXs.back(), mInputYs.back())
    424                     < mProximityInfo->getMostCommonKeyWidth() * LAST_POINT_SKIP_DISTANCE_SCALE) {
    425                 if (DEBUG_GEO_FULL) {
    426                     AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %f, "
    427                            "width = %f", size, x, y, mInputXs.back(), mInputYs.back(),
    428                            getDistanceFloat(x, y, mInputXs.back(), mInputYs.back()),
    429                            mProximityInfo->getMostCommonKeyWidth()
    430                                    * LAST_POINT_SKIP_DISTANCE_SCALE);
    431                 }
    432                 return popped;
    433             } else if (size > 1) {
    434                 int minChar = 0;
    435                 float minDist = mMaxPointToKeyLength;
    436                 for (NearKeysDistanceMap::const_iterator it = currentNearKeysDistances->begin();
    437                         it != currentNearKeysDistances->end(); ++it) {
    438                     if (minDist > it->second) {
    439                         minChar = it->first;
    440                         minDist = it->second;
    441                     }
    442                 }
    443                 NearKeysDistanceMap::const_iterator itPP =
    444                         prevNearKeysDistances->find(minChar);
    445                 if (itPP != prevNearKeysDistances->end() && minDist > itPP->second) {
    446                     if (DEBUG_GEO_FULL) {
    447                         AKLOGI("p1: char = %c, minDist = %f, prevNear key minDist = %f",
    448                                 minChar, itPP->second, minDist);
    449                     }
    450                     return popped;
    451                 }
    452             }
    453         }
    454     }
    455 
    456     if (nodeChar >= 0 && (x < 0 || y < 0)) {
    457         const int keyId = mProximityInfo->getKeyIndexOf(nodeChar);
    458         if (keyId >= 0) {
    459             x = mProximityInfo->getKeyCenterXOfKeyIdG(keyId);
    460             y = mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
    461         }
    462     }
    463 
    464     // Pushing point information.
    465     if (size > 0) {
    466         mLengthCache.push_back(
    467                 mLengthCache.back() + getDistanceInt(x, y, mInputXs.back(), mInputYs.back()));
    468     } else {
    469         mLengthCache.push_back(0);
    470     }
    471     mInputXs.push_back(x);
    472     mInputYs.push_back(y);
    473     mTimes.push_back(time);
    474     mInputIndice.push_back(inputIndex);
    475     if (DEBUG_GEO_FULL) {
    476         AKLOGI("pushTouchPoint: x = %03d, y = %03d, time = %d, index = %d, popped ? %01d",
    477                 x, y, time, inputIndex, popped);
    478     }
    479     return popped;
    480 }
    481 
    482 float ProximityInfoState::calculateNormalizedSquaredDistance(
    483         const int keyIndex, const int inputIndex) const {
    484     if (keyIndex == NOT_AN_INDEX) {
    485         return NOT_A_DISTANCE_FLOAT;
    486     }
    487     if (!mProximityInfo->hasSweetSpotData(keyIndex)) {
    488         return NOT_A_DISTANCE_FLOAT;
    489     }
    490     if (NOT_A_COORDINATE == mInputXs[inputIndex]) {
    491         return NOT_A_DISTANCE_FLOAT;
    492     }
    493     const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(
    494             keyIndex, inputIndex);
    495     const float squaredRadius = square(mProximityInfo->getSweetSpotRadiiAt(keyIndex));
    496     return squaredDistance / squaredRadius;
    497 }
    498 
    499 int ProximityInfoState::getDuration(const int index) const {
    500     if (index >= 0 && index < mInputSize - 1) {
    501         return mTimes[index + 1] - mTimes[index];
    502     }
    503     return 0;
    504 }
    505 
    506 float ProximityInfoState::getPointToKeyLength(const int inputIndex, const int codePoint,
    507         const float scale) const {
    508     const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
    509     if (keyId != NOT_AN_INDEX) {
    510         const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
    511         return min(mDistanceCache[index] * scale, mMaxPointToKeyLength);
    512     }
    513     if (isSkippableChar(codePoint)) {
    514         return 0.0f;
    515     }
    516     // If the char is not a key on the keyboard then return the max length.
    517     return MAX_POINT_TO_KEY_LENGTH;
    518 }
    519 
    520 int ProximityInfoState::getSpaceY() const {
    521     const int keyId = mProximityInfo->getKeyIndexOf(' ');
    522     return mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
    523 }
    524 
    525 float ProximityInfoState::calculateSquaredDistanceFromSweetSpotCenter(
    526         const int keyIndex, const int inputIndex) const {
    527     const float sweetSpotCenterX = mProximityInfo->getSweetSpotCenterXAt(keyIndex);
    528     const float sweetSpotCenterY = mProximityInfo->getSweetSpotCenterYAt(keyIndex);
    529     const float inputX = static_cast<float>(mInputXs[inputIndex]);
    530     const float inputY = static_cast<float>(mInputYs[inputIndex]);
    531     return square(inputX - sweetSpotCenterX) + square(inputY - sweetSpotCenterY);
    532 }
    533 
    534 // Puts possible characters into filter and returns new filter size.
    535 int32_t ProximityInfoState::getAllPossibleChars(
    536         const size_t index, int32_t *const filter, const int32_t filterSize) const {
    537     if (index >= mInputXs.size()) {
    538         return filterSize;
    539     }
    540     int newFilterSize = filterSize;
    541     for (int j = 0; j < mProximityInfo->getKeyCount(); ++j) {
    542         if (mNearKeysVector[index].test(j)) {
    543             const int32_t keyCodePoint = mProximityInfo->getCodePointOf(j);
    544             bool insert = true;
    545             // TODO: Avoid linear search
    546             for (int k = 0; k < filterSize; ++k) {
    547                 if (filter[k] == keyCodePoint) {
    548                     insert = false;
    549                     break;
    550                 }
    551             }
    552             if (insert) {
    553                 filter[newFilterSize++] = keyCodePoint;
    554             }
    555         }
    556     }
    557     return newFilterSize;
    558 }
    559 
    560 void ProximityInfoState::popInputData() {
    561     mInputXs.pop_back();
    562     mInputYs.pop_back();
    563     mTimes.pop_back();
    564     mLengthCache.pop_back();
    565     mInputIndice.pop_back();
    566 }
    567 
    568 } // namespace latinime
    569