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