blob: ffe12ce490f3a2b60111401f4abe86736d96dfb1 [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>
Ken Wakasaf1008c52012-07-31 17:56:40 +090018#include <cstring>
satok8fbd5522011-02-22 17:28:55 +090019
satok817e5172011-03-04 06:06:45 -080020#define LOG_TAG "LatinIME: proximity_info.cpp"
21
satok552c3c22012-03-13 16:33:47 +090022#include "additional_proximity_chars.h"
Ken Wakasa77e8e812012-08-02 19:48:08 +090023#include "char_utils.h"
Ken Wakasa3b088a22012-05-16 23:05:32 +090024#include "defines.h"
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +090025#include "geometry_utils.h"
Ken Wakasabb005f72012-08-08 20:43:47 +090026#include "jni.h"
satok8fbd5522011-02-22 17:28:55 +090027#include "proximity_info.h"
28
29namespace latinime {
Ken Wakasace9e52a2011-06-18 13:09:55 +090030
Ken Wakasa162c2112012-08-24 14:51:15 +090031/* static */ const float ProximityInfo::NOT_A_DISTANCE_FLOAT = -1.0f;
32
Ken Wakasa6e663492012-11-03 02:50:47 +090033static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray,
34 jsize len, jint *buffer) {
Ken Wakasabb005f72012-08-08 20:43:47 +090035 if (jArray && buffer) {
36 env->GetIntArrayRegion(jArray, 0, len, buffer);
37 } else if (buffer) {
Ken Wakasa44d9c1e2012-11-01 17:05:08 +090038 memset(buffer, 0, len * sizeof(buffer[0]));
Yusuke Nojimade2f8422011-09-27 11:15:18 +090039 }
40}
41
Ken Wakasa6e663492012-11-03 02:50:47 +090042static AK_FORCE_INLINE void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray,
43 jsize len, jfloat *buffer) {
Ken Wakasabb005f72012-08-08 20:43:47 +090044 if (jArray && buffer) {
45 env->GetFloatArrayRegion(jArray, 0, len, buffer);
46 } else if (buffer) {
Ken Wakasa44d9c1e2012-11-01 17:05:08 +090047 memset(buffer, 0, len * sizeof(buffer[0]));
Ken Wakasabb005f72012-08-08 20:43:47 +090048 }
49}
50
Ken Wakasa01511452012-08-09 15:58:15 +090051ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int maxProximityCharsSize,
satok552c3c22012-03-13 16:33:47 +090052 const int keyboardWidth, const int keyboardHeight, const int gridWidth,
Ken Wakasabb005f72012-08-08 20:43:47 +090053 const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars,
54 const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates,
55 const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes,
56 const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs,
57 const jfloatArray sweetSpotRadii)
Ken Wakasa162c2112012-08-24 14:51:15 +090058 : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), GRID_WIDTH(gridWidth),
59 GRID_HEIGHT(gridHeight), 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)),
Keisuke Kuroyanagi95a49a52012-09-04 17:00:24 +090064 KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight),
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),
Ken Wakasa1d516fb2012-12-03 19:43:15 +090068 mProximityCharsArray(new int[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE
Tom Ouyang13216852012-09-03 12:50:21 -070069 /* proximityGridLength */]),
70 mCodeToKeyMap() {
satok1035bc92012-06-13 16:07:54 -070071 const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
satok817e5172011-03-04 06:06:45 -080072 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +090073 AKLOGI("Create proximity info array %d", proximityGridLength);
satok817e5172011-03-04 06:06:45 -080074 }
Ken Wakasa01511452012-08-09 15:58:15 +090075 const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr);
Ken Wakasa9e0c7112012-08-09 22:26:58 +090076 if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) {
77 AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length);
78 assert(false);
79 }
80 memset(mLocaleStr, 0, sizeof(mLocaleStr));
81 env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr);
Ken Wakasabb005f72012-08-08 20:43:47 +090082 safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray);
83 safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
84 safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates);
85 safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths);
86 safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights);
Ken Wakasaf2789812012-09-04 12:49:46 +090087 safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCodePoints);
Ken Wakasabb005f72012-08-08 20:43:47 +090088 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs);
89 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs);
90 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii);
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +090091 initializeG();
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090092}
93
satok8fbd5522011-02-22 17:28:55 +090094ProximityInfo::~ProximityInfo() {
95 delete[] mProximityCharsArray;
96}
satok817e5172011-03-04 06:06:45 -080097
98inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const {
satok3c4bb772011-03-04 22:50:19 -080099 return ((y / CELL_HEIGHT) * GRID_WIDTH + (x / CELL_WIDTH))
satok817e5172011-03-04 06:06:45 -0800100 * MAX_PROXIMITY_CHARS_SIZE;
satok8fbd5522011-02-22 17:28:55 +0900101}
satok817e5172011-03-04 06:06:45 -0800102
103bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
satok744dab62011-12-15 22:29:05 +0900104 if (x < 0 || y < 0) {
105 if (DEBUG_DICT) {
satok9fb6f472012-01-13 18:01:22 +0900106 AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
Ken Wakasaf2789812012-09-04 12:49:46 +0900107 // TODO: Enable this assertion.
108 //assert(false);
satok744dab62011-12-15 22:29:05 +0900109 }
110 return false;
111 }
112
satok817e5172011-03-04 06:06:45 -0800113 const int startIndex = getStartIndexFromCoordinates(x, y);
114 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900115 AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
satok817e5172011-03-04 06:06:45 -0800116 }
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900117 int *proximityCharsArray = mProximityCharsArray;
satok817e5172011-03-04 06:06:45 -0800118 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
119 if (DEBUG_PROXIMITY_INFO) {
satok9fb6f472012-01-13 18:01:22 +0900120 AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
satok817e5172011-03-04 06:06:45 -0800121 }
Satoshi Kataoka3e8c58f2012-06-05 17:55:52 +0900122 if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
satok817e5172011-03-04 06:06:45 -0800123 return true;
124 }
125 }
126 return false;
127}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900128
Satoshi Kataoka0edab9d2012-09-24 18:29:31 +0900129float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG(
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900130 const int keyId, const int x, const int y) const {
Satoshi Kataoka0edab9d2012-09-24 18:29:31 +0900131 const static float verticalSweetSpotScaleForGeometric = 1.1f;
132 const bool correctTouchPosition = hasTouchPositionCorrectionData();
133 const float centerX = static_cast<float>(correctTouchPosition
134 ? getSweetSpotCenterXAt(keyId)
135 : getKeyCenterXOfKeyIdG(keyId));
136 const float visualKeyCenterY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId));
137 float centerY;
138 if (correctTouchPosition) {
139 const float sweetSpotCenterY = static_cast<float>(getSweetSpotCenterYAt(keyId));
140 const float gapY = sweetSpotCenterY - visualKeyCenterY;
141 centerY = visualKeyCenterY + gapY * verticalSweetSpotScaleForGeometric;
142 } else {
143 centerY = visualKeyCenterY;
144 }
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900145 const float touchX = static_cast<float>(x);
146 const float touchY = static_cast<float>(y);
147 const float keyWidth = static_cast<float>(getMostCommonKeyWidth());
148 return getNormalizedSquaredDistanceFloat(centerX, centerY, touchX, touchY, keyWidth);
149}
150
satok9df4a452012-03-23 16:05:18 +0900151int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
Jean Chalard081616c2012-03-22 17:39:27 +0900152 if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
satoka70ee6e2012-03-07 15:12:22 +0900153 const int left = mKeyXCoordinates[keyId];
154 const int top = mKeyYCoordinates[keyId];
satok1caff472012-03-14 23:17:12 +0900155 const int right = left + mKeyWidths[keyId];
satoka70ee6e2012-03-07 15:12:22 +0900156 const int bottom = top + mKeyHeights[keyId];
157 const int edgeX = x < left ? left : (x > right ? right : x);
158 const int edgeY = y < top ? top : (y > bottom ? bottom : y);
159 const int dx = x - edgeX;
160 const int dy = y - edgeY;
161 return dx * dx + dy * dy;
162}
163
164void ProximityInfo::calculateNearbyKeyCodes(
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900165 const int x, const int y, const int primaryKey, int *inputCodes) const {
166 int *proximityCharsArray = mProximityCharsArray;
satoka70ee6e2012-03-07 15:12:22 +0900167 int insertPos = 0;
168 inputCodes[insertPos++] = primaryKey;
169 const int startIndex = getStartIndexFromCoordinates(x, y);
Jean Chalard52612a02012-03-23 19:38:23 +0900170 if (startIndex >= 0) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900171 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900172 const int c = proximityCharsArray[startIndex + i];
Jean Chalard88ec1252012-03-23 19:25:10 +0900173 if (c < KEYCODE_SPACE || c == primaryKey) {
174 continue;
175 }
Ken Wakasaf2789812012-09-04 12:49:46 +0900176 const int keyIndex = getKeyIndexOf(c);
Jean Chalard88ec1252012-03-23 19:25:10 +0900177 const bool onKey = isOnKey(keyIndex, x, y);
178 const int distance = squaredDistanceToEdge(keyIndex, x, y);
179 if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) {
180 inputCodes[insertPos++] = c;
181 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
182 if (DEBUG_DICT) {
183 assert(false);
184 }
185 return;
satok0cb20972012-03-13 22:07:56 +0900186 }
satoka70ee6e2012-03-07 15:12:22 +0900187 }
188 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900189 const int additionalProximitySize =
Ken Wakasa01511452012-08-09 15:58:15 +0900190 AdditionalProximityChars::getAdditionalCharsSize(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900191 if (additionalProximitySize > 0) {
192 inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
193 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
Jean Chalard3094d122012-03-23 19:36:07 +0900194 if (DEBUG_DICT) {
195 assert(false);
196 }
197 return;
satok1caff472012-03-14 23:17:12 +0900198 }
satok1caff472012-03-14 23:17:12 +0900199
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900200 const int *additionalProximityChars =
Ken Wakasa01511452012-08-09 15:58:15 +0900201 AdditionalProximityChars::getAdditionalChars(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900202 for (int j = 0; j < additionalProximitySize; ++j) {
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900203 const int ac = additionalProximityChars[j];
Jean Chalard88ec1252012-03-23 19:25:10 +0900204 int k = 0;
205 for (; k < insertPos; ++k) {
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900206 if (ac == inputCodes[k]) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900207 break;
208 }
satok5eec5742012-03-13 18:26:23 +0900209 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900210 if (k < insertPos) {
211 continue;
satok0cb20972012-03-13 22:07:56 +0900212 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900213 inputCodes[insertPos++] = ac;
214 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
215 if (DEBUG_DICT) {
216 assert(false);
217 }
218 return;
Jean Chalard3094d122012-03-23 19:36:07 +0900219 }
satok0cb20972012-03-13 22:07:56 +0900220 }
satok5eec5742012-03-13 18:26:23 +0900221 }
Jean Chalard52612a02012-03-23 19:38:23 +0900222 }
satok0cb20972012-03-13 22:07:56 +0900223 // Add a delimiter for the proximity characters
satok1caff472012-03-14 23:17:12 +0900224 for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900225 inputCodes[i] = NOT_A_CODE_POINT;
satok1caff472012-03-14 23:17:12 +0900226 }
satoka70ee6e2012-03-07 15:12:22 +0900227}
228
Ken Wakasaf2789812012-09-04 12:49:46 +0900229int ProximityInfo::getKeyIndexOf(const int c) const {
satok1caff472012-03-14 23:17:12 +0900230 if (KEY_COUNT == 0) {
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900231 // We do not have the coordinate data
Jean Chalardbbc25602012-03-23 17:05:03 +0900232 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900233 }
Keisuke Kuroyanagiff74cc32012-10-11 13:08:06 +0900234 if (c == NOT_A_CODE_POINT) {
235 return NOT_AN_INDEX;
236 }
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900237 const int lowerCode = toLowerCase(c);
Tom Ouyangf34ec5a2012-09-25 00:06:31 -0700238 hash_map_compat<int, int>::const_iterator mapPos = mCodeToKeyMap.find(lowerCode);
Tom Ouyang13216852012-09-03 12:50:21 -0700239 if (mapPos != mCodeToKeyMap.end()) {
240 return mapPos->second;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900241 }
Tom Ouyang13216852012-09-03 12:50:21 -0700242 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900243}
Satoshi Kataokaefb63242012-06-27 14:52:40 +0900244
Ken Wakasaf2789812012-09-04 12:49:46 +0900245int ProximityInfo::getCodePointOf(const int keyIndex) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900246 if (keyIndex < 0 || keyIndex >= KEY_COUNT) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900247 return NOT_A_CODE_POINT;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900248 }
Ken Wakasaf2789812012-09-04 12:49:46 +0900249 return mKeyIndexToCodePointG[keyIndex];
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900250}
251
252void ProximityInfo::initializeG() {
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900253 // TODO: Optimize
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900254 for (int i = 0; i < KEY_COUNT; ++i) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900255 const int code = mKeyCodePoints[i];
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900256 const int lowerCode = toLowerCase(code);
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900257 mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
258 mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
Tom Ouyang13216852012-09-03 12:50:21 -0700259 mCodeToKeyMap[lowerCode] = i;
260 mKeyIndexToCodePointG[i] = lowerCode;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900261 }
262 for (int i = 0; i < KEY_COUNT; i++) {
263 mKeyKeyDistancesG[i][i] = 0;
264 for (int j = i + 1; j < KEY_COUNT; j++) {
Ken Wakasabcec82d2012-08-12 11:10:48 +0900265 mKeyKeyDistancesG[i][j] = getDistanceInt(
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900266 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
267 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
268 }
269 }
270}
271
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900272int ProximityInfo::getKeyCenterXOfCodePointG(int charCode) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900273 return getKeyCenterXOfKeyIdG(getKeyIndexOf(charCode));
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900274}
275
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900276int ProximityInfo::getKeyCenterYOfCodePointG(int charCode) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900277 return getKeyCenterYOfKeyIdG(getKeyIndexOf(charCode));
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900278}
279
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900280int ProximityInfo::getKeyCenterXOfKeyIdG(int keyId) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900281 if (keyId >= 0) {
282 return mCenterXsG[keyId];
283 }
284 return 0;
285}
286
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900287int ProximityInfo::getKeyCenterYOfKeyIdG(int keyId) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900288 if (keyId >= 0) {
289 return mCenterYsG[keyId];
290 }
291 return 0;
292}
293
Keisuke Kuroyanagiff74cc32012-10-11 13:08:06 +0900294int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900295 if (keyId0 >= 0 && keyId1 >= 0) {
296 return mKeyKeyDistancesG[keyId0][keyId1];
297 }
Jean Chalard07aea402012-08-29 20:09:27 +0900298 return MAX_POINT_TO_KEY_LENGTH;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900299}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900300} // namespace latinime