proximity_info.cpp revision 464d3ba43257da34ab165da8ba0af11e928aae5c
1/*
2 * Copyright (C) 2011 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.cpp"
18
19#include "suggest/core/layout/proximity_info.h"
20
21#include <cstring>
22#include <cmath>
23
24#include "defines.h"
25#include "jni.h"
26#include "suggest/core/dictionary/char_utils.h"
27#include "suggest/core/layout/additional_proximity_chars.h"
28#include "suggest/core/layout/geometry_utils.h"
29#include "suggest/core/layout/proximity_info_params.h"
30
31namespace latinime {
32
33static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray,
34        jsize len, jint *buffer) {
35    if (jArray && buffer) {
36        env->GetIntArrayRegion(jArray, 0, len, buffer);
37    } else if (buffer) {
38        memset(buffer, 0, len * sizeof(buffer[0]));
39    }
40}
41
42static AK_FORCE_INLINE void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray,
43        jsize len, jfloat *buffer) {
44    if (jArray && buffer) {
45        env->GetFloatArrayRegion(jArray, 0, len, buffer);
46    } else if (buffer) {
47        memset(buffer, 0, len * sizeof(buffer[0]));
48    }
49}
50
51ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr,
52        const int keyboardWidth, const int keyboardHeight, const int gridWidth,
53        const int gridHeight, const int mostCommonKeyWidth, const int mostCommonKeyHeight,
54        const jintArray proximityChars, const int keyCount, const jintArray keyXCoordinates,
55        const jintArray keyYCoordinates, const jintArray keyWidths, const jintArray keyHeights,
56        const jintArray keyCharCodes, const jfloatArray sweetSpotCenterXs,
57        const jfloatArray sweetSpotCenterYs, const jfloatArray sweetSpotRadii)
58        : GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth),
59          MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth),
60          MOST_COMMON_KEY_HEIGHT(mostCommonKeyHeight),
61          NORMALIZED_SQUARED_MOST_COMMON_KEY_HYPOTENUSE(1.0f +
62                  GeometryUtils::SQUARE_FLOAT(static_cast<float>(mostCommonKeyHeight) /
63                          static_cast<float>(mostCommonKeyWidth))),
64          CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
65          CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
66          KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)),
67          KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight),
68          KEYBOARD_HYPOTENUSE(hypotf(KEYBOARD_WIDTH, KEYBOARD_HEIGHT)),
69          HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates
70                  && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
71                  && sweetSpotCenterYs && sweetSpotRadii),
72          mProximityCharsArray(new int[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE
73                  /* proximityCharsLength */]),
74          mCodeToKeyMap() {
75    /* Let's check the input array length here to make sure */
76    const jsize proximityCharsLength = env->GetArrayLength(proximityChars);
77    if (proximityCharsLength != GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE) {
78        AKLOGE("Invalid proximityCharsLength: %d", proximityCharsLength);
79        ASSERT(false);
80        return;
81    }
82    if (DEBUG_PROXIMITY_INFO) {
83        AKLOGI("Create proximity info array %d", proximityCharsLength);
84    }
85    const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr);
86    if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) {
87        AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length);
88        ASSERT(false);
89    }
90    memset(mLocaleStr, 0, sizeof(mLocaleStr));
91    env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr);
92    safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityCharsLength,
93            mProximityCharsArray);
94    safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
95    safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates);
96    safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths);
97    safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights);
98    safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCodePoints);
99    safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs);
100    safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs);
101    safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii);
102    initializeG();
103}
104
105ProximityInfo::~ProximityInfo() {
106    delete[] mProximityCharsArray;
107}
108
109bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
110    if (x < 0 || y < 0) {
111        if (DEBUG_DICT) {
112            AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
113            // TODO: Enable this assertion.
114            //ASSERT(false);
115        }
116        return false;
117    }
118
119    const int startIndex = ProximityInfoUtils::getStartIndexFromCoordinates(x, y,
120            CELL_HEIGHT, CELL_WIDTH, GRID_WIDTH);
121    if (DEBUG_PROXIMITY_INFO) {
122        AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
123    }
124    int *proximityCharsArray = mProximityCharsArray;
125    for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
126        if (DEBUG_PROXIMITY_INFO) {
127            AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
128        }
129        if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
130            return true;
131        }
132    }
133    return false;
134}
135
136float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG(
137        const int keyId, const int x, const int y, const float verticalScale) const {
138    const bool correctTouchPosition = hasTouchPositionCorrectionData();
139    const float centerX = static_cast<float>(correctTouchPosition ? getSweetSpotCenterXAt(keyId)
140            : getKeyCenterXOfKeyIdG(keyId));
141    const float visualKeyCenterY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId));
142    float centerY;
143    if (correctTouchPosition) {
144        const float sweetSpotCenterY = static_cast<float>(getSweetSpotCenterYAt(keyId));
145        const float gapY = sweetSpotCenterY - visualKeyCenterY;
146        centerY = visualKeyCenterY + gapY * verticalScale;
147    } else {
148        centerY = visualKeyCenterY;
149    }
150    const float touchX = static_cast<float>(x);
151    const float touchY = static_cast<float>(y);
152    const float keyWidth = static_cast<float>(getMostCommonKeyWidth());
153    return ProximityInfoUtils::getSquaredDistanceFloat(centerX, centerY, touchX, touchY)
154            / GeometryUtils::SQUARE_FLOAT(keyWidth);
155}
156
157int ProximityInfo::getCodePointOf(const int keyIndex) const {
158    if (keyIndex < 0 || keyIndex >= KEY_COUNT) {
159        return NOT_A_CODE_POINT;
160    }
161    return mKeyIndexToCodePointG[keyIndex];
162}
163
164void ProximityInfo::initializeG() {
165    // TODO: Optimize
166    for (int i = 0; i < KEY_COUNT; ++i) {
167        const int code = mKeyCodePoints[i];
168        const int lowerCode = CharUtils::toLowerCase(code);
169        mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
170        mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
171        mCodeToKeyMap[lowerCode] = i;
172        mKeyIndexToCodePointG[i] = lowerCode;
173    }
174    for (int i = 0; i < KEY_COUNT; i++) {
175        mKeyKeyDistancesG[i][i] = 0;
176        for (int j = i + 1; j < KEY_COUNT; j++) {
177            mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt(
178                    mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
179            mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
180        }
181    }
182}
183
184int ProximityInfo::getKeyCenterXOfCodePointG(int charCode) const {
185    return getKeyCenterXOfKeyIdG(
186            ProximityInfoUtils::getKeyIndexOf(KEY_COUNT, charCode, &mCodeToKeyMap));
187}
188
189int ProximityInfo::getKeyCenterYOfCodePointG(int charCode) const {
190    return getKeyCenterYOfKeyIdG(
191            ProximityInfoUtils::getKeyIndexOf(KEY_COUNT, charCode, &mCodeToKeyMap));
192}
193
194int ProximityInfo::getKeyCenterXOfKeyIdG(int keyId) const {
195    if (keyId >= 0) {
196        return mCenterXsG[keyId];
197    }
198    return 0;
199}
200
201int ProximityInfo::getKeyCenterYOfKeyIdG(int keyId) const {
202    if (keyId >= 0) {
203        return mCenterYsG[keyId];
204    }
205    return 0;
206}
207
208int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const {
209    if (keyId0 >= 0 && keyId1 >= 0) {
210        return mKeyKeyDistancesG[keyId0][keyId1];
211    }
212    return MAX_VALUE_FOR_WEIGHTING;
213}
214} // namespace latinime
215