blob: 2633913f7c1c5bfa886f70196bde80fc3591141c [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 Wakasabb005f72012-08-08 20:43:47 +090032static inline void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, jsize len,
33 jint *buffer) {
34 if (jArray && buffer) {
35 env->GetIntArrayRegion(jArray, 0, len, buffer);
36 } else if (buffer) {
37 memset(buffer, 0, len);
Yusuke Nojimade2f8422011-09-27 11:15:18 +090038 }
39}
40
Ken Wakasabb005f72012-08-08 20:43:47 +090041static inline void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray, jsize len,
42 jfloat *buffer) {
43 if (jArray && buffer) {
44 env->GetFloatArrayRegion(jArray, 0, len, buffer);
45 } else if (buffer) {
46 memset(buffer, 0, len);
47 }
48}
49
Ken Wakasa01511452012-08-09 15:58:15 +090050ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int maxProximityCharsSize,
satok552c3c22012-03-13 16:33:47 +090051 const int keyboardWidth, const int keyboardHeight, const int gridWidth,
Ken Wakasabb005f72012-08-08 20:43:47 +090052 const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars,
53 const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates,
54 const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes,
55 const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs,
56 const jfloatArray sweetSpotRadii)
satok817e5172011-03-04 06:06:45 -080057 : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth),
58 KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight),
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +090059 MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth),
satoka70ee6e2012-03-07 15:12:22 +090060 MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth),
satok817e5172011-03-04 06:06:45 -080061 CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090062 CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
Yusuke Nojima258bfe62011-09-28 12:59:43 +090063 KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)),
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +090064 HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates
65 && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
Ken Wakasa01511452012-08-09 15:58:15 +090066 && sweetSpotCenterYs && sweetSpotRadii) {
satok1035bc92012-06-13 16:07:54 -070067 const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
satok817e5172011-03-04 06:06:45 -080068 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +090069 AKLOGI("Create proximity info array %d", proximityGridLength);
satok817e5172011-03-04 06:06:45 -080070 }
Ken Wakasa01511452012-08-09 15:58:15 +090071 const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr);
Ken Wakasa9e0c7112012-08-09 22:26:58 +090072 if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) {
73 AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length);
74 assert(false);
75 }
76 memset(mLocaleStr, 0, sizeof(mLocaleStr));
77 env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr);
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +090078 mProximityCharsArray = new int32_t[proximityGridLength];
Ken Wakasabb005f72012-08-08 20:43:47 +090079 safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray);
80 safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
81 safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates);
82 safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths);
83 safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights);
84 safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCharCodes);
85 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs);
86 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs);
87 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii);
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090088 initializeCodeToKeyIndex();
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +090089 initializeG();
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090090}
91
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090092// Build the reversed look up table from the char code to the index in mKeyXCoordinates,
93// mKeyYCoordinates, mKeyWidths, mKeyHeights, mKeyCharCodes.
94void ProximityInfo::initializeCodeToKeyIndex() {
Yusuke Nojimaad358352011-09-29 16:44:54 +090095 memset(mCodeToKeyIndex, -1, (MAX_CHAR_CODE + 1) * sizeof(mCodeToKeyIndex[0]));
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090096 for (int i = 0; i < KEY_COUNT; ++i) {
97 const int code = mKeyCharCodes[i];
Yusuke Nojimaad358352011-09-29 16:44:54 +090098 if (0 <= code && code <= MAX_CHAR_CODE) {
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090099 mCodeToKeyIndex[code] = i;
Yusuke Nojimaad358352011-09-29 16:44:54 +0900100 }
Yusuke Nojima0e1f6562011-09-21 12:02:47 +0900101 }
satok8fbd5522011-02-22 17:28:55 +0900102}
103
104ProximityInfo::~ProximityInfo() {
105 delete[] mProximityCharsArray;
106}
satok817e5172011-03-04 06:06:45 -0800107
108inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const {
satok3c4bb772011-03-04 22:50:19 -0800109 return ((y / CELL_HEIGHT) * GRID_WIDTH + (x / CELL_WIDTH))
satok817e5172011-03-04 06:06:45 -0800110 * MAX_PROXIMITY_CHARS_SIZE;
satok8fbd5522011-02-22 17:28:55 +0900111}
satok817e5172011-03-04 06:06:45 -0800112
113bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
satok744dab62011-12-15 22:29:05 +0900114 if (x < 0 || y < 0) {
115 if (DEBUG_DICT) {
satok9fb6f472012-01-13 18:01:22 +0900116 AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
satok1a6da632011-12-16 23:15:06 +0900117 assert(false);
satok744dab62011-12-15 22:29:05 +0900118 }
119 return false;
120 }
121
satok817e5172011-03-04 06:06:45 -0800122 const int startIndex = getStartIndexFromCoordinates(x, y);
123 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900124 AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
satok817e5172011-03-04 06:06:45 -0800125 }
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900126 int32_t *proximityCharsArray = mProximityCharsArray;
satok817e5172011-03-04 06:06:45 -0800127 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
128 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900129 AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
satok817e5172011-03-04 06:06:45 -0800130 }
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900131 if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
satok817e5172011-03-04 06:06:45 -0800132 return true;
133 }
134 }
135 return false;
136}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900137
satok9df4a452012-03-23 16:05:18 +0900138int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
Jean Chalard081616c2012-03-22 17:39:27 +0900139 if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
satoka70ee6e2012-03-07 15:12:22 +0900140 const int left = mKeyXCoordinates[keyId];
141 const int top = mKeyYCoordinates[keyId];
satok1caff472012-03-14 23:17:12 +0900142 const int right = left + mKeyWidths[keyId];
satoka70ee6e2012-03-07 15:12:22 +0900143 const int bottom = top + mKeyHeights[keyId];
144 const int edgeX = x < left ? left : (x > right ? right : x);
145 const int edgeY = y < top ? top : (y > bottom ? bottom : y);
146 const int dx = x - edgeX;
147 const int dy = y - edgeY;
148 return dx * dx + dy * dy;
149}
150
151void ProximityInfo::calculateNearbyKeyCodes(
satok9df4a452012-03-23 16:05:18 +0900152 const int x, const int y, const int32_t primaryKey, int *inputCodes) const {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900153 int32_t *proximityCharsArray = mProximityCharsArray;
satoka70ee6e2012-03-07 15:12:22 +0900154 int insertPos = 0;
155 inputCodes[insertPos++] = primaryKey;
156 const int startIndex = getStartIndexFromCoordinates(x, y);
Jean Chalard52612a02012-03-23 19:38:23 +0900157 if (startIndex >= 0) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900158 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900159 const int32_t c = proximityCharsArray[startIndex + i];
Jean Chalard88ec1252012-03-23 19:25:10 +0900160 if (c < KEYCODE_SPACE || c == primaryKey) {
161 continue;
162 }
163 const int keyIndex = getKeyIndex(c);
164 const bool onKey = isOnKey(keyIndex, x, y);
165 const int distance = squaredDistanceToEdge(keyIndex, x, y);
166 if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) {
167 inputCodes[insertPos++] = c;
168 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
169 if (DEBUG_DICT) {
170 assert(false);
171 }
172 return;
satok0cb20972012-03-13 22:07:56 +0900173 }
satoka70ee6e2012-03-07 15:12:22 +0900174 }
175 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900176 const int additionalProximitySize =
Ken Wakasa01511452012-08-09 15:58:15 +0900177 AdditionalProximityChars::getAdditionalCharsSize(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900178 if (additionalProximitySize > 0) {
179 inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
180 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
Jean Chalard3094d122012-03-23 19:36:07 +0900181 if (DEBUG_DICT) {
182 assert(false);
183 }
184 return;
satok1caff472012-03-14 23:17:12 +0900185 }
satok1caff472012-03-14 23:17:12 +0900186
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900187 const int32_t *additionalProximityChars =
Ken Wakasa01511452012-08-09 15:58:15 +0900188 AdditionalProximityChars::getAdditionalChars(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900189 for (int j = 0; j < additionalProximitySize; ++j) {
190 const int32_t ac = additionalProximityChars[j];
191 int k = 0;
192 for (; k < insertPos; ++k) {
193 if ((int)ac == inputCodes[k]) {
194 break;
195 }
satok5eec5742012-03-13 18:26:23 +0900196 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900197 if (k < insertPos) {
198 continue;
satok0cb20972012-03-13 22:07:56 +0900199 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900200 inputCodes[insertPos++] = ac;
201 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
202 if (DEBUG_DICT) {
203 assert(false);
204 }
205 return;
Jean Chalard3094d122012-03-23 19:36:07 +0900206 }
satok0cb20972012-03-13 22:07:56 +0900207 }
satok5eec5742012-03-13 18:26:23 +0900208 }
Jean Chalard52612a02012-03-23 19:38:23 +0900209 }
satok0cb20972012-03-13 22:07:56 +0900210 // Add a delimiter for the proximity characters
satok1caff472012-03-14 23:17:12 +0900211 for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
212 inputCodes[i] = NOT_A_CODE;
213 }
satoka70ee6e2012-03-07 15:12:22 +0900214}
215
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900216int ProximityInfo::getKeyIndex(const int c) const {
satok1caff472012-03-14 23:17:12 +0900217 if (KEY_COUNT == 0) {
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900218 // We do not have the coordinate data
Jean Chalardbbc25602012-03-23 17:05:03 +0900219 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900220 }
Tadashi G. Takaoka6e3cb272011-11-11 14:26:13 +0900221 const unsigned short baseLowerC = toBaseLowerCase(c);
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900222 if (baseLowerC > MAX_CHAR_CODE) {
Jean Chalardbbc25602012-03-23 17:05:03 +0900223 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900224 }
225 return mCodeToKeyIndex[baseLowerC];
226}
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900227
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900228int ProximityInfo::getKeyCode(const int keyIndex) const {
229 if (keyIndex < 0 || keyIndex >= KEY_COUNT) {
230 return NOT_AN_INDEX;
231 }
232 return mKeyToCodeIndexG[keyIndex];
233}
234
235void ProximityInfo::initializeG() {
236 for (int i = 0; i < KEY_COUNT; ++i) {
237 const int code = mKeyCharCodes[i];
238 const int lowerCode = toBaseLowerCase(code);
239 mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
240 mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
241 if (code != lowerCode && lowerCode >= 0 && lowerCode <= MAX_CHAR_CODE) {
242 mCodeToKeyIndex[lowerCode] = i;
243 mKeyToCodeIndexG[i] = lowerCode;
244 } else {
245 mKeyToCodeIndexG[i] = code;
246 }
247 }
248 for (int i = 0; i < KEY_COUNT; i++) {
249 mKeyKeyDistancesG[i][i] = 0;
250 for (int j = i + 1; j < KEY_COUNT; j++) {
251 mKeyKeyDistancesG[i][j] = getDistance(
252 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
253 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
254 }
255 }
256}
257
258float ProximityInfo::getKeyCenterXOfCharG(int charCode) const {
259 return getKeyCenterXOfIdG(getKeyIndex(charCode));
260}
261
262float ProximityInfo::getKeyCenterYOfCharG(int charCode) const {
263 return getKeyCenterYOfIdG(getKeyIndex(charCode));
264}
265
266float ProximityInfo::getKeyCenterXOfIdG(int keyId) const {
267 if (keyId >= 0) {
268 return mCenterXsG[keyId];
269 }
270 return 0;
271}
272
273float ProximityInfo::getKeyCenterYOfIdG(int keyId) const {
274 if (keyId >= 0) {
275 return mCenterYsG[keyId];
276 }
277 return 0;
278}
279
280int ProximityInfo::getKeyKeyDistanceG(int key0, int key1) const {
281 const int keyId0 = getKeyIndex(key0);
282 const int keyId1 = getKeyIndex(key1);
283 if (keyId0 >= 0 && keyId1 >= 0) {
284 return mKeyKeyDistancesG[keyId0][keyId1];
285 }
286 return 0;
287}
288
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900289// TODO: [Staging] Optimize
290void ProximityInfo::getCenters(int *centerXs, int *centerYs, int *codeToKeyIndex,
291 int *keyToCodeIndex, int *keyCount, int *keyWidth) const {
292 *keyCount = KEY_COUNT;
Ken Wakasa77e8e812012-08-02 19:48:08 +0900293 *keyWidth = sqrt(static_cast<float>(MOST_COMMON_KEY_WIDTH_SQUARE));
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900294
295 for (int i = 0; i < KEY_COUNT; ++i) {
296 const int code = mKeyCharCodes[i];
297 const int lowerCode = toBaseLowerCase(code);
298 centerXs[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
299 centerYs[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
300 codeToKeyIndex[code] = i;
301 if (code != lowerCode && lowerCode >= 0 && lowerCode <= MAX_CHAR_CODE) {
302 codeToKeyIndex[lowerCode] = i;
303 keyToCodeIndex[i] = lowerCode;
304 } else {
305 keyToCodeIndex[i] = code;
306 }
307 }
308}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900309} // namespace latinime