Index: third_party/android_prediction/suggest/core/layout/proximity_info.cpp |
diff --git a/third_party/android_prediction/suggest/core/layout/proximity_info.cpp b/third_party/android_prediction/suggest/core/layout/proximity_info.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ffd977bacfa440c7eca0e7200cdcf9923d82af7d |
--- /dev/null |
+++ b/third_party/android_prediction/suggest/core/layout/proximity_info.cpp |
@@ -0,0 +1,263 @@ |
+/* |
+ * Copyright (C) 2011 The Android Open Source Project |
+ * |
+ * Licensed under the Apache License, Version 2.0 (the "License"); |
+ * you may not use this file except in compliance with the License. |
+ * You may obtain a copy of the License at |
+ * |
+ * http://www.apache.org/licenses/LICENSE-2.0 |
+ * |
+ * Unless required by applicable law or agreed to in writing, software |
+ * distributed under the License is distributed on an "AS IS" BASIS, |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+ * See the License for the specific language governing permissions and |
+ * limitations under the License. |
+ */ |
+ |
+#define LOG_TAG "LatinIME: proximity_info.cpp" |
+ |
+#include "third_party/android_prediction/suggest/core/layout/proximity_info.h" |
+ |
+#include <algorithm> |
+#include <cstring> |
+#include <cmath> |
+ |
+#include "third_party/android_prediction/defines.h" |
+#include "third_party/android_prediction/suggest/core/layout/additional_proximity_chars.h" |
+#include "third_party/android_prediction/suggest/core/layout/geometry_utils.h" |
+#include "third_party/android_prediction/suggest/core/layout/proximity_info_params.h" |
+#include "third_party/android_prediction/utils/char_utils.h" |
+ |
+namespace latinime { |
+ |
+static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(const int *jArray, |
+ int len, int *buffer) { |
+ if (jArray && buffer) { |
+ for (int i = 0; i < len; i++) { |
+ buffer[i] = jArray[i]; |
+ } |
+ } else if (buffer) { |
+ memset(buffer, 0, len * sizeof(buffer[0])); |
+ } |
+} |
+ |
+static AK_FORCE_INLINE void safeGetOrFillZeroFloatArrayRegion(const float *jArray, |
+ int len, float *buffer) { |
+ if (jArray && buffer) { |
+ for (int i = 0; i < len; i++) { |
+ buffer[i] = jArray[i]; |
+ } |
+ } else if (buffer) { |
+ memset(buffer, 0, len * sizeof(buffer[0])); |
+ } |
+} |
+ |
+ProximityInfo::ProximityInfo(const std::string localeJStr, |
+ const int keyboardWidth, const int keyboardHeight, const int gridWidth, |
+ const int gridHeight, const int mostCommonKeyWidth, const int mostCommonKeyHeight, |
+ int *proximityChars, int proximitySize, const int keyCount, const int *keyXCoordinates, |
+ const int *keyYCoordinates, const int *keyWidths, const int *keyHeights, |
+ const int *keyCharCodes, const float *sweetSpotCenterXs, |
+ const float *sweetSpotCenterYs, const float *sweetSpotRadii) |
+ : GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth), |
+ MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), |
+ NORMALIZED_SQUARED_MOST_COMMON_KEY_HYPOTENUSE(1.0f + |
+ GeometryUtils::SQUARE_FLOAT(static_cast<float>(mostCommonKeyHeight) / |
+ static_cast<float>(mostCommonKeyWidth))), |
+ CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), |
+ CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), |
+ KEY_COUNT(std::min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), |
+ KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight), |
+ KEYBOARD_HYPOTENUSE(hypotf(KEYBOARD_WIDTH, KEYBOARD_HEIGHT)), |
+ HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates |
+ && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs |
+ && sweetSpotCenterYs && sweetSpotRadii), |
+ mProximityCharsArray(new int[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE |
+ /* proximityCharsLength */]), |
+ mLowerCodePointToKeyMap() { |
+ /* Let's check the input array length here to make sure */ |
+ int proximityCharsLength = proximitySize; |
+ if (proximityCharsLength != GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE) { |
+ AKLOGE("Invalid proximityCharsLength: %d", proximityCharsLength); |
+ ASSERT(false); |
+ return; |
+ } |
+ if (DEBUG_PROXIMITY_INFO) { |
+ AKLOGI("Create proximity info array %d", proximityCharsLength); |
+ } |
+ const int localeCStrUtf8Length = localeJStr.length(); |
+ if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) { |
+ AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length); |
+ ASSERT(false); |
+ } |
+ memset(mLocaleStr, 0, sizeof(mLocaleStr)); |
+ for (int i = 0; i < localeCStrUtf8Length; i++) { |
+ mLocaleStr[i] = localeJStr[i]; |
+ } |
+ safeGetOrFillZeroIntArrayRegion(proximityChars, proximityCharsLength, |
+ mProximityCharsArray); |
+ safeGetOrFillZeroIntArrayRegion(keyXCoordinates, KEY_COUNT, mKeyXCoordinates); |
+ safeGetOrFillZeroIntArrayRegion(keyYCoordinates, KEY_COUNT, mKeyYCoordinates); |
+ safeGetOrFillZeroIntArrayRegion(keyWidths, KEY_COUNT, mKeyWidths); |
+ safeGetOrFillZeroIntArrayRegion(keyHeights, KEY_COUNT, mKeyHeights); |
+ safeGetOrFillZeroIntArrayRegion(keyCharCodes, KEY_COUNT, mKeyCodePoints); |
+ safeGetOrFillZeroFloatArrayRegion(sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs); |
+ safeGetOrFillZeroFloatArrayRegion(sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs); |
+ safeGetOrFillZeroFloatArrayRegion(sweetSpotRadii, KEY_COUNT, mSweetSpotRadii); |
+ initializeG(); |
+} |
+ |
+ProximityInfo::~ProximityInfo() { |
+ delete[] mProximityCharsArray; |
+} |
+ |
+bool ProximityInfo::hasSpaceProximity(const int x, const int y) const { |
+ if (x < 0 || y < 0) { |
+ if (DEBUG_DICT) { |
+ AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y); |
+ // TODO: Enable this assertion. |
+ //ASSERT(false); |
+ } |
+ return false; |
+ } |
+ |
+ const int startIndex = ProximityInfoUtils::getStartIndexFromCoordinates(x, y, |
+ CELL_HEIGHT, CELL_WIDTH, GRID_WIDTH); |
+ if (DEBUG_PROXIMITY_INFO) { |
+ AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y); |
+ } |
+ int *proximityCharsArray = mProximityCharsArray; |
+ for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) { |
+ if (DEBUG_PROXIMITY_INFO) { |
+ AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]); |
+ } |
+ if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG( |
+ const int keyId, const int x, const int y, const bool isGeometric) const { |
+ const float centerX = static_cast<float>(getKeyCenterXOfKeyIdG(keyId, x, isGeometric)); |
+ const float centerY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId, y, isGeometric)); |
+ const float touchX = static_cast<float>(x); |
+ const float touchY = static_cast<float>(y); |
+ return ProximityInfoUtils::getSquaredDistanceFloat(centerX, centerY, touchX, touchY) |
+ / GeometryUtils::SQUARE_FLOAT(static_cast<float>(getMostCommonKeyWidth())); |
+} |
+ |
+int ProximityInfo::getCodePointOf(const int keyIndex) const { |
+ if (keyIndex < 0 || keyIndex >= KEY_COUNT) { |
+ return NOT_A_CODE_POINT; |
+ } |
+ return mKeyIndexToLowerCodePointG[keyIndex]; |
+} |
+ |
+int ProximityInfo::getOriginalCodePointOf(const int keyIndex) const { |
+ if (keyIndex < 0 || keyIndex >= KEY_COUNT) { |
+ return NOT_A_CODE_POINT; |
+ } |
+ return mKeyIndexToOriginalCodePoint[keyIndex]; |
+} |
+ |
+void ProximityInfo::initializeG() { |
+ // TODO: Optimize |
+ for (int i = 0; i < KEY_COUNT; ++i) { |
+ const int code = mKeyCodePoints[i]; |
+ const int lowerCode = CharUtils::toLowerCase(code); |
+ mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2; |
+ mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2; |
+ if (hasTouchPositionCorrectionData()) { |
+ // Computes sweet spot center points for geometric input. |
+ const float verticalScale = ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G; |
+ const float sweetSpotCenterY = static_cast<float>(mSweetSpotCenterYs[i]); |
+ const float gapY = sweetSpotCenterY - mCenterYsG[i]; |
+ mSweetSpotCenterYsG[i] = static_cast<int>(mCenterYsG[i] + gapY * verticalScale); |
+ } |
+ mLowerCodePointToKeyMap[lowerCode] = i; |
+ mKeyIndexToOriginalCodePoint[i] = code; |
+ mKeyIndexToLowerCodePointG[i] = lowerCode; |
+ } |
+ for (int i = 0; i < KEY_COUNT; i++) { |
+ mKeyKeyDistancesG[i][i] = 0; |
+ for (int j = i + 1; j < KEY_COUNT; j++) { |
+ if (hasTouchPositionCorrectionData()) { |
+ // Computes distances using sweet spots if they exist. |
+ // We have two types of Y coordinate sweet spots, for geometric and for the others. |
+ // The sweet spots for geometric input are used for calculating key-key distances |
+ // here. |
+ mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt( |
+ mSweetSpotCenterXs[i], mSweetSpotCenterYsG[i], |
+ mSweetSpotCenterXs[j], mSweetSpotCenterYsG[j]); |
+ } else { |
+ mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt( |
+ mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]); |
+ } |
+ mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j]; |
+ } |
+ } |
+} |
+ |
+// referencePointX is used only for keys wider than most common key width. When the referencePointX |
+// is NOT_A_COORDINATE, this method calculates the return value without using the line segment. |
+// isGeometric is currently not used because we don't have extra X coordinates sweet spots for |
+// geometric input. |
+int ProximityInfo::getKeyCenterXOfKeyIdG( |
+ const int keyId, const int referencePointX, const bool isGeometric) const { |
+ if (keyId < 0) { |
+ return 0; |
+ } |
+ int centerX = (hasTouchPositionCorrectionData()) ? static_cast<int>(mSweetSpotCenterXs[keyId]) |
+ : mCenterXsG[keyId]; |
+ const int keyWidth = mKeyWidths[keyId]; |
+ if (referencePointX != NOT_A_COORDINATE |
+ && keyWidth > getMostCommonKeyWidth()) { |
+ // For keys wider than most common keys, we use a line segment instead of the center point; |
+ // thus, centerX is adjusted depending on referencePointX. |
+ const int keyWidthHalfDiff = (keyWidth - getMostCommonKeyWidth()) / 2; |
+ if (referencePointX < centerX - keyWidthHalfDiff) { |
+ centerX -= keyWidthHalfDiff; |
+ } else if (referencePointX > centerX + keyWidthHalfDiff) { |
+ centerX += keyWidthHalfDiff; |
+ } else { |
+ centerX = referencePointX; |
+ } |
+ } |
+ return centerX; |
+} |
+ |
+// When the referencePointY is NOT_A_COORDINATE, this method calculates the return value without |
+// using the line segment. |
+int ProximityInfo::getKeyCenterYOfKeyIdG( |
+ const int keyId, const int referencePointY, const bool isGeometric) const { |
+ // TODO: Remove "isGeometric" and have separate "proximity_info"s for gesture and typing. |
+ if (keyId < 0) { |
+ return 0; |
+ } |
+ int centerY; |
+ if (!hasTouchPositionCorrectionData()) { |
+ centerY = mCenterYsG[keyId]; |
+ } else if (isGeometric) { |
+ centerY = static_cast<int>(mSweetSpotCenterYsG[keyId]); |
+ } else { |
+ centerY = static_cast<int>(mSweetSpotCenterYs[keyId]); |
+ } |
+ if (referencePointY != NOT_A_COORDINATE && |
+ centerY + mKeyHeights[keyId] > KEYBOARD_HEIGHT && centerY < referencePointY) { |
+ // When the distance between center point and bottom edge of the keyboard is shorter than |
+ // the key height, we assume the key is located at the bottom row of the keyboard. |
+ // The center point is extended to the bottom edge for such keys. |
+ return referencePointY; |
+ } |
+ return centerY; |
+} |
+ |
+int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const { |
+ if (keyId0 >= 0 && keyId1 >= 0) { |
+ return mKeyKeyDistancesG[keyId0][keyId1]; |
+ } |
+ return MAX_VALUE_FOR_WEIGHTING; |
+} |
+} // namespace latinime |