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 <algorithm> 22 #include <cstring> 23 #include <cmath> 24 25 #include "defines.h" 26 #include "jni.h" 27 #include "suggest/core/layout/additional_proximity_chars.h" 28 #include "suggest/core/layout/geometry_utils.h" 29 #include "suggest/core/layout/proximity_info_params.h" 30 #include "utils/char_utils.h" 31 32 namespace latinime { 33 34 static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, 35 jsize len, jint *buffer) { 36 if (jArray && buffer) { 37 env->GetIntArrayRegion(jArray, 0, len, buffer); 38 } else if (buffer) { 39 memset(buffer, 0, len * sizeof(buffer[0])); 40 } 41 } 42 43 static AK_FORCE_INLINE void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray, 44 jsize len, jfloat *buffer) { 45 if (jArray && buffer) { 46 env->GetFloatArrayRegion(jArray, 0, len, buffer); 47 } else if (buffer) { 48 memset(buffer, 0, len * sizeof(buffer[0])); 49 } 50 } 51 52 ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, 53 const int keyboardWidth, const int keyboardHeight, const int gridWidth, 54 const int gridHeight, const int mostCommonKeyWidth, const int mostCommonKeyHeight, 55 const jintArray proximityChars, const int keyCount, const jintArray keyXCoordinates, 56 const jintArray keyYCoordinates, const jintArray keyWidths, const jintArray keyHeights, 57 const jintArray keyCharCodes, const jfloatArray sweetSpotCenterXs, 58 const jfloatArray sweetSpotCenterYs, const jfloatArray sweetSpotRadii) 59 : GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth), 60 MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), 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(std::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 mLowerCodePointToKeyMap() { 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 mKeyIndexToLowerCodePointG[keyIndex]; 151 } 152 153 int ProximityInfo::getOriginalCodePointOf(const int keyIndex) const { 154 if (keyIndex < 0 || keyIndex >= KEY_COUNT) { 155 return NOT_A_CODE_POINT; 156 } 157 return mKeyIndexToOriginalCodePoint[keyIndex]; 158 } 159 160 void ProximityInfo::initializeG() { 161 // TODO: Optimize 162 for (int i = 0; i < KEY_COUNT; ++i) { 163 const int code = mKeyCodePoints[i]; 164 const int lowerCode = CharUtils::toLowerCase(code); 165 mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2; 166 mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2; 167 if (hasTouchPositionCorrectionData()) { 168 // Computes sweet spot center points for geometric input. 169 const float verticalScale = ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G; 170 const float sweetSpotCenterY = static_cast<float>(mSweetSpotCenterYs[i]); 171 const float gapY = sweetSpotCenterY - mCenterYsG[i]; 172 mSweetSpotCenterYsG[i] = static_cast<int>(mCenterYsG[i] + gapY * verticalScale); 173 } 174 mLowerCodePointToKeyMap[lowerCode] = i; 175 mKeyIndexToOriginalCodePoint[i] = code; 176 mKeyIndexToLowerCodePointG[i] = lowerCode; 177 } 178 for (int i = 0; i < KEY_COUNT; i++) { 179 mKeyKeyDistancesG[i][i] = 0; 180 for (int j = i + 1; j < KEY_COUNT; j++) { 181 if (hasTouchPositionCorrectionData()) { 182 // Computes distances using sweet spots if they exist. 183 // We have two types of Y coordinate sweet spots, for geometric and for the others. 184 // The sweet spots for geometric input are used for calculating key-key distances 185 // here. 186 mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt( 187 mSweetSpotCenterXs[i], mSweetSpotCenterYsG[i], 188 mSweetSpotCenterXs[j], mSweetSpotCenterYsG[j]); 189 } else { 190 mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt( 191 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]); 192 } 193 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j]; 194 } 195 } 196 } 197 198 // referencePointX is used only for keys wider than most common key width. When the referencePointX 199 // is NOT_A_COORDINATE, this method calculates the return value without using the line segment. 200 // isGeometric is currently not used because we don't have extra X coordinates sweet spots for 201 // geometric input. 202 int ProximityInfo::getKeyCenterXOfKeyIdG( 203 const int keyId, const int referencePointX, const bool isGeometric) const { 204 if (keyId < 0) { 205 return 0; 206 } 207 int centerX = (hasTouchPositionCorrectionData()) ? static_cast<int>(mSweetSpotCenterXs[keyId]) 208 : mCenterXsG[keyId]; 209 const int keyWidth = mKeyWidths[keyId]; 210 if (referencePointX != NOT_A_COORDINATE 211 && keyWidth > getMostCommonKeyWidth()) { 212 // For keys wider than most common keys, we use a line segment instead of the center point; 213 // thus, centerX is adjusted depending on referencePointX. 214 const int keyWidthHalfDiff = (keyWidth - getMostCommonKeyWidth()) / 2; 215 if (referencePointX < centerX - keyWidthHalfDiff) { 216 centerX -= keyWidthHalfDiff; 217 } else if (referencePointX > centerX + keyWidthHalfDiff) { 218 centerX += keyWidthHalfDiff; 219 } else { 220 centerX = referencePointX; 221 } 222 } 223 return centerX; 224 } 225 226 // When the referencePointY is NOT_A_COORDINATE, this method calculates the return value without 227 // using the line segment. 228 int ProximityInfo::getKeyCenterYOfKeyIdG( 229 const int keyId, const int referencePointY, const bool isGeometric) const { 230 // TODO: Remove "isGeometric" and have separate "proximity_info"s for gesture and typing. 231 if (keyId < 0) { 232 return 0; 233 } 234 int centerY; 235 if (!hasTouchPositionCorrectionData()) { 236 centerY = mCenterYsG[keyId]; 237 } else if (isGeometric) { 238 centerY = static_cast<int>(mSweetSpotCenterYsG[keyId]); 239 } else { 240 centerY = static_cast<int>(mSweetSpotCenterYs[keyId]); 241 } 242 if (referencePointY != NOT_A_COORDINATE && 243 centerY + mKeyHeights[keyId] > KEYBOARD_HEIGHT && centerY < referencePointY) { 244 // When the distance between center point and bottom edge of the keyboard is shorter than 245 // the key height, we assume the key is located at the bottom row of the keyboard. 246 // The center point is extended to the bottom edge for such keys. 247 return referencePointY; 248 } 249 return centerY; 250 } 251 252 int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const { 253 if (keyId0 >= 0 && keyId1 >= 0) { 254 return mKeyKeyDistancesG[keyId0][keyId1]; 255 } 256 return MAX_VALUE_FOR_WEIGHTING; 257 } 258 } // namespace latinime 259