proximity_info_state.cpp revision a1d84bcf8ffd031c135b6f3f8c94b6732071849b
1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Copyright (C) 2012 The Android Open Source Project
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * you may not use this file except in compliance with the License.
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * You may obtain a copy of the License at
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License.
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <cstring> // for memset() and memcpy()
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <sstream> // for debug prints
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <vector>
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#define LOG_TAG "LatinIME: proximity_info_state.cpp"
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
23f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes#include "defines.h"
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include "geometry_utils.h"
25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include "proximity_info.h"
26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include "proximity_info_state.h"
27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include "proximity_info_state_utils.h"
28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
29f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughesnamespace latinime {
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
31adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project// TODO: Remove the dependency of "isGeometric"
32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectvoid ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength,
33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize,
34adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        const int *const xCoordinates, const int *const yCoordinates, const int *const times,
35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        const int *const pointerIds, const bool isGeometric) {
36f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes    ASSERT(isGeometric || (inputSize < MAX_WORD_LENGTH));
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    mIsContinuousSuggestionPossible =
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible(
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    inputSize, xCoordinates, yCoordinates, times, mSampledInputSize,
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledInputIndice);
41adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    if (DEBUG_DICT) {
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        AKLOGI("isContinuousSuggestionPossible = %s",
43f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                (mIsContinuousSuggestionPossible ? "true" : "false"));
44adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    mProximityInfo = proximityInfo;
47adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData();
48adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare();
49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    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        mSampledDistanceCache_G.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, &mSampledDistanceCache_G);
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                    &mSampledDistanceCache_G, &mSampledNearKeySets, &mCharProbabilities);
133            ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo,
134                    mSampledInputSize, lastSavedInputSize, &mSampledLengthCache,
135                    &mSampledNearKeySets, &mSampledSearchKeySets,
136                    &mSampledSearchKeyVectors);
137            mMostProbableStringProbability = ProximityInfoStateUtils::getMostProbableString(
138                    mProximityInfo, mSampledInputSize, &mCharProbabilities, mMostProbableString);
139
140        }
141    }
142
143    if (DEBUG_SAMPLING_POINTS) {
144        ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates,
145                mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates,
146                &mBeelineSpeedPercentiles);
147    }
148    // end
149    ///////////////////////
150
151    mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData
152            && xCoordinates && yCoordinates;
153    if (!isGeometric && pointerId == 0) {
154        ProximityInfoStateUtils::initPrimaryInputWord(
155                inputSize, mInputProximities, mPrimaryInputWord);
156        if (mTouchPositionCorrectionEnabled) {
157            ProximityInfoStateUtils::initNormalizedSquaredDistances(
158                    mProximityInfo, inputSize, xCoordinates, yCoordinates, mInputProximities,
159                    &mSampledInputXs, &mSampledInputYs, mNormalizedSquaredDistances);
160        }
161    }
162    if (DEBUG_GEO_FULL) {
163        AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize);
164    }
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(mSampledDistanceCache_G[index], mMaxPointToKeyLength);
175    }
176    if (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            &mSampledDistanceCache_G, mProximityInfo->getKeyCount(), inputIndex, keyId);
187}
188
189// In the following function, c is the current character of the dictionary word currently examined.
190// currentChars is an array containing the keys close to the character the user actually typed at
191// the same position. We want to see if c is in it: if so, then the word contains at that position
192// a character close to what the user typed.
193// What the user typed is actually the first character of the array.
194// proximityIndex is a pointer to the variable where getProximityType returns the index of c
195// in the proximity chars of the input index.
196// Notice : accented characters do not have a proximity list, so they are alone in their list. The
197// non-accented version of the character should be considered "close", but not the other keys close
198// to the non-accented version.
199ProximityType ProximityInfoState::getProximityType(const int index, const int codePoint,
200        const bool checkProximityChars, int *proximityIndex) const {
201    const int *currentCodePoints = getProximityCodePointsAt(index);
202    const int firstCodePoint = currentCodePoints[0];
203    const int baseLowerC = toBaseLowerCase(codePoint);
204
205    // The first char in the array is what user typed. If it matches right away, that means the
206    // user typed that same char for this pos.
207    if (firstCodePoint == baseLowerC || firstCodePoint == codePoint) {
208        return MATCH_CHAR;
209    }
210
211    if (!checkProximityChars) return SUBSTITUTION_CHAR;
212
213    // If the non-accented, lowercased version of that first character matches c, then we have a
214    // non-accented version of the accented character the user typed. Treat it as a close char.
215    if (toBaseLowerCase(firstCodePoint) == baseLowerC) {
216        return PROXIMITY_CHAR;
217    }
218
219    // Not an exact nor an accent-alike match: search the list of close keys
220    int j = 1;
221    while (j < MAX_PROXIMITY_CHARS_SIZE
222            && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
223        const bool matched = (currentCodePoints[j] == baseLowerC
224                || currentCodePoints[j] == codePoint);
225        if (matched) {
226            if (proximityIndex) {
227                *proximityIndex = j;
228            }
229            return PROXIMITY_CHAR;
230        }
231        ++j;
232    }
233    if (j < MAX_PROXIMITY_CHARS_SIZE
234            && currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
235        ++j;
236        while (j < MAX_PROXIMITY_CHARS_SIZE
237                && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
238            const bool matched = (currentCodePoints[j] == baseLowerC
239                    || currentCodePoints[j] == codePoint);
240            if (matched) {
241                if (proximityIndex) {
242                    *proximityIndex = j;
243                }
244                return ADDITIONAL_PROXIMITY_CHAR;
245            }
246            ++j;
247        }
248    }
249    // Was not included, signal this as a substitution character.
250    return SUBSTITUTION_CHAR;
251}
252
253ProximityType ProximityInfoState::getProximityTypeG(const int index, const int codePoint) const {
254    if (!isUsed()) {
255        return UNRELATED_CHAR;
256    }
257    const int lowerCodePoint = toLowerCase(codePoint);
258    const int baseLowerCodePoint = toBaseCodePoint(lowerCodePoint);
259    for (int i = 0; i < static_cast<int>(mSampledSearchKeyVectors[index].size()); ++i) {
260        if (mSampledSearchKeyVectors[index][i] == lowerCodePoint
261                || mSampledSearchKeyVectors[index][i] == baseLowerCodePoint) {
262            return MATCH_CHAR;
263        }
264    }
265    return UNRELATED_CHAR;
266}
267
268bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const {
269    ASSERT(keyId >= 0 && index >= 0 && index < mSampledInputSize);
270    return mSampledSearchKeySets[index].test(keyId);
271}
272
273float ProximityInfoState::getDirection(const int index0, const int index1) const {
274    return ProximityInfoStateUtils::getDirection(
275            &mSampledInputXs, &mSampledInputYs, index0, index1);
276}
277
278float ProximityInfoState::getLineToKeyDistance(
279        const int from, const int to, const int keyId, const bool extend) const {
280    if (from < 0 || from > mSampledInputSize - 1) {
281        return 0.0f;
282    }
283    if (to < 0 || to > mSampledInputSize - 1) {
284        return 0.0f;
285    }
286    const int x0 = mSampledInputXs[from];
287    const int y0 = mSampledInputYs[from];
288    const int x1 = mSampledInputXs[to];
289    const int y1 = mSampledInputYs[to];
290
291    const int keyX = mProximityInfo->getKeyCenterXOfKeyIdG(keyId);
292    const int keyY = mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
293
294    return ProximityInfoUtils::pointToLineSegSquaredDistanceFloat(
295            keyX, keyY, x0, y0, x1, y1, extend);
296}
297
298float ProximityInfoState::getMostProbableString(int *const codePointBuf) const {
299    memcpy(codePointBuf, mMostProbableString, sizeof(mMostProbableString));
300    return mMostProbableStringProbability;
301}
302
303bool ProximityInfoState::hasSpaceProximity(const int index) const {
304    ASSERT(0 <= index && index < mSampledInputSize);
305    return mProximityInfo->hasSpaceProximity(getInputX(index), getInputY(index));
306}
307
308// Returns a probability of mapping index to keyIndex.
309float ProximityInfoState::getProbability(const int index, const int keyIndex) const {
310    ASSERT(0 <= index && index < mSampledInputSize);
311    hash_map_compat<int, float>::const_iterator it = mCharProbabilities[index].find(keyIndex);
312    if (it != mCharProbabilities[index].end()) {
313        return it->second;
314    }
315    return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
316}
317} // namespace latinime
318