blob: 1b9bac0f07bcc3121802bda3cb7f15290e24384b [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) {
Ken Wakasa063c3e22012-08-10 22:10:04 +090037 memset(buffer, 0, len * sizeof(jint));
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) {
Ken Wakasa063c3e22012-08-10 22:10:04 +090046 memset(buffer, 0, len * sizeof(jfloat));
Ken Wakasabb005f72012-08-08 20:43:47 +090047 }
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
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900138static inline float getNormalizedSquaredDistanceFloat(float x1, float y1, float x2, float y2,
139 float scale) {
140 return squareFloat((x1 - x2) / scale) + squareFloat((y1 - y2) / scale);
141}
142
143float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloat(
144 const int keyId, const int x, const int y) const {
145 const float centerX = static_cast<float>(getKeyCenterXOfIdG(keyId));
146 const float centerY = static_cast<float>(getKeyCenterYOfIdG(keyId));
147 const float touchX = static_cast<float>(x);
148 const float touchY = static_cast<float>(y);
149 const float keyWidth = static_cast<float>(getMostCommonKeyWidth());
150 return getNormalizedSquaredDistanceFloat(centerX, centerY, touchX, touchY, keyWidth);
151}
152
satok9df4a452012-03-23 16:05:18 +0900153int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
Jean Chalard081616c2012-03-22 17:39:27 +0900154 if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
satoka70ee6e2012-03-07 15:12:22 +0900155 const int left = mKeyXCoordinates[keyId];
156 const int top = mKeyYCoordinates[keyId];
satok1caff472012-03-14 23:17:12 +0900157 const int right = left + mKeyWidths[keyId];
satoka70ee6e2012-03-07 15:12:22 +0900158 const int bottom = top + mKeyHeights[keyId];
159 const int edgeX = x < left ? left : (x > right ? right : x);
160 const int edgeY = y < top ? top : (y > bottom ? bottom : y);
161 const int dx = x - edgeX;
162 const int dy = y - edgeY;
163 return dx * dx + dy * dy;
164}
165
166void ProximityInfo::calculateNearbyKeyCodes(
satok9df4a452012-03-23 16:05:18 +0900167 const int x, const int y, const int32_t primaryKey, int *inputCodes) const {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900168 int32_t *proximityCharsArray = mProximityCharsArray;
satoka70ee6e2012-03-07 15:12:22 +0900169 int insertPos = 0;
170 inputCodes[insertPos++] = primaryKey;
171 const int startIndex = getStartIndexFromCoordinates(x, y);
Jean Chalard52612a02012-03-23 19:38:23 +0900172 if (startIndex >= 0) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900173 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900174 const int32_t c = proximityCharsArray[startIndex + i];
Jean Chalard88ec1252012-03-23 19:25:10 +0900175 if (c < KEYCODE_SPACE || c == primaryKey) {
176 continue;
177 }
178 const int keyIndex = getKeyIndex(c);
179 const bool onKey = isOnKey(keyIndex, x, y);
180 const int distance = squaredDistanceToEdge(keyIndex, x, y);
181 if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) {
182 inputCodes[insertPos++] = c;
183 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
184 if (DEBUG_DICT) {
185 assert(false);
186 }
187 return;
satok0cb20972012-03-13 22:07:56 +0900188 }
satoka70ee6e2012-03-07 15:12:22 +0900189 }
190 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900191 const int additionalProximitySize =
Ken Wakasa01511452012-08-09 15:58:15 +0900192 AdditionalProximityChars::getAdditionalCharsSize(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900193 if (additionalProximitySize > 0) {
194 inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
195 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
Jean Chalard3094d122012-03-23 19:36:07 +0900196 if (DEBUG_DICT) {
197 assert(false);
198 }
199 return;
satok1caff472012-03-14 23:17:12 +0900200 }
satok1caff472012-03-14 23:17:12 +0900201
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900202 const int32_t *additionalProximityChars =
Ken Wakasa01511452012-08-09 15:58:15 +0900203 AdditionalProximityChars::getAdditionalChars(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900204 for (int j = 0; j < additionalProximitySize; ++j) {
205 const int32_t ac = additionalProximityChars[j];
206 int k = 0;
207 for (; k < insertPos; ++k) {
208 if ((int)ac == inputCodes[k]) {
209 break;
210 }
satok5eec5742012-03-13 18:26:23 +0900211 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900212 if (k < insertPos) {
213 continue;
satok0cb20972012-03-13 22:07:56 +0900214 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900215 inputCodes[insertPos++] = ac;
216 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
217 if (DEBUG_DICT) {
218 assert(false);
219 }
220 return;
Jean Chalard3094d122012-03-23 19:36:07 +0900221 }
satok0cb20972012-03-13 22:07:56 +0900222 }
satok5eec5742012-03-13 18:26:23 +0900223 }
Jean Chalard52612a02012-03-23 19:38:23 +0900224 }
satok0cb20972012-03-13 22:07:56 +0900225 // Add a delimiter for the proximity characters
satok1caff472012-03-14 23:17:12 +0900226 for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
227 inputCodes[i] = NOT_A_CODE;
228 }
satoka70ee6e2012-03-07 15:12:22 +0900229}
230
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900231int ProximityInfo::getKeyIndex(const int c) const {
satok1caff472012-03-14 23:17:12 +0900232 if (KEY_COUNT == 0) {
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900233 // We do not have the coordinate data
Jean Chalardbbc25602012-03-23 17:05:03 +0900234 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900235 }
Tadashi G. Takaoka6e3cb272011-11-11 14:26:13 +0900236 const unsigned short baseLowerC = toBaseLowerCase(c);
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900237 if (baseLowerC > MAX_CHAR_CODE) {
Jean Chalardbbc25602012-03-23 17:05:03 +0900238 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900239 }
240 return mCodeToKeyIndex[baseLowerC];
241}
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900242
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900243int ProximityInfo::getKeyCode(const int keyIndex) const {
244 if (keyIndex < 0 || keyIndex >= KEY_COUNT) {
245 return NOT_AN_INDEX;
246 }
247 return mKeyToCodeIndexG[keyIndex];
248}
249
250void ProximityInfo::initializeG() {
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900251 // TODO: Optimize
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900252 for (int i = 0; i < KEY_COUNT; ++i) {
253 const int code = mKeyCharCodes[i];
254 const int lowerCode = toBaseLowerCase(code);
255 mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
256 mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
257 if (code != lowerCode && lowerCode >= 0 && lowerCode <= MAX_CHAR_CODE) {
258 mCodeToKeyIndex[lowerCode] = i;
259 mKeyToCodeIndexG[i] = lowerCode;
260 } else {
261 mKeyToCodeIndexG[i] = code;
262 }
263 }
264 for (int i = 0; i < KEY_COUNT; i++) {
265 mKeyKeyDistancesG[i][i] = 0;
266 for (int j = i + 1; j < KEY_COUNT; j++) {
Ken Wakasabcec82d2012-08-12 11:10:48 +0900267 mKeyKeyDistancesG[i][j] = getDistanceInt(
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900268 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
269 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
270 }
271 }
272}
273
274float ProximityInfo::getKeyCenterXOfCharG(int charCode) const {
275 return getKeyCenterXOfIdG(getKeyIndex(charCode));
276}
277
278float ProximityInfo::getKeyCenterYOfCharG(int charCode) const {
279 return getKeyCenterYOfIdG(getKeyIndex(charCode));
280}
281
282float ProximityInfo::getKeyCenterXOfIdG(int keyId) const {
283 if (keyId >= 0) {
284 return mCenterXsG[keyId];
285 }
286 return 0;
287}
288
289float ProximityInfo::getKeyCenterYOfIdG(int keyId) const {
290 if (keyId >= 0) {
291 return mCenterYsG[keyId];
292 }
293 return 0;
294}
295
296int ProximityInfo::getKeyKeyDistanceG(int key0, int key1) const {
297 const int keyId0 = getKeyIndex(key0);
298 const int keyId1 = getKeyIndex(key1);
299 if (keyId0 >= 0 && keyId1 >= 0) {
300 return mKeyKeyDistancesG[keyId0][keyId1];
301 }
302 return 0;
303}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900304} // namespace latinime