diff options
Diffstat (limited to 'native')
-rw-r--r-- | native/jni/src/binary_format.h | 4 | ||||
-rw-r--r-- | native/jni/src/defines.h | 26 | ||||
-rw-r--r-- | native/jni/src/proximity_info_state.cpp | 196 | ||||
-rw-r--r-- | native/jni/src/proximity_info_state.h | 19 | ||||
-rw-r--r-- | native/jni/src/terminal_attributes.h | 4 | ||||
-rw-r--r-- | native/jni/src/unigram_dictionary.cpp | 10 |
6 files changed, 241 insertions, 18 deletions
diff --git a/native/jni/src/binary_format.h b/native/jni/src/binary_format.h index d8f3e83dd..25d504bfb 100644 --- a/native/jni/src/binary_format.h +++ b/native/jni/src/binary_format.h @@ -43,6 +43,10 @@ class BinaryFormat { static const int FLAG_HAS_SHORTCUT_TARGETS = 0x08; // Flag for bigram presence static const int FLAG_HAS_BIGRAMS = 0x04; + // Flag for non-words (typically, shortcut only entries) + static const int FLAG_IS_NOT_A_WORD = 0x02; + // Flag for blacklist + static const int FLAG_IS_BLACKLISTED = 0x01; // Attribute (bigram/shortcut) related flags: // Flag for presence of more attributes diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index 9b530077a..28661ab20 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -83,12 +83,38 @@ static inline void dumpWordInt(const int *word, const int length) { AKLOGI("i[ %s ]", charBuf); } +#ifndef __ANDROID__ +#define ASSERT(success) do { if(!success) { showStackTrace(); assert(success);};} while (0) +#define SHOW_STACK_TRACE do { showStackTrace(); } while (0) + +#include <execinfo.h> +#include <stdlib.h> +static inline void showStackTrace() { + void *callstack[128]; + int i, frames = backtrace(callstack, 128); + char **strs = backtrace_symbols(callstack, frames); + for (i = 0; i < frames; ++i) { + if (i == 0) { + AKLOGI("=== Trace ==="); + continue; + } + AKLOGI("%s", strs[i]); + } + free(strs); +} +#else +#define ASSERT(success) +#define SHOW_STACK_TRACE +#endif + #else #define AKLOGE(fmt, ...) #define AKLOGI(fmt, ...) #define DUMP_RESULT(words, frequencies, maxWordCount, maxWordLength) #define DUMP_WORD(word, length) #define DUMP_WORD_INT(word, length) +#define ASSERT(success) +#define SHOW_STACK_TRACE #endif #ifdef FLAG_DO_PROFILE diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp index f01b81e8d..e13d4e664 100644 --- a/native/jni/src/proximity_info_state.cpp +++ b/native/jni/src/proximity_info_state.cpp @@ -76,10 +76,24 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi mTimes.clear(); mLengthCache.clear(); mDistanceCache.clear(); - mInputSize = 0; + if (xCoordinates && yCoordinates) { const bool proximityOnly = !isGeometric && (xCoordinates[0] < 0 || yCoordinates[0] < 0); + int lastInputIndex = 0; + for (int i = 0; i < inputSize; ++i) { + const int pid = pointerIds ? pointerIds[i] : 0; + if (pointerId == pid) { + lastInputIndex = i; + } + } + // Working space to save near keys distances for current, prev and prevprev input point. + NearKeysDistanceMap nearKeysDistances[3]; + // These pointers are swapped for each inputs points. + NearKeysDistanceMap *currentNearKeysDistances = &nearKeysDistances[0]; + NearKeysDistanceMap *prevNearKeysDistances = &nearKeysDistances[1]; + NearKeysDistanceMap *prevPrevNearKeysDistances = &nearKeysDistances[2]; + for (int i = 0; i < inputSize; ++i) { // Assuming pointerId == 0 if pointerIds is null. const int pid = pointerIds ? pointerIds[i] : 0; @@ -88,11 +102,22 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi const int x = proximityOnly ? NOT_A_COORDINATE : xCoordinates[i]; const int y = proximityOnly ? NOT_A_COORDINATE : yCoordinates[i]; const int time = times ? times[i] : -1; - if (pushTouchPoint(c, x, y, time, isGeometric)) { - ++mInputSize; + if (pushTouchPoint(c, x, y, time, isGeometric, i == lastInputIndex, + currentNearKeysDistances, prevNearKeysDistances, + prevPrevNearKeysDistances)) { + // Previous point information was popped. + NearKeysDistanceMap *tmp = prevNearKeysDistances; + prevNearKeysDistances = currentNearKeysDistances; + currentNearKeysDistances = tmp; + } else { + NearKeysDistanceMap *tmp = prevPrevNearKeysDistances; + prevPrevNearKeysDistances = prevNearKeysDistances; + prevNearKeysDistances = currentNearKeysDistances; + currentNearKeysDistances = tmp; } } } + mInputSize = mInputXs.size(); } if (mInputSize > 0) { @@ -153,20 +178,151 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi } } -bool ProximityInfoState::pushTouchPoint(const int nodeChar, int x, int y, - const int time, const bool sample) { - const uint32_t size = mInputXs.size(); - // TODO: Should have a const variable for 10 - const int sampleRate = mProximityInfo->getMostCommonKeyWidth() / 10; - if (size > 0) { - const int dist = getDistanceInt(x, y, mInputXs[size - 1], mInputYs[size - 1]); - if (sample && dist < sampleRate) { - return false; +// Calculating point to key distance for all near keys and returning the distance between +// the given point and the nearest key position. +float ProximityInfoState::updateNearKeysDistances(const int x, const int y, + NearKeysDistanceMap *const currentNearKeysDistances) { + static const float NEAR_KEY_THRESHOLD = 10.0f; + + currentNearKeysDistances->clear(); + const int keyCount = mProximityInfo->getKeyCount(); + float nearestKeyDistance = mMaxPointToKeyLength; + for (int k = 0; k < keyCount; ++k) { + const float dist = mProximityInfo->getNormalizedSquaredDistanceFromCenterFloat(k, x, y); + if (dist < NEAR_KEY_THRESHOLD) { + currentNearKeysDistances->insert(std::pair<int, float>(k, dist)); } - mLengthCache.push_back(mLengthCache[size - 1] + dist); - } else { - mLengthCache.push_back(0); + if (nearestKeyDistance > dist) { + nearestKeyDistance = dist; + } + } + return nearestKeyDistance; +} + +// Check if previous point is at local minimum position to near keys. +bool ProximityInfoState::isPrevLocalMin(const NearKeysDistanceMap *const currentNearKeysDistances, + const NearKeysDistanceMap *const prevNearKeysDistances, + const NearKeysDistanceMap *const prevPrevNearKeysDistances) const { + static const float MARGIN = 0.5f; + + for (NearKeysDistanceMap::const_iterator it = prevNearKeysDistances->begin(); + it != prevNearKeysDistances->end(); ++it) { + NearKeysDistanceMap::const_iterator itPP = prevPrevNearKeysDistances->find(it->first); + NearKeysDistanceMap::const_iterator itC = currentNearKeysDistances->find(it->first); + if ((itPP == prevPrevNearKeysDistances->end() || itPP->second > it->second + MARGIN) + && (itC == currentNearKeysDistances->end() || itC->second > it->second + MARGIN)) { + return true; + } + } + return false; +} + +// Calculating a point score that indicates usefulness of the point. +float ProximityInfoState::getPointScore( + const int x, const int y, const int time, const bool lastPoint, const float nearest, + const NearKeysDistanceMap *const currentNearKeysDistances, + const NearKeysDistanceMap *const prevNearKeysDistances, + const NearKeysDistanceMap *const prevPrevNearKeysDistances) const { + static const float BASE_SAMPLE_RATE_SCALE = 0.1f; + static const float SAVE_DISTANCE_SCALE = 12.0f; + static const float SAVE_DISTANCE_SCORE = 2.0f; + static const float SKIP_DISTANCE_SCALE = 1.5f; + static const float SKIP_DISTANCE_SCORE = -1.0f; + static const float CHECK_LOCALMIN_DISTANCE_THRESHOLD_SCALE = 2.5f; + static const float CHECK_LOCALMIN_DISTANCE_SCORE = -1.0f; + static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F / 32.0f; + static const float STRAIGHT_SKIP_DISTANCE_THRESHOLD_SCALE = 4.0f; + static const float STRAIGHT_SKIP_NEAREST_DISTANCE_THRESHOLD = 0.5f; + static const float STRAIGHT_SKIP_SCORE = -1.0f; + + const std::size_t size = mInputXs.size(); + if (size <= 1) { + return 0; } + const float baseSampleRate = mProximityInfo->getMostCommonKeyWidth() * BASE_SAMPLE_RATE_SCALE; + const float distNext = getDistanceFloat(x, y, mInputXs.back(), mInputYs.back()); + const float distPrev = getDistanceFloat(mInputXs.back(), mInputYs.back(), + mInputXs[size - 2], mInputYs[size - 2]); + float score = 0.0f; + + // Sum of distances + if (distPrev + distNext > baseSampleRate * SAVE_DISTANCE_SCALE) { + score += SAVE_DISTANCE_SCORE; + } + // Distance + if (distPrev < baseSampleRate * SKIP_DISTANCE_SCALE) { + score += SKIP_DISTANCE_SCORE; + } + // Location + if (!isPrevLocalMin(currentNearKeysDistances, currentNearKeysDistances, + prevPrevNearKeysDistances)) { + if (distPrev < baseSampleRate * CHECK_LOCALMIN_DISTANCE_THRESHOLD_SCALE) { + score += CHECK_LOCALMIN_DISTANCE_SCORE; + } + } + // Angle + const float angle1 = getAngle(x, y, mInputXs.back(), mInputYs.back()); + const float angle2 = getAngle(mInputXs.back(), mInputYs.back(), + mInputXs[size - 2], mInputYs[size - 2]); + if (getAngleDiff(angle1, angle2) < STRAIGHT_ANGLE_THRESHOLD) { + if (nearest > STRAIGHT_SKIP_NEAREST_DISTANCE_THRESHOLD + && distPrev < baseSampleRate * STRAIGHT_SKIP_DISTANCE_THRESHOLD_SCALE) { + score += STRAIGHT_SKIP_SCORE; + } + } + return score; +} + +// Sampling touch point and pushing information to vectors. +// Returning if previous point is popped or not. +bool ProximityInfoState::pushTouchPoint(const int nodeChar, int x, int y, const int time, + const bool sample, const bool isLastPoint, + NearKeysDistanceMap *const currentNearKeysDistances, + const NearKeysDistanceMap *const prevNearKeysDistances, + const NearKeysDistanceMap *const prevPrevNearKeysDistances) { + static const float LAST_POINT_SKIP_DISTANCE_SCALE = 0.25f; + + uint32_t size = mInputXs.size(); + bool popped = false; + if (nodeChar < 0 && sample) { + const float nearest = updateNearKeysDistances(x, y, currentNearKeysDistances); + const float score = getPointScore(x, y, time, isLastPoint, nearest, + currentNearKeysDistances, prevNearKeysDistances, prevPrevNearKeysDistances); + if (score < 0) { + // Pop previous point because it would be useless. + mInputXs.pop_back(); + mInputYs.pop_back(); + mTimes.pop_back(); + mLengthCache.pop_back(); + size = mInputXs.size(); + popped = true; + } else { + popped = false; + } + // Check if the last point should be skipped. + if (isLastPoint) { + if (size > 0 && getDistanceFloat(x, y, mInputXs.back(), mInputYs.back()) + < mProximityInfo->getMostCommonKeyWidth() * LAST_POINT_SKIP_DISTANCE_SCALE) { + return popped; + } else if (size > 1) { + int minChar = 0; + float minDist = mMaxPointToKeyLength; + for (NearKeysDistanceMap::const_iterator it = currentNearKeysDistances->begin(); + it != currentNearKeysDistances->end(); ++it) { + if(minDist > it->second){ + minChar = it->first; + minDist = it->second; + } + } + NearKeysDistanceMap::const_iterator itPP = + prevNearKeysDistances->find(minChar); + if (itPP != prevNearKeysDistances->end() && minDist > itPP->second) { + return popped; + } + } + } + } + if (nodeChar >= 0 && (x < 0 || y < 0)) { const int keyId = mProximityInfo->getKeyIndex(nodeChar); if (keyId >= 0) { @@ -174,10 +330,18 @@ bool ProximityInfoState::pushTouchPoint(const int nodeChar, int x, int y, y = mProximityInfo->getKeyCenterYOfIdG(keyId); } } + + // Pushing point information. + if (size > 0) { + mLengthCache.push_back( + mLengthCache.back() + getDistanceInt(x, y, mInputXs.back(), mInputYs.back())); + } else { + mLengthCache.push_back(0); + } mInputXs.push_back(x); mInputYs.push_back(y); mTimes.push_back(time); - return true; + return popped; } float ProximityInfoState::calculateNormalizedSquaredDistance( diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h index 26fd89b36..746b9c968 100644 --- a/native/jni/src/proximity_info_state.h +++ b/native/jni/src/proximity_info_state.h @@ -24,6 +24,7 @@ #include "char_utils.h" #include "defines.h" +#include "hash_map_compat.h" namespace latinime { @@ -216,6 +217,7 @@ class ProximityInfoState { private: DISALLOW_COPY_AND_ASSIGN(ProximityInfoState); + typedef hash_map_compat<int, float> NearKeysDistanceMap; ///////////////////////////////////////// // Defined in proximity_info_state.cpp // ///////////////////////////////////////// @@ -224,7 +226,11 @@ class ProximityInfoState { float calculateSquaredDistanceFromSweetSpotCenter( const int keyIndex, const int inputIndex) const; - bool pushTouchPoint(const int nodeChar, int x, int y, const int time, const bool sample); + bool pushTouchPoint(const int nodeChar, int x, int y, const int time, + const bool sample, const bool isLastPoint, + NearKeysDistanceMap *const currentNearKeysDistances, + const NearKeysDistanceMap *const prevNearKeysDistances, + const NearKeysDistanceMap *const prevPrevNearKeysDistances); ///////////////////////////////////////// // Defined here // ///////////////////////////////////////// @@ -238,6 +244,17 @@ class ProximityInfoState { return mInputCodes + (index * MAX_PROXIMITY_CHARS_SIZE_INTERNAL); } + float updateNearKeysDistances(const int x, const int y, + NearKeysDistanceMap *const currentNearKeysDistances); + bool isPrevLocalMin(const NearKeysDistanceMap *const currentNearKeysDistances, + const NearKeysDistanceMap *const prevNearKeysDistances, + const NearKeysDistanceMap *const prevPrevNearKeysDistances) const; + float getPointScore( + const int x, const int y, const int time, const bool last, const float nearest, + const NearKeysDistanceMap *const currentNearKeysDistances, + const NearKeysDistanceMap *const prevNearKeysDistances, + const NearKeysDistanceMap *const prevPrevNearKeysDistances) const; + // const const ProximityInfo *mProximityInfo; float mMaxPointToKeyLength; diff --git a/native/jni/src/terminal_attributes.h b/native/jni/src/terminal_attributes.h index 34ab8f0ef..9ff2772b1 100644 --- a/native/jni/src/terminal_attributes.h +++ b/native/jni/src/terminal_attributes.h @@ -72,6 +72,10 @@ class TerminalAttributes { return ShortcutIterator(mDict, mStartPos + BinaryFormat::SHORTCUT_LIST_SIZE_SIZE, mFlags); } + bool isBlacklistedOrNotAWord() const { + return mFlags & (BinaryFormat::FLAG_IS_BLACKLISTED | BinaryFormat::FLAG_IS_NOT_A_WORD); + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(TerminalAttributes); const uint8_t *const mDict; diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp index ba3c2db6b..d4c51df63 100644 --- a/native/jni/src/unigram_dictionary.cpp +++ b/native/jni/src/unigram_dictionary.cpp @@ -391,9 +391,11 @@ inline void UnigramDictionary::onTerminal(const int probability, const int finalProbability = correction->getFinalProbability(probability, &wordPointer, &wordLength); - if (0 != finalProbability) { + if (0 != finalProbability && !terminalAttributes.isBlacklistedOrNotAWord()) { // If the probability is 0, we don't want to add this word. However we still // want to add its shortcuts (including a possible whitelist entry) if any. + // Furthermore, if this is not a word (shortcut only for example) or a blacklisted + // entry then we never want to suggest this. addWord(wordPointer, wordLength, finalProbability, masterQueue, Dictionary::KIND_CORRECTION); } @@ -841,6 +843,12 @@ int UnigramDictionary::getFrequency(const int32_t *const inWord, const int lengt return NOT_A_PROBABILITY; } const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos); + if (flags & (BinaryFormat::FLAG_IS_BLACKLISTED | BinaryFormat::FLAG_IS_NOT_A_WORD)) { + // If this is not a word, or if it's a blacklisted entry, it should behave as + // having no frequency outside of the suggestion process (where it should be used + // for shortcuts). + return NOT_A_PROBABILITY; + } const bool hasMultipleChars = (0 != (BinaryFormat::FLAG_HAS_MULTIPLE_CHARS & flags)); if (hasMultipleChars) { pos = BinaryFormat::skipOtherCharacters(root, pos); |