blob: c9f83b62c265ef7d393bdcdd7aa3888469d5b13a [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)),
Keisuke Kuroyanagi95a49a52012-09-04 17:00:24 +090065 KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight),
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +090066 HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates
67 && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs
Ken Wakasa162c2112012-08-24 14:51:15 +090068 && sweetSpotCenterYs && sweetSpotRadii),
69 mProximityCharsArray(new int32_t[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE
Tom Ouyang13216852012-09-03 12:50:21 -070070 /* proximityGridLength */]),
71 mCodeToKeyMap() {
satok1035bc92012-06-13 16:07:54 -070072 const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
satok817e5172011-03-04 06:06:45 -080073 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +090074 AKLOGI("Create proximity info array %d", proximityGridLength);
satok817e5172011-03-04 06:06:45 -080075 }
Ken Wakasa01511452012-08-09 15:58:15 +090076 const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr);
Ken Wakasa9e0c7112012-08-09 22:26:58 +090077 if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) {
78 AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length);
79 assert(false);
80 }
81 memset(mLocaleStr, 0, sizeof(mLocaleStr));
82 env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr);
Ken Wakasabb005f72012-08-08 20:43:47 +090083 safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray);
84 safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
85 safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates);
86 safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths);
87 safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights);
Ken Wakasaf2789812012-09-04 12:49:46 +090088 safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCodePoints);
Ken Wakasabb005f72012-08-08 20:43:47 +090089 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs);
90 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs);
91 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii);
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +090092 initializeG();
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090093}
94
satok8fbd5522011-02-22 17:28:55 +090095ProximityInfo::~ProximityInfo() {
96 delete[] mProximityCharsArray;
97}
satok817e5172011-03-04 06:06:45 -080098
99inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const {
satok3c4bb772011-03-04 22:50:19 -0800100 return ((y / CELL_HEIGHT) * GRID_WIDTH + (x / CELL_WIDTH))
satok817e5172011-03-04 06:06:45 -0800101 * MAX_PROXIMITY_CHARS_SIZE;
satok8fbd5522011-02-22 17:28:55 +0900102}
satok817e5172011-03-04 06:06:45 -0800103
104bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
satok744dab62011-12-15 22:29:05 +0900105 if (x < 0 || y < 0) {
106 if (DEBUG_DICT) {
satok9fb6f472012-01-13 18:01:22 +0900107 AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
Ken Wakasaf2789812012-09-04 12:49:46 +0900108 // TODO: Enable this assertion.
109 //assert(false);
satok744dab62011-12-15 22:29:05 +0900110 }
111 return false;
112 }
113
satok817e5172011-03-04 06:06:45 -0800114 const int startIndex = getStartIndexFromCoordinates(x, y);
115 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900116 AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
satok817e5172011-03-04 06:06:45 -0800117 }
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900118 int32_t *proximityCharsArray = mProximityCharsArray;
satok817e5172011-03-04 06:06:45 -0800119 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
120 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900121 AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
satok817e5172011-03-04 06:06:45 -0800122 }
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900123 if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
satok817e5172011-03-04 06:06:45 -0800124 return true;
125 }
126 }
127 return false;
128}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900129
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900130static inline float getNormalizedSquaredDistanceFloat(float x1, float y1, float x2, float y2,
131 float scale) {
Satoshi Kataokaf4554d82012-09-12 20:50:21 +0900132 const float deltaX = x1 - x2;
133 const float deltaY = y1 - y2;
134 return (SQUARE_FLOAT(deltaX) + SQUARE_FLOAT(deltaY)) / SQUARE_FLOAT(scale);
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900135}
136
137float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloat(
138 const int keyId, const int x, const int y) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900139 const float centerX = static_cast<float>(getKeyCenterXOfKeyIdG(keyId));
140 const float centerY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId));
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900141 const float touchX = static_cast<float>(x);
142 const float touchY = static_cast<float>(y);
143 const float keyWidth = static_cast<float>(getMostCommonKeyWidth());
144 return getNormalizedSquaredDistanceFloat(centerX, centerY, touchX, touchY, keyWidth);
145}
146
satok9df4a452012-03-23 16:05:18 +0900147int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
Jean Chalard081616c2012-03-22 17:39:27 +0900148 if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
satoka70ee6e2012-03-07 15:12:22 +0900149 const int left = mKeyXCoordinates[keyId];
150 const int top = mKeyYCoordinates[keyId];
satok1caff472012-03-14 23:17:12 +0900151 const int right = left + mKeyWidths[keyId];
satoka70ee6e2012-03-07 15:12:22 +0900152 const int bottom = top + mKeyHeights[keyId];
153 const int edgeX = x < left ? left : (x > right ? right : x);
154 const int edgeY = y < top ? top : (y > bottom ? bottom : y);
155 const int dx = x - edgeX;
156 const int dy = y - edgeY;
157 return dx * dx + dy * dy;
158}
159
160void ProximityInfo::calculateNearbyKeyCodes(
satok9df4a452012-03-23 16:05:18 +0900161 const int x, const int y, const int32_t primaryKey, int *inputCodes) const {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900162 int32_t *proximityCharsArray = mProximityCharsArray;
satoka70ee6e2012-03-07 15:12:22 +0900163 int insertPos = 0;
164 inputCodes[insertPos++] = primaryKey;
165 const int startIndex = getStartIndexFromCoordinates(x, y);
Jean Chalard52612a02012-03-23 19:38:23 +0900166 if (startIndex >= 0) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900167 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900168 const int32_t c = proximityCharsArray[startIndex + i];
Jean Chalard88ec1252012-03-23 19:25:10 +0900169 if (c < KEYCODE_SPACE || c == primaryKey) {
170 continue;
171 }
Ken Wakasaf2789812012-09-04 12:49:46 +0900172 const int keyIndex = getKeyIndexOf(c);
Jean Chalard88ec1252012-03-23 19:25:10 +0900173 const bool onKey = isOnKey(keyIndex, x, y);
174 const int distance = squaredDistanceToEdge(keyIndex, x, y);
175 if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) {
176 inputCodes[insertPos++] = c;
177 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
178 if (DEBUG_DICT) {
179 assert(false);
180 }
181 return;
satok0cb20972012-03-13 22:07:56 +0900182 }
satoka70ee6e2012-03-07 15:12:22 +0900183 }
184 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900185 const int additionalProximitySize =
Ken Wakasa01511452012-08-09 15:58:15 +0900186 AdditionalProximityChars::getAdditionalCharsSize(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900187 if (additionalProximitySize > 0) {
188 inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
189 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
Jean Chalard3094d122012-03-23 19:36:07 +0900190 if (DEBUG_DICT) {
191 assert(false);
192 }
193 return;
satok1caff472012-03-14 23:17:12 +0900194 }
satok1caff472012-03-14 23:17:12 +0900195
Ken Wakasa0bbb9172012-07-25 17:51:43 +0900196 const int32_t *additionalProximityChars =
Ken Wakasa01511452012-08-09 15:58:15 +0900197 AdditionalProximityChars::getAdditionalChars(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900198 for (int j = 0; j < additionalProximitySize; ++j) {
199 const int32_t ac = additionalProximityChars[j];
200 int k = 0;
201 for (; k < insertPos; ++k) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900202 if (static_cast<int>(ac) == inputCodes[k]) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900203 break;
204 }
satok5eec5742012-03-13 18:26:23 +0900205 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900206 if (k < insertPos) {
207 continue;
satok0cb20972012-03-13 22:07:56 +0900208 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900209 inputCodes[insertPos++] = ac;
210 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
211 if (DEBUG_DICT) {
212 assert(false);
213 }
214 return;
Jean Chalard3094d122012-03-23 19:36:07 +0900215 }
satok0cb20972012-03-13 22:07:56 +0900216 }
satok5eec5742012-03-13 18:26:23 +0900217 }
Jean Chalard52612a02012-03-23 19:38:23 +0900218 }
satok0cb20972012-03-13 22:07:56 +0900219 // Add a delimiter for the proximity characters
satok1caff472012-03-14 23:17:12 +0900220 for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900221 inputCodes[i] = NOT_A_CODE_POINT;
satok1caff472012-03-14 23:17:12 +0900222 }
satoka70ee6e2012-03-07 15:12:22 +0900223}
224
Ken Wakasaf2789812012-09-04 12:49:46 +0900225int ProximityInfo::getKeyIndexOf(const int c) const {
satok1caff472012-03-14 23:17:12 +0900226 if (KEY_COUNT == 0) {
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900227 // We do not have the coordinate data
Jean Chalardbbc25602012-03-23 17:05:03 +0900228 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900229 }
Tom Ouyang13216852012-09-03 12:50:21 -0700230 const int baseLowerC = static_cast<int>(toBaseLowerCase(c));
231 hash_map_compat<int, int>::const_iterator mapPos = mCodeToKeyMap.find(baseLowerC);
232 if (mapPos != mCodeToKeyMap.end()) {
233 return mapPos->second;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900234 }
Tom Ouyang13216852012-09-03 12:50:21 -0700235 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900236}
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900237
Ken Wakasaf2789812012-09-04 12:49:46 +0900238int ProximityInfo::getCodePointOf(const int keyIndex) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900239 if (keyIndex < 0 || keyIndex >= KEY_COUNT) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900240 return NOT_A_CODE_POINT;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900241 }
Ken Wakasaf2789812012-09-04 12:49:46 +0900242 return mKeyIndexToCodePointG[keyIndex];
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900243}
244
245void ProximityInfo::initializeG() {
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900246 // TODO: Optimize
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900247 for (int i = 0; i < KEY_COUNT; ++i) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900248 const int code = mKeyCodePoints[i];
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900249 const int lowerCode = toBaseLowerCase(code);
250 mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
251 mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
Tom Ouyang13216852012-09-03 12:50:21 -0700252 mCodeToKeyMap[lowerCode] = i;
253 mKeyIndexToCodePointG[i] = lowerCode;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900254 }
255 for (int i = 0; i < KEY_COUNT; i++) {
256 mKeyKeyDistancesG[i][i] = 0;
257 for (int j = i + 1; j < KEY_COUNT; j++) {
Ken Wakasabcec82d2012-08-12 11:10:48 +0900258 mKeyKeyDistancesG[i][j] = getDistanceInt(
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900259 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
260 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
261 }
262 }
263}
264
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900265int ProximityInfo::getKeyCenterXOfCodePointG(int charCode) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900266 return getKeyCenterXOfKeyIdG(getKeyIndexOf(charCode));
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900267}
268
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900269int ProximityInfo::getKeyCenterYOfCodePointG(int charCode) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900270 return getKeyCenterYOfKeyIdG(getKeyIndexOf(charCode));
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900271}
272
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900273int ProximityInfo::getKeyCenterXOfKeyIdG(int keyId) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900274 if (keyId >= 0) {
275 return mCenterXsG[keyId];
276 }
277 return 0;
278}
279
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900280int ProximityInfo::getKeyCenterYOfKeyIdG(int keyId) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900281 if (keyId >= 0) {
282 return mCenterYsG[keyId];
283 }
284 return 0;
285}
286
287int ProximityInfo::getKeyKeyDistanceG(int key0, int key1) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900288 const int keyId0 = getKeyIndexOf(key0);
289 const int keyId1 = getKeyIndexOf(key1);
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900290 if (keyId0 >= 0 && keyId1 >= 0) {
291 return mKeyKeyDistancesG[keyId0][keyId1];
292 }
Jean Chalard07aea402012-08-29 20:09:27 +0900293 return MAX_POINT_TO_KEY_LENGTH;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900294}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900295} // namespace latinime