blob: 73717269306f1c8124f962c79afc894902610445 [file] [log] [blame]
satok8fbd5522011-02-22 17:28:55 +09001/*
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
Ken Wakasaf1008c52012-07-31 17:56:40 +090017#include <cassert>
18#include <cmath>
19#include <cstring>
satok8fbd5522011-02-22 17:28:55 +090020
satok817e5172011-03-04 06:06:45 -080021#define LOG_TAG "LatinIME: proximity_info.cpp"
22
satok552c3c22012-03-13 16:33:47 +090023#include "additional_proximity_chars.h"
Ken Wakasa77e8e812012-08-02 19:48:08 +090024#include "char_utils.h"
Ken Wakasa3b088a22012-05-16 23:05:32 +090025#include "defines.h"
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +090026#include "geometry_utils.h"
Ken Wakasabb005f72012-08-08 20:43:47 +090027#include "jni.h"
satok8fbd5522011-02-22 17:28:55 +090028#include "proximity_info.h"
29
30namespace latinime {
Ken Wakasace9e52a2011-06-18 13:09:55 +090031
Ken Wakasa162c2112012-08-24 14:51:15 +090032/* static */ const float ProximityInfo::NOT_A_DISTANCE_FLOAT = -1.0f;
33
Ken Wakasabb005f72012-08-08 20:43:47 +090034static inline void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, jsize len,
35 jint *buffer) {
36 if (jArray && buffer) {
37 env->GetIntArrayRegion(jArray, 0, len, buffer);
38 } else if (buffer) {
Ken Wakasa063c3e22012-08-10 22:10:04 +090039 memset(buffer, 0, len * sizeof(jint));
Yusuke Nojimade2f8422011-09-27 11:15:18 +090040 }
41}
42
Ken Wakasabb005f72012-08-08 20:43:47 +090043static inline void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray, jsize len,
44 jfloat *buffer) {
45 if (jArray && buffer) {
46 env->GetFloatArrayRegion(jArray, 0, len, buffer);
47 } else if (buffer) {
Ken Wakasa063c3e22012-08-10 22:10:04 +090048 memset(buffer, 0, len * sizeof(jfloat));
Ken Wakasabb005f72012-08-08 20:43:47 +090049 }
50}
51
Ken Wakasa01511452012-08-09 15:58:15 +090052ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int maxProximityCharsSize,
satok552c3c22012-03-13 16:33:47 +090053 const int keyboardWidth, const int keyboardHeight, const int gridWidth,
Ken Wakasabb005f72012-08-08 20:43:47 +090054 const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars,
55 const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates,
56 const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes,
57 const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs,
58 const jfloatArray sweetSpotRadii)
Ken Wakasa162c2112012-08-24 14:51:15 +090059 : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), GRID_WIDTH(gridWidth),
60 GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth),
satoka70ee6e2012-03-07 15:12:22 +090061 MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth),
satok817e5172011-03-04 06:06:45 -080062 CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090063 CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
Yusuke Nojima258bfe62011-09-28 12:59:43 +090064 KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)),
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +090065 HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates
66 && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
Ken Wakasa162c2112012-08-24 14:51:15 +090067 && sweetSpotCenterYs && sweetSpotRadii),
68 mProximityCharsArray(new int32_t[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE
69 /* proximityGridLength */]) {
satok1035bc92012-06-13 16:07:54 -070070 const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
satok817e5172011-03-04 06:06:45 -080071 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +090072 AKLOGI("Create proximity info array %d", proximityGridLength);
satok817e5172011-03-04 06:06:45 -080073 }
Ken Wakasa01511452012-08-09 15:58:15 +090074 const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr);
Ken Wakasa9e0c7112012-08-09 22:26:58 +090075 if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) {
76 AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length);
77 assert(false);
78 }
79 memset(mLocaleStr, 0, sizeof(mLocaleStr));
80 env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr);
Ken Wakasabb005f72012-08-08 20:43:47 +090081 safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray);
82 safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
83 safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates);
84 safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths);
85 safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights);
Ken Wakasaf2789812012-09-04 12:49:46 +090086 safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCodePoints);
Ken Wakasabb005f72012-08-08 20:43:47 +090087 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs);
88 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs);
89 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii);
Ken Wakasaf2789812012-09-04 12:49:46 +090090 initializeCodePointToKeyIndex();
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +090091 initializeG();
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090092}
93
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090094// Build the reversed look up table from the char code to the index in mKeyXCoordinates,
95// mKeyYCoordinates, mKeyWidths, mKeyHeights, mKeyCharCodes.
Ken Wakasaf2789812012-09-04 12:49:46 +090096void ProximityInfo::initializeCodePointToKeyIndex() {
97 memset(mCodePointToKeyIndex, -1, sizeof(mCodePointToKeyIndex));
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090098 for (int i = 0; i < KEY_COUNT; ++i) {
Ken Wakasaf2789812012-09-04 12:49:46 +090099 const int code = mKeyCodePoints[i];
Yusuke Nojimaad358352011-09-29 16:44:54 +0900100 if (0 <= code && code <= MAX_CHAR_CODE) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900101 mCodePointToKeyIndex[code] = i;
Yusuke Nojimaad358352011-09-29 16:44:54 +0900102 }
Yusuke Nojima0e1f6562011-09-21 12:02:47 +0900103 }
satok8fbd5522011-02-22 17:28:55 +0900104}
105
106ProximityInfo::~ProximityInfo() {
107 delete[] mProximityCharsArray;
108}
satok817e5172011-03-04 06:06:45 -0800109
110inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const {
satok3c4bb772011-03-04 22:50:19 -0800111 return ((y / CELL_HEIGHT) * GRID_WIDTH + (x / CELL_WIDTH))
satok817e5172011-03-04 06:06:45 -0800112 * MAX_PROXIMITY_CHARS_SIZE;
satok8fbd5522011-02-22 17:28:55 +0900113}
satok817e5172011-03-04 06:06:45 -0800114
115bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
satok744dab62011-12-15 22:29:05 +0900116 if (x < 0 || y < 0) {
117 if (DEBUG_DICT) {
satok9fb6f472012-01-13 18:01:22 +0900118 AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
Ken Wakasaf2789812012-09-04 12:49:46 +0900119 // TODO: Enable this assertion.
120 //assert(false);
satok744dab62011-12-15 22:29:05 +0900121 }
122 return false;
123 }
124
satok817e5172011-03-04 06:06:45 -0800125 const int startIndex = getStartIndexFromCoordinates(x, y);
126 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900127 AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
satok817e5172011-03-04 06:06:45 -0800128 }
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900129 int32_t *proximityCharsArray = mProximityCharsArray;
satok817e5172011-03-04 06:06:45 -0800130 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
131 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900132 AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
satok817e5172011-03-04 06:06:45 -0800133 }
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900134 if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
satok817e5172011-03-04 06:06:45 -0800135 return true;
136 }
137 }
138 return false;
139}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900140
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900141static inline float getNormalizedSquaredDistanceFloat(float x1, float y1, float x2, float y2,
142 float scale) {
143 return squareFloat((x1 - x2) / scale) + squareFloat((y1 - y2) / scale);
144}
145
146float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloat(
147 const int keyId, const int x, const int y) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900148 const float centerX = static_cast<float>(getKeyCenterXOfKeyIdG(keyId));
149 const float centerY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId));
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900150 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 getNormalizedSquaredDistanceFloat(centerX, centerY, touchX, touchY, keyWidth);
154}
155
satok9df4a452012-03-23 16:05:18 +0900156int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
Jean Chalard081616c2012-03-22 17:39:27 +0900157 if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
satoka70ee6e2012-03-07 15:12:22 +0900158 const int left = mKeyXCoordinates[keyId];
159 const int top = mKeyYCoordinates[keyId];
satok1caff472012-03-14 23:17:12 +0900160 const int right = left + mKeyWidths[keyId];
satoka70ee6e2012-03-07 15:12:22 +0900161 const int bottom = top + mKeyHeights[keyId];
162 const int edgeX = x < left ? left : (x > right ? right : x);
163 const int edgeY = y < top ? top : (y > bottom ? bottom : y);
164 const int dx = x - edgeX;
165 const int dy = y - edgeY;
166 return dx * dx + dy * dy;
167}
168
169void ProximityInfo::calculateNearbyKeyCodes(
satok9df4a452012-03-23 16:05:18 +0900170 const int x, const int y, const int32_t primaryKey, int *inputCodes) const {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900171 int32_t *proximityCharsArray = mProximityCharsArray;
satoka70ee6e2012-03-07 15:12:22 +0900172 int insertPos = 0;
173 inputCodes[insertPos++] = primaryKey;
174 const int startIndex = getStartIndexFromCoordinates(x, y);
Jean Chalard52612a02012-03-23 19:38:23 +0900175 if (startIndex >= 0) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900176 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900177 const int32_t c = proximityCharsArray[startIndex + i];
Jean Chalard88ec1252012-03-23 19:25:10 +0900178 if (c < KEYCODE_SPACE || c == primaryKey) {
179 continue;
180 }
Ken Wakasaf2789812012-09-04 12:49:46 +0900181 const int keyIndex = getKeyIndexOf(c);
Jean Chalard88ec1252012-03-23 19:25:10 +0900182 const bool onKey = isOnKey(keyIndex, x, y);
183 const int distance = squaredDistanceToEdge(keyIndex, x, y);
184 if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) {
185 inputCodes[insertPos++] = c;
186 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
187 if (DEBUG_DICT) {
188 assert(false);
189 }
190 return;
satok0cb20972012-03-13 22:07:56 +0900191 }
satoka70ee6e2012-03-07 15:12:22 +0900192 }
193 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900194 const int additionalProximitySize =
Ken Wakasa01511452012-08-09 15:58:15 +0900195 AdditionalProximityChars::getAdditionalCharsSize(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900196 if (additionalProximitySize > 0) {
197 inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
198 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
Jean Chalard3094d122012-03-23 19:36:07 +0900199 if (DEBUG_DICT) {
200 assert(false);
201 }
202 return;
satok1caff472012-03-14 23:17:12 +0900203 }
satok1caff472012-03-14 23:17:12 +0900204
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900205 const int32_t *additionalProximityChars =
Ken Wakasa01511452012-08-09 15:58:15 +0900206 AdditionalProximityChars::getAdditionalChars(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900207 for (int j = 0; j < additionalProximitySize; ++j) {
208 const int32_t ac = additionalProximityChars[j];
209 int k = 0;
210 for (; k < insertPos; ++k) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900211 if (static_cast<int>(ac) == inputCodes[k]) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900212 break;
213 }
satok5eec5742012-03-13 18:26:23 +0900214 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900215 if (k < insertPos) {
216 continue;
satok0cb20972012-03-13 22:07:56 +0900217 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900218 inputCodes[insertPos++] = ac;
219 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
220 if (DEBUG_DICT) {
221 assert(false);
222 }
223 return;
Jean Chalard3094d122012-03-23 19:36:07 +0900224 }
satok0cb20972012-03-13 22:07:56 +0900225 }
satok5eec5742012-03-13 18:26:23 +0900226 }
Jean Chalard52612a02012-03-23 19:38:23 +0900227 }
satok0cb20972012-03-13 22:07:56 +0900228 // Add a delimiter for the proximity characters
satok1caff472012-03-14 23:17:12 +0900229 for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900230 inputCodes[i] = NOT_A_CODE_POINT;
satok1caff472012-03-14 23:17:12 +0900231 }
satoka70ee6e2012-03-07 15:12:22 +0900232}
233
Ken Wakasaf2789812012-09-04 12:49:46 +0900234int ProximityInfo::getKeyIndexOf(const int c) const {
satok1caff472012-03-14 23:17:12 +0900235 if (KEY_COUNT == 0) {
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900236 // We do not have the coordinate data
Jean Chalardbbc25602012-03-23 17:05:03 +0900237 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900238 }
Tadashi G. Takaoka6e3cb272011-11-11 14:26:13 +0900239 const unsigned short baseLowerC = toBaseLowerCase(c);
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900240 if (baseLowerC > MAX_CHAR_CODE) {
Jean Chalardbbc25602012-03-23 17:05:03 +0900241 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900242 }
Ken Wakasaf2789812012-09-04 12:49:46 +0900243 return mCodePointToKeyIndex[baseLowerC];
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900244}
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900245
Ken Wakasaf2789812012-09-04 12:49:46 +0900246int ProximityInfo::getCodePointOf(const int keyIndex) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900247 if (keyIndex < 0 || keyIndex >= KEY_COUNT) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900248 return NOT_A_CODE_POINT;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900249 }
Ken Wakasaf2789812012-09-04 12:49:46 +0900250 return mKeyIndexToCodePointG[keyIndex];
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900251}
252
253void ProximityInfo::initializeG() {
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900254 // TODO: Optimize
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900255 for (int i = 0; i < KEY_COUNT; ++i) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900256 const int code = mKeyCodePoints[i];
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900257 const int lowerCode = toBaseLowerCase(code);
258 mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
259 mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
260 if (code != lowerCode && lowerCode >= 0 && lowerCode <= MAX_CHAR_CODE) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900261 mCodePointToKeyIndex[lowerCode] = i;
262 mKeyIndexToCodePointG[i] = lowerCode;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900263 } else {
Ken Wakasaf2789812012-09-04 12:49:46 +0900264 mKeyIndexToCodePointG[i] = code;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900265 }
266 }
267 for (int i = 0; i < KEY_COUNT; i++) {
268 mKeyKeyDistancesG[i][i] = 0;
269 for (int j = i + 1; j < KEY_COUNT; j++) {
Ken Wakasabcec82d2012-08-12 11:10:48 +0900270 mKeyKeyDistancesG[i][j] = getDistanceInt(
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900271 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
272 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
273 }
274 }
275}
276
Ken Wakasaf2789812012-09-04 12:49:46 +0900277float ProximityInfo::getKeyCenterXOfCodePointG(int charCode) const {
278 return getKeyCenterXOfKeyIdG(getKeyIndexOf(charCode));
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900279}
280
Ken Wakasaf2789812012-09-04 12:49:46 +0900281float ProximityInfo::getKeyCenterYOfCodePointG(int charCode) const {
282 return getKeyCenterYOfKeyIdG(getKeyIndexOf(charCode));
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900283}
284
Ken Wakasaf2789812012-09-04 12:49:46 +0900285float ProximityInfo::getKeyCenterXOfKeyIdG(int keyId) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900286 if (keyId >= 0) {
287 return mCenterXsG[keyId];
288 }
289 return 0;
290}
291
Ken Wakasaf2789812012-09-04 12:49:46 +0900292float ProximityInfo::getKeyCenterYOfKeyIdG(int keyId) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900293 if (keyId >= 0) {
294 return mCenterYsG[keyId];
295 }
296 return 0;
297}
298
299int ProximityInfo::getKeyKeyDistanceG(int key0, int key1) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900300 const int keyId0 = getKeyIndexOf(key0);
301 const int keyId1 = getKeyIndexOf(key1);
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900302 if (keyId0 >= 0 && keyId1 >= 0) {
303 return mKeyKeyDistancesG[keyId0][keyId1];
304 }
Jean Chalard07aea402012-08-29 20:09:27 +0900305 return MAX_POINT_TO_KEY_LENGTH;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900306}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900307} // namespace latinime