1 /* 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 17 #define LOG_TAG "LatinIME: proximity_info.cpp" 18 19 #include "suggest/core/layout/proximity_info.h" 20 21 #include <cstring> 22 #include <cmath> 23 24 #include "defines.h" 25 #include "jni.h" 26 #include "suggest/core/layout/additional_proximity_chars.h" 27 #include "suggest/core/layout/geometry_utils.h" 28 #include "suggest/core/layout/proximity_info_params.h" 29 #include "utils/char_utils.h" 30 31 namespace latinime { 32 33 static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, 34 jsize len, jint *buffer) { 35 if (jArray && buffer) { 36 env->GetIntArrayRegion(jArray, 0, len, buffer); 37 } else if (buffer) { 38 memset(buffer, 0, len * sizeof(buffer[0])); 39 } 40 } 41 42 static AK_FORCE_INLINE void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray, 43 jsize len, jfloat *buffer) { 44 if (jArray && buffer) { 45 env->GetFloatArrayRegion(jArray, 0, len, buffer); 46 } else if (buffer) { 47 memset(buffer, 0, len * sizeof(buffer[0])); 48 } 49 } 50 51 ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, 52 const int keyboardWidth, const int keyboardHeight, const int gridWidth, 53 const int gridHeight, const int mostCommonKeyWidth, const int mostCommonKeyHeight, 54 const jintArray proximityChars, const int keyCount, const jintArray keyXCoordinates, 55 const jintArray keyYCoordinates, const jintArray keyWidths, const jintArray keyHeights, 56 const jintArray keyCharCodes, const jfloatArray sweetSpotCenterXs, 57 const jfloatArray sweetSpotCenterYs, const jfloatArray sweetSpotRadii) 58 : GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth), 59 MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), 60 MOST_COMMON_KEY_HEIGHT(mostCommonKeyHeight), 61 NORMALIZED_SQUARED_MOST_COMMON_KEY_HYPOTENUSE(1.0f + 62 GeometryUtils::SQUARE_FLOAT(static_cast<float>(mostCommonKeyHeight) / 63 static_cast<float>(mostCommonKeyWidth))), 64 CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), 65 CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), 66 KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), 67 KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight), 68 KEYBOARD_HYPOTENUSE(hypotf(KEYBOARD_WIDTH, KEYBOARD_HEIGHT)), 69 HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates 70 && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs 71 && sweetSpotCenterYs && sweetSpotRadii), 72 mProximityCharsArray(new int[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE 73 /* proximityCharsLength */]), 74 mCodeToKeyMap() { 75 /* Let's check the input array length here to make sure */ 76 const jsize proximityCharsLength = env->GetArrayLength(proximityChars); 77 if (proximityCharsLength != GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE) { 78 AKLOGE("Invalid proximityCharsLength: %d", proximityCharsLength); 79 ASSERT(false); 80 return; 81 } 82 if (DEBUG_PROXIMITY_INFO) { 83 AKLOGI("Create proximity info array %d", proximityCharsLength); 84 } 85 const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr); 86 if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) { 87 AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length); 88 ASSERT(false); 89 } 90 memset(mLocaleStr, 0, sizeof(mLocaleStr)); 91 env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr); 92 safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityCharsLength, 93 mProximityCharsArray); 94 safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates); 95 safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates); 96 safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths); 97 safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights); 98 safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCodePoints); 99 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs); 100 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs); 101 safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii); 102 initializeG(); 103 } 104 105 ProximityInfo::~ProximityInfo() { 106 delete[] mProximityCharsArray; 107 } 108 109 bool ProximityInfo::hasSpaceProximity(const int x, const int y) const { 110 if (x < 0 || y < 0) { 111 if (DEBUG_DICT) { 112 AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y); 113 // TODO: Enable this assertion. 114 //ASSERT(false); 115 } 116 return false; 117 } 118 119 const int startIndex = ProximityInfoUtils::getStartIndexFromCoordinates(x, y, 120 CELL_HEIGHT, CELL_WIDTH, GRID_WIDTH); 121 if (DEBUG_PROXIMITY_INFO) { 122 AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y); 123 } 124 int *proximityCharsArray = mProximityCharsArray; 125 for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) { 126 if (DEBUG_PROXIMITY_INFO) { 127 AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]); 128 } 129 if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) { 130 return true; 131 } 132 } 133 return false; 134 } 135 136 float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG( 137 const int keyId, const int x, const int y, const bool isGeometric) const { 138 const float centerX = static_cast<float>(getKeyCenterXOfKeyIdG(keyId, x, isGeometric)); 139 const float centerY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId, y, isGeometric)); 140 const float touchX = static_cast<float>(x); 141 const float touchY = static_cast<float>(y); 142 return ProximityInfoUtils::getSquaredDistanceFloat(centerX, centerY, touchX, touchY) 143 / GeometryUtils::SQUARE_FLOAT(static_cast<float>(getMostCommonKeyWidth())); 144 } 145 146 int ProximityInfo::getCodePointOf(const int keyIndex) const { 147 if (keyIndex < 0 || keyIndex >= KEY_COUNT) { 148 return NOT_A_CODE_POINT; 149 } 150 return mKeyIndexToCodePointG[keyIndex]; 151 } 152 153 void ProximityInfo::initializeG() { 154 // TODO: Optimize 155 for (int i = 0; i < KEY_COUNT; ++i) { 156 const int code = mKeyCodePoints[i]; 157 const int lowerCode = CharUtils::toLowerCase(code); 158 mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2; 159 mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2; 160 if (hasTouchPositionCorrectionData()) { 161 // Computes sweet spot center points for geometric input. 162 const float verticalScale = ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G; 163 const float sweetSpotCenterY = static_cast<float>(mSweetSpotCenterYs[i]); 164 const float gapY = sweetSpotCenterY - mCenterYsG[i]; 165 mSweetSpotCenterYsG[i] = static_cast<int>(mCenterYsG[i] + gapY * verticalScale); 166 } 167 mCodeToKeyMap[lowerCode] = i; 168 mKeyIndexToCodePointG[i] = lowerCode; 169 } 170 for (int i = 0; i < KEY_COUNT; i++) { 171 mKeyKeyDistancesG[i][i] = 0; 172 for (int j = i + 1; j < KEY_COUNT; j++) { 173 if (hasTouchPositionCorrectionData()) { 174 // Computes distances using sweet spots if they exist. 175 // We have two types of Y coordinate sweet spots, for geometric and for the others. 176 // The sweet spots for geometric input are used for calculating key-key distances 177 // here. 178 mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt( 179 mSweetSpotCenterXs[i], mSweetSpotCenterYsG[i], 180 mSweetSpotCenterXs[j], mSweetSpotCenterYsG[j]); 181 } else { 182 mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt( 183 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]); 184 } 185 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j]; 186 } 187 } 188 } 189 190 // referencePointX is used only for keys wider than most common key width. When the referencePointX 191 // is NOT_A_COORDINATE, this method calculates the return value without using the line segment. 192 // isGeometric is currently not used because we don't have extra X coordinates sweet spots for 193 // geometric input. 194 int ProximityInfo::getKeyCenterXOfKeyIdG( 195 const int keyId, const int referencePointX, const bool isGeometric) const { 196 if (keyId < 0) { 197 return 0; 198 } 199 int centerX = (hasTouchPositionCorrectionData()) ? static_cast<int>(mSweetSpotCenterXs[keyId]) 200 : mCenterXsG[keyId]; 201 const int keyWidth = mKeyWidths[keyId]; 202 if (referencePointX != NOT_A_COORDINATE 203 && keyWidth > getMostCommonKeyWidth()) { 204 // For keys wider than most common keys, we use a line segment instead of the center point; 205 // thus, centerX is adjusted depending on referencePointX. 206 const int keyWidthHalfDiff = (keyWidth - getMostCommonKeyWidth()) / 2; 207 if (referencePointX < centerX - keyWidthHalfDiff) { 208 centerX -= keyWidthHalfDiff; 209 } else if (referencePointX > centerX + keyWidthHalfDiff) { 210 centerX += keyWidthHalfDiff; 211 } else { 212 centerX = referencePointX; 213 } 214 } 215 return centerX; 216 } 217 218 // When the referencePointY is NOT_A_COORDINATE, this method calculates the return value without 219 // using the line segment. 220 int ProximityInfo::getKeyCenterYOfKeyIdG( 221 const int keyId, const int referencePointY, const bool isGeometric) const { 222 // TODO: Remove "isGeometric" and have separate "proximity_info"s for gesture and typing. 223 if (keyId < 0) { 224 return 0; 225 } 226 int centerY; 227 if (!hasTouchPositionCorrectionData()) { 228 centerY = mCenterYsG[keyId]; 229 } else if (isGeometric) { 230 centerY = static_cast<int>(mSweetSpotCenterYsG[keyId]); 231 } else { 232 centerY = static_cast<int>(mSweetSpotCenterYs[keyId]); 233 } 234 if (referencePointY != NOT_A_COORDINATE && 235 centerY + mKeyHeights[keyId] > KEYBOARD_HEIGHT && centerY < referencePointY) { 236 // When the distance between center point and bottom edge of the keyboard is shorter than 237 // the key height, we assume the key is located at the bottom row of the keyboard. 238 // The center point is extended to the bottom edge for such keys. 239 return referencePointY; 240 } 241 return centerY; 242 } 243 244 int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const { 245 if (keyId0 >= 0 && keyId1 >= 0) { 246 return mKeyKeyDistancesG[keyId0][keyId1]; 247 } 248 return MAX_VALUE_FOR_WEIGHTING; 249 } 250 } // namespace latinime 251