proximity_info.cpp revision 0052dbda762b1871c3214a6abeb5e89f11e091cd
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 <cstring> 22#include <cmath> 23 24#include "defines.h" 25#include "jni.h" 26#include "suggest/core/layout/additional_proximity_chars.h" 27#include "suggest/core/layout/geometry_utils.h" 28#include "suggest/core/layout/proximity_info_params.h" 29#include "utils/char_utils.h" 30 31namespace latinime { 32 33static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, 34 jsize len, jint *buffer) { 35 if (jArray && buffer) { 36 env->GetIntArrayRegion(jArray, 0, len, buffer); 37 } else if (buffer) { 38 memset(buffer, 0, len * sizeof(buffer[0])); 39 } 40} 41 42static AK_FORCE_INLINE void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray, 43 jsize len, jfloat *buffer) { 44 if (jArray && buffer) { 45 env->GetFloatArrayRegion(jArray, 0, len, buffer); 46 } else if (buffer) { 47 memset(buffer, 0, len * sizeof(buffer[0])); 48 } 49} 50 51ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, 52 const int keyboardWidth, const int keyboardHeight, const int gridWidth, 53 const int gridHeight, const int mostCommonKeyWidth, const int mostCommonKeyHeight, 54 const jintArray proximityChars, const int keyCount, const jintArray keyXCoordinates, 55 const jintArray keyYCoordinates, const jintArray keyWidths, const jintArray keyHeights, 56 const jintArray keyCharCodes, const jfloatArray sweetSpotCenterXs, 57 const jfloatArray sweetSpotCenterYs, const jfloatArray sweetSpotRadii) 58 : GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth), 59 MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), 60 MOST_COMMON_KEY_HEIGHT(mostCommonKeyHeight), 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(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 mCodeToKeyMap() { 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 105ProximityInfo::~ProximityInfo() { 106 delete[] mProximityCharsArray; 107} 108 109bool 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 136float 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 146int ProximityInfo::getCodePointOf(const int keyIndex) const { 147 if (keyIndex < 0 || keyIndex >= KEY_COUNT) { 148 return NOT_A_CODE_POINT; 149 } 150 return mKeyIndexToCodePointG[keyIndex]; 151} 152 153void 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 mCodeToKeyMap[lowerCode] = i; 168 mKeyIndexToCodePointG[i] = lowerCode; 169 } 170 for (int i = 0; i < KEY_COUNT; i++) { 171 mKeyKeyDistancesG[i][i] = 0; 172 for (int j = i + 1; j < KEY_COUNT; j++) { 173 if (hasTouchPositionCorrectionData()) { 174 // Computes distances using sweet spots if they exist. 175 // We have two types of Y coordinate sweet spots, for geometric and for the others. 176 // The sweet spots for geometric input are used for calculating key-key distances 177 // here. 178 mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt( 179 mSweetSpotCenterXs[i], mSweetSpotCenterYsG[i], 180 mSweetSpotCenterXs[j], mSweetSpotCenterYsG[j]); 181 } else { 182 mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt( 183 mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]); 184 } 185 mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j]; 186 } 187 } 188} 189 190// referencePointX is used only for keys wider than most common key width. When the referencePointX 191// is NOT_A_COORDINATE, this method calculates the return value without using the line segment. 192// isGeometric is currently not used because we don't have extra X coordinates sweet spots for 193// geometric input. 194int ProximityInfo::getKeyCenterXOfKeyIdG( 195 const int keyId, const int referencePointX, const bool isGeometric) const { 196 if (keyId < 0) { 197 return 0; 198 } 199 int centerX = (hasTouchPositionCorrectionData()) ? static_cast<int>(mSweetSpotCenterXs[keyId]) 200 : mCenterXsG[keyId]; 201 const int keyWidth = mKeyWidths[keyId]; 202 if (referencePointX != NOT_A_COORDINATE 203 && keyWidth > getMostCommonKeyWidth()) { 204 // For keys wider than most common keys, we use a line segment instead of the center point; 205 // thus, centerX is adjusted depending on referencePointX. 206 const int keyWidthHalfDiff = (keyWidth - getMostCommonKeyWidth()) / 2; 207 if (referencePointX < centerX - keyWidthHalfDiff) { 208 centerX -= keyWidthHalfDiff; 209 } else if (referencePointX > centerX + keyWidthHalfDiff) { 210 centerX += keyWidthHalfDiff; 211 } else { 212 centerX = referencePointX; 213 } 214 } 215 return centerX; 216} 217 218// referencePointY is currently not used because we don't specially handle keys higher than the 219// most common key height. When the referencePointY is NOT_A_COORDINATE, this method should 220// calculate the return value without using the line segment. 221int 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 if (!hasTouchPositionCorrectionData()) { 228 return mCenterYsG[keyId]; 229 } else if (isGeometric) { 230 return static_cast<int>(mSweetSpotCenterYsG[keyId]); 231 } else { 232 return static_cast<int>(mSweetSpotCenterYs[keyId]); 233 } 234} 235 236int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const { 237 if (keyId0 >= 0 && keyId1 >= 0) { 238 return mKeyKeyDistancesG[keyId0][keyId1]; 239 } 240 return MAX_VALUE_FOR_WEIGHTING; 241} 242} // namespace latinime 243