| Index: third_party/prediction/suggest/core/layout/proximity_info.cpp
|
| diff --git a/third_party/prediction/suggest/core/layout/proximity_info.cpp b/third_party/prediction/suggest/core/layout/proximity_info.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f6dd4e9c11256c09bfcfd99ff4d5798a6a65ef88
|
| --- /dev/null
|
| +++ b/third_party/prediction/suggest/core/layout/proximity_info.cpp
|
| @@ -0,0 +1,311 @@
|
| +/*
|
| + * 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/prediction/suggest/core/layout/proximity_info.h"
|
| +
|
| +#include <algorithm>
|
| +#include <cstring>
|
| +#include <cmath>
|
| +
|
| +#include "third_party/prediction/defines.h"
|
| +#include "third_party/prediction/suggest/core/layout/additional_proximity_chars.h"
|
| +#include "third_party/prediction/suggest/core/layout/geometry_utils.h"
|
| +#include "third_party/prediction/suggest/core/layout/proximity_info_params.h"
|
| +#include "third_party/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
|
|
|