Home | History | Annotate | Download | only in layout
      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 int keyboardWidth, const int keyboardHeight,
     53         const int gridWidth, const int gridHeight, const int mostCommonKeyWidth,
     54         const int mostCommonKeyHeight, const jintArray proximityChars, const int keyCount,
     55         const jintArray keyXCoordinates, const jintArray keyYCoordinates,
     56         const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes,
     57         const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs,
     58         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     safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityCharsLength,
     86             mProximityCharsArray);
     87     safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates);
     88     safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates);
     89     safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths);
     90     safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights);
     91     safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCodePoints);
     92     safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs);
     93     safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs);
     94     safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii);
     95     initializeG();
     96 }
     97 
     98 ProximityInfo::~ProximityInfo() {
     99     delete[] mProximityCharsArray;
    100 }
    101 
    102 bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
    103     if (x < 0 || y < 0) {
    104         if (DEBUG_DICT) {
    105             AKLOGI("HasSpaceProximity: Illegal coordinates (%d, %d)", x, y);
    106             // TODO: Enable this assertion.
    107             //ASSERT(false);
    108         }
    109         return false;
    110     }
    111 
    112     const int startIndex = ProximityInfoUtils::getStartIndexFromCoordinates(x, y,
    113             CELL_HEIGHT, CELL_WIDTH, GRID_WIDTH);
    114     if (DEBUG_PROXIMITY_INFO) {
    115         AKLOGI("hasSpaceProximity: index %d, %d, %d", startIndex, x, y);
    116     }
    117     int *proximityCharsArray = mProximityCharsArray;
    118     for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
    119         if (DEBUG_PROXIMITY_INFO) {
    120             AKLOGI("Index: %d", mProximityCharsArray[startIndex + i]);
    121         }
    122         if (proximityCharsArray[startIndex + i] == KEYCODE_SPACE) {
    123             return true;
    124         }
    125     }
    126     return false;
    127 }
    128 
    129 float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG(
    130         const int keyId, const int x, const int y, const bool isGeometric) const {
    131     const float centerX = static_cast<float>(getKeyCenterXOfKeyIdG(keyId, x, isGeometric));
    132     const float centerY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId, y, isGeometric));
    133     const float touchX = static_cast<float>(x);
    134     const float touchY = static_cast<float>(y);
    135     return ProximityInfoUtils::getSquaredDistanceFloat(centerX, centerY, touchX, touchY)
    136             / GeometryUtils::SQUARE_FLOAT(static_cast<float>(getMostCommonKeyWidth()));
    137 }
    138 
    139 int ProximityInfo::getCodePointOf(const int keyIndex) const {
    140     if (keyIndex < 0 || keyIndex >= KEY_COUNT) {
    141         return NOT_A_CODE_POINT;
    142     }
    143     return mKeyIndexToLowerCodePointG[keyIndex];
    144 }
    145 
    146 int ProximityInfo::getOriginalCodePointOf(const int keyIndex) const {
    147     if (keyIndex < 0 || keyIndex >= KEY_COUNT) {
    148         return NOT_A_CODE_POINT;
    149     }
    150     return mKeyIndexToOriginalCodePoint[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         mLowerCodePointToKeyMap[lowerCode] = i;
    168         mKeyIndexToOriginalCodePoint[i] = code;
    169         mKeyIndexToLowerCodePointG[i] = lowerCode;
    170     }
    171     for (int i = 0; i < KEY_COUNT; i++) {
    172         mKeyKeyDistancesG[i][i] = 0;
    173         for (int j = i + 1; j < KEY_COUNT; j++) {
    174             if (hasTouchPositionCorrectionData()) {
    175                 // Computes distances using sweet spots if they exist.
    176                 // We have two types of Y coordinate sweet spots, for geometric and for the others.
    177                 // The sweet spots for geometric input are used for calculating key-key distances
    178                 // here.
    179                 mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt(
    180                         mSweetSpotCenterXs[i], mSweetSpotCenterYsG[i],
    181                         mSweetSpotCenterXs[j], mSweetSpotCenterYsG[j]);
    182             } else {
    183                 mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt(
    184                         mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
    185             }
    186             mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
    187         }
    188     }
    189 }
    190 
    191 // referencePointX is used only for keys wider than most common key width. When the referencePointX
    192 // is NOT_A_COORDINATE, this method calculates the return value without using the line segment.
    193 // isGeometric is currently not used because we don't have extra X coordinates sweet spots for
    194 // geometric input.
    195 int ProximityInfo::getKeyCenterXOfKeyIdG(
    196         const int keyId, const int referencePointX, const bool isGeometric) const {
    197     if (keyId < 0) {
    198         return 0;
    199     }
    200     int centerX = (hasTouchPositionCorrectionData()) ? static_cast<int>(mSweetSpotCenterXs[keyId])
    201             : mCenterXsG[keyId];
    202     const int keyWidth = mKeyWidths[keyId];
    203     if (referencePointX != NOT_A_COORDINATE
    204             && keyWidth > getMostCommonKeyWidth()) {
    205         // For keys wider than most common keys, we use a line segment instead of the center point;
    206         // thus, centerX is adjusted depending on referencePointX.
    207         const int keyWidthHalfDiff = (keyWidth - getMostCommonKeyWidth()) / 2;
    208         if (referencePointX < centerX - keyWidthHalfDiff) {
    209             centerX -= keyWidthHalfDiff;
    210         } else if (referencePointX > centerX + keyWidthHalfDiff) {
    211             centerX += keyWidthHalfDiff;
    212         } else {
    213             centerX = referencePointX;
    214         }
    215     }
    216     return centerX;
    217 }
    218 
    219 // When the referencePointY is NOT_A_COORDINATE, this method calculates the return value without
    220 // using the line segment.
    221 int ProximityInfo::getKeyCenterYOfKeyIdG(
    222         const int keyId, const int referencePointY, const bool isGeometric) const {
    223     // TODO: Remove "isGeometric" and have separate "proximity_info"s for gesture and typing.
    224     if (keyId < 0) {
    225         return 0;
    226     }
    227     int centerY;
    228     if (!hasTouchPositionCorrectionData()) {
    229         centerY = mCenterYsG[keyId];
    230     } else if (isGeometric) {
    231         centerY = static_cast<int>(mSweetSpotCenterYsG[keyId]);
    232     } else {
    233         centerY = static_cast<int>(mSweetSpotCenterYs[keyId]);
    234     }
    235     if (referencePointY != NOT_A_COORDINATE &&
    236             centerY + mKeyHeights[keyId] > KEYBOARD_HEIGHT && centerY < referencePointY) {
    237         // When the distance between center point and bottom edge of the keyboard is shorter than
    238         // the key height, we assume the key is located at the bottom row of the keyboard.
    239         // The center point is extended to the bottom edge for such keys.
    240         return referencePointY;
    241     }
    242     return centerY;
    243 }
    244 
    245 int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const {
    246     if (keyId0 >= 0 && keyId1 >= 0) {
    247         return mKeyKeyDistancesG[keyId0][keyId1];
    248     }
    249     return MAX_VALUE_FOR_WEIGHTING;
    250 }
    251 } // namespace latinime
    252