blob: d38ea670c4818320d131155c6f4666b4bd052433 [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 Wakasa6e663492012-11-03 02:50:47 +090034static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray,
35 jsize len, jint *buffer) {
Ken Wakasabb005f72012-08-08 20:43:47 +090036 if (jArray && buffer) {
37 env->GetIntArrayRegion(jArray, 0, len, buffer);
38 } else if (buffer) {
Ken Wakasa44d9c1e2012-11-01 17:05:08 +090039 memset(buffer, 0, len * sizeof(buffer[0]));
Yusuke Nojimade2f8422011-09-27 11:15:18 +090040 }
41}
42
Ken Wakasa6e663492012-11-03 02:50:47 +090043static AK_FORCE_INLINE void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray,
44 jsize len, jfloat *buffer) {
Ken Wakasabb005f72012-08-08 20:43:47 +090045 if (jArray && buffer) {
46 env->GetFloatArrayRegion(jArray, 0, len, buffer);
47 } else if (buffer) {
Ken Wakasa44d9c1e2012-11-01 17:05:08 +090048 memset(buffer, 0, len * sizeof(buffer[0]));
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),
Ken Wakasa1d516fb2012-12-03 19:43:15 +090069 mProximityCharsArray(new int[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 Wakasa1d516fb2012-12-03 19:43:15 +0900118 int *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 Kataoka0edab9d2012-09-24 18:29:31 +0900130float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG(
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900131 const int keyId, const int x, const int y) const {
Satoshi Kataoka0edab9d2012-09-24 18:29:31 +0900132 const static float verticalSweetSpotScaleForGeometric = 1.1f;
133 const bool correctTouchPosition = hasTouchPositionCorrectionData();
134 const float centerX = static_cast<float>(correctTouchPosition
135 ? getSweetSpotCenterXAt(keyId)
136 : getKeyCenterXOfKeyIdG(keyId));
137 const float visualKeyCenterY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId));
138 float centerY;
139 if (correctTouchPosition) {
140 const float sweetSpotCenterY = static_cast<float>(getSweetSpotCenterYAt(keyId));
141 const float gapY = sweetSpotCenterY - visualKeyCenterY;
142 centerY = visualKeyCenterY + gapY * verticalSweetSpotScaleForGeometric;
143 } else {
144 centerY = visualKeyCenterY;
145 }
Satoshi Kataokae7398cd2012-08-13 20:20:04 +0900146 const float touchX = static_cast<float>(x);
147 const float touchY = static_cast<float>(y);
148 const float keyWidth = static_cast<float>(getMostCommonKeyWidth());
149 return getNormalizedSquaredDistanceFloat(centerX, centerY, touchX, touchY, keyWidth);
150}
151
satok9df4a452012-03-23 16:05:18 +0900152int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const {
Jean Chalard081616c2012-03-22 17:39:27 +0900153 if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
satoka70ee6e2012-03-07 15:12:22 +0900154 const int left = mKeyXCoordinates[keyId];
155 const int top = mKeyYCoordinates[keyId];
satok1caff472012-03-14 23:17:12 +0900156 const int right = left + mKeyWidths[keyId];
satoka70ee6e2012-03-07 15:12:22 +0900157 const int bottom = top + mKeyHeights[keyId];
158 const int edgeX = x < left ? left : (x > right ? right : x);
159 const int edgeY = y < top ? top : (y > bottom ? bottom : y);
160 const int dx = x - edgeX;
161 const int dy = y - edgeY;
162 return dx * dx + dy * dy;
163}
164
165void ProximityInfo::calculateNearbyKeyCodes(
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900166 const int x, const int y, const int primaryKey, int *inputCodes) const {
167 int *proximityCharsArray = mProximityCharsArray;
satoka70ee6e2012-03-07 15:12:22 +0900168 int insertPos = 0;
169 inputCodes[insertPos++] = primaryKey;
170 const int startIndex = getStartIndexFromCoordinates(x, y);
Jean Chalard52612a02012-03-23 19:38:23 +0900171 if (startIndex >= 0) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900172 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900173 const int c = proximityCharsArray[startIndex + i];
Jean Chalard88ec1252012-03-23 19:25:10 +0900174 if (c < KEYCODE_SPACE || c == primaryKey) {
175 continue;
176 }
Ken Wakasaf2789812012-09-04 12:49:46 +0900177 const int keyIndex = getKeyIndexOf(c);
Jean Chalard88ec1252012-03-23 19:25:10 +0900178 const bool onKey = isOnKey(keyIndex, x, y);
179 const int distance = squaredDistanceToEdge(keyIndex, x, y);
180 if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) {
181 inputCodes[insertPos++] = c;
182 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
183 if (DEBUG_DICT) {
184 assert(false);
185 }
186 return;
satok0cb20972012-03-13 22:07:56 +0900187 }
satoka70ee6e2012-03-07 15:12:22 +0900188 }
189 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900190 const int additionalProximitySize =
Ken Wakasa01511452012-08-09 15:58:15 +0900191 AdditionalProximityChars::getAdditionalCharsSize(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900192 if (additionalProximitySize > 0) {
193 inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
194 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
Jean Chalard3094d122012-03-23 19:36:07 +0900195 if (DEBUG_DICT) {
196 assert(false);
197 }
198 return;
satok1caff472012-03-14 23:17:12 +0900199 }
satok1caff472012-03-14 23:17:12 +0900200
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900201 const int *additionalProximityChars =
Ken Wakasa01511452012-08-09 15:58:15 +0900202 AdditionalProximityChars::getAdditionalChars(mLocaleStr, primaryKey);
Jean Chalard88ec1252012-03-23 19:25:10 +0900203 for (int j = 0; j < additionalProximitySize; ++j) {
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900204 const int ac = additionalProximityChars[j];
Jean Chalard88ec1252012-03-23 19:25:10 +0900205 int k = 0;
206 for (; k < insertPos; ++k) {
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900207 if (ac == inputCodes[k]) {
Jean Chalard88ec1252012-03-23 19:25:10 +0900208 break;
209 }
satok5eec5742012-03-13 18:26:23 +0900210 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900211 if (k < insertPos) {
212 continue;
satok0cb20972012-03-13 22:07:56 +0900213 }
Jean Chalard88ec1252012-03-23 19:25:10 +0900214 inputCodes[insertPos++] = ac;
215 if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
216 if (DEBUG_DICT) {
217 assert(false);
218 }
219 return;
Jean Chalard3094d122012-03-23 19:36:07 +0900220 }
satok0cb20972012-03-13 22:07:56 +0900221 }
satok5eec5742012-03-13 18:26:23 +0900222 }
Jean Chalard52612a02012-03-23 19:38:23 +0900223 }
satok0cb20972012-03-13 22:07:56 +0900224 // Add a delimiter for the proximity characters
satok1caff472012-03-14 23:17:12 +0900225 for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
Ken Wakasaf2789812012-09-04 12:49:46 +0900226 inputCodes[i] = NOT_A_CODE_POINT;
satok1caff472012-03-14 23:17:12 +0900227 }
satoka70ee6e2012-03-07 15:12:22 +0900228}
229
Ken Wakasaf2789812012-09-04 12:49:46 +0900230int ProximityInfo::getKeyIndexOf(const int c) const {
satok1caff472012-03-14 23:17:12 +0900231 if (KEY_COUNT == 0) {
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900232 // We do not have the coordinate data
Jean Chalardbbc25602012-03-23 17:05:03 +0900233 return NOT_AN_INDEX;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900234 }
Keisuke Kuroyanagiff74cc32012-10-11 13:08:06 +0900235 if (c == NOT_A_CODE_POINT) {
236 return NOT_AN_INDEX;
237 }
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900238 const int lowerCode = toLowerCase(c);
Tom Ouyangf34ec5a2012-09-25 00:06:31 -0700239 hash_map_compat<int, int>::const_iterator mapPos = mCodeToKeyMap.find(lowerCode);
Tom Ouyang13216852012-09-03 12:50:21 -0700240 if (mapPos != mCodeToKeyMap.end()) {
241 return mapPos->second;
Yusuke Nojimaa4c1f1c2011-10-06 19:12:20 +0900242 }
Tom Ouyang13216852012-09-03 12:50:21 -0700243 return NOT_AN_INDEX;
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];
Ken Wakasa1d516fb2012-12-03 19:43:15 +0900257 const int lowerCode = toLowerCase(code);
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900258 mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
259 mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
Tom Ouyang13216852012-09-03 12:50:21 -0700260 mCodeToKeyMap[lowerCode] = i;
261 mKeyIndexToCodePointG[i] = lowerCode;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900262 }
263 for (int i = 0; i < KEY_COUNT; i++) {
264 mKeyKeyDistancesG[i][i] = 0;
265 for (int j = i + 1; j < KEY_COUNT; j++) {
Ken Wakasabcec82d2012-08-12 11:10:48 +0900266 mKeyKeyDistancesG[i][j] = getDistanceInt(
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900267 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
268 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
269 }
270 }
271}
272
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900273int ProximityInfo::getKeyCenterXOfCodePointG(int charCode) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900274 return getKeyCenterXOfKeyIdG(getKeyIndexOf(charCode));
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900275}
276
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900277int ProximityInfo::getKeyCenterYOfCodePointG(int charCode) const {
Ken Wakasaf2789812012-09-04 12:49:46 +0900278 return getKeyCenterYOfKeyIdG(getKeyIndexOf(charCode));
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900279}
280
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900281int ProximityInfo::getKeyCenterXOfKeyIdG(int keyId) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900282 if (keyId >= 0) {
283 return mCenterXsG[keyId];
284 }
285 return 0;
286}
287
Ken Wakasa5964d4e2012-09-10 16:49:36 +0900288int ProximityInfo::getKeyCenterYOfKeyIdG(int keyId) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900289 if (keyId >= 0) {
290 return mCenterYsG[keyId];
291 }
292 return 0;
293}
294
Keisuke Kuroyanagiff74cc32012-10-11 13:08:06 +0900295int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const {
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900296 if (keyId0 >= 0 && keyId1 >= 0) {
297 return mKeyKeyDistancesG[keyId0][keyId1];
298 }
Jean Chalard07aea402012-08-29 20:09:27 +0900299 return MAX_POINT_TO_KEY_LENGTH;
Satoshi Kataoka6b4a1d72012-08-10 15:42:56 +0900300}
Ken Wakasace9e52a2011-06-18 13:09:55 +0900301} // namespace latinime