Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4ba822c authored by Yusuke Nojima's avatar Yusuke Nojima
Browse files

Promote touches in hit box according to the distance from sweet spot

Change-Id: Ice0fd0514304a79aed67627c2ea3439bd5177de4
parent a62a022e
Loading
Loading
Loading
Loading
+47 −36
Original line number Diff line number Diff line
@@ -118,9 +118,8 @@ bool Correction::initProcessState(const int outputIndex) {
    mInputIndex = mCorrectionStates[outputIndex].mInputIndex;
    mNeedsToTraverseAllNodes = mCorrectionStates[outputIndex].mNeedsToTraverseAllNodes;

    mEquivalentCharStrongCount = mCorrectionStates[outputIndex].mEquivalentCharStrongCount;
    mEquivalentCharNormalCount = mCorrectionStates[outputIndex].mEquivalentCharNormalCount;
    mEquivalentCharWeakCount = mCorrectionStates[outputIndex].mEquivalentCharWeakCount;
    mSumOfDistance = mCorrectionStates[outputIndex].mSumOfDistance;
    mEquivalentCharCount = mCorrectionStates[outputIndex].mEquivalentCharCount;
    mProximityCount = mCorrectionStates[outputIndex].mProximityCount;
    mTransposedCount = mCorrectionStates[outputIndex].mTransposedCount;
    mExcessiveCount = mCorrectionStates[outputIndex].mExcessiveCount;
@@ -175,9 +174,8 @@ void Correction::incrementOutputIndex() {
    mCorrectionStates[mOutputIndex].mInputIndex = mInputIndex;
    mCorrectionStates[mOutputIndex].mNeedsToTraverseAllNodes = mNeedsToTraverseAllNodes;

    mCorrectionStates[mOutputIndex].mEquivalentCharStrongCount = mEquivalentCharStrongCount;
    mCorrectionStates[mOutputIndex].mEquivalentCharNormalCount = mEquivalentCharNormalCount;
    mCorrectionStates[mOutputIndex].mEquivalentCharWeakCount = mEquivalentCharWeakCount;
    mCorrectionStates[mOutputIndex].mSumOfDistance = mSumOfDistance;
    mCorrectionStates[mOutputIndex].mEquivalentCharCount = mEquivalentCharCount;
    mCorrectionStates[mOutputIndex].mProximityCount = mProximityCount;
    mCorrectionStates[mOutputIndex].mTransposedCount = mTransposedCount;
    mCorrectionStates[mOutputIndex].mExcessiveCount = mExcessiveCount;
@@ -220,9 +218,7 @@ Correction::CorrectionType Correction::processSkipChar(
}

inline bool isEquivalentChar(ProximityInfo::ProximityType type) {
    // 'type ProximityInfo::EQUIVALENT_CHAR_WEAK' means that
    // type == ..._WEAK or type == ..._NORMAL or type == ..._STRONG.
    return type <= ProximityInfo::EQUIVALENT_CHAR_WEAK;
    return type == ProximityInfo::EQUIVALENT_CHAR;
}

Correction::CorrectionType Correction::processCharAndCalcState(
@@ -304,7 +300,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
    // TODO: Change the limit if we'll allow two or more proximity chars with corrections
    const bool checkProximityChars = noCorrectionsHappenedSoFar ||  mProximityCount == 0;
    const ProximityInfo::ProximityType matchedProximityCharId = secondTransposing
            ? ProximityInfo::EQUIVALENT_CHAR_NORMAL
            ? ProximityInfo::EQUIVALENT_CHAR
            : mProximityInfo->getMatchedProximityId(mInputIndex, c, checkProximityChars);

    if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
@@ -384,18 +380,14 @@ Correction::CorrectionType Correction::processCharAndCalcState(
        mMatching = true;
    } else if (isEquivalentChar(matchedProximityCharId)) {
        mMatching = true;
        switch (matchedProximityCharId) {
        case ProximityInfo::EQUIVALENT_CHAR_STRONG:
            ++mEquivalentCharStrongCount;
            break;
        case ProximityInfo::EQUIVALENT_CHAR_NORMAL:
            ++mEquivalentCharNormalCount;
            break;
        case ProximityInfo::EQUIVALENT_CHAR_WEAK:
            ++mEquivalentCharWeakCount;
            break;
        default:
            assert(false);
        ++mEquivalentCharCount;
        if (mSumOfDistance != NOT_A_DISTANCE) {
            const int distance = mProximityInfo->getNormalizedSquaredDistance(mInputIndex);
            if (distance != NOT_A_DISTANCE) {
                mSumOfDistance += distance;
            } else {
                mSumOfDistance = NOT_A_DISTANCE;
            }
        }
    } else if (ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) {
        mProximityMatching = true;
@@ -568,8 +560,8 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
    const int transposedCount = correction->mTransposedCount / 2;
    const int excessiveCount = correction->mExcessiveCount + correction->mTransposedCount % 2;
    const int proximityMatchedCount = correction->mProximityCount;
    const int equivalentCharStrongCount = correction->mEquivalentCharStrongCount;
    const int equivalentCharWeakCount = correction->mEquivalentCharWeakCount;
    const int mSumOfDistance = correction->mSumOfDistance;
    const int mEquivalentCharCount = correction->mEquivalentCharCount;
    const bool lastCharExceeded = correction->mLastCharExceeded;
    const bool useFullEditDistance = correction->mUseFullEditDistance;
    const int outputLength = outputIndex + 1;
@@ -679,18 +671,37 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
        multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
    }

    for (int i = 0; i < equivalentCharStrongCount; ++i) {
        if (DEBUG_DICT_FULL) {
            LOGI("equivalent char strong");
        }
        multiplyRate(WORDS_WITH_EQUIVALENT_CHAR_STRONG_PROMOTION_RATE, &finalFreq);
    }

    for (int i = 0; i < equivalentCharWeakCount; ++i) {
        if (DEBUG_DICT_FULL) {
            LOGI("equivalent char weak");
        }
        multiplyRate(WORDS_WITH_EQUIVALENT_CHAR_WEAK_DEMOTION_RATE, &finalFreq);
    if (CALIBRATE_SCORE_BY_TOUCH_COORDINATES
            && mEquivalentCharCount > 0 && mSumOfDistance != NOT_A_DISTANCE) {
        // Let (x, y) be the coordinate of a user's touch, and let c be a key.
        // Assuming users' touch distribution is gauss distribution, the conditional probability of
        // the user touching (x, y) given he or she intends to hit c is:
        //   p(x, y | c) = exp(-(x - m_x) / (2 * s^2)) / (sqrt(2 * pi) * s)
        //               * exp(-(y - m_y) / (2 * s^2)) / (sqrt(2 * pi) * s)
        // where (m_x, m_y) is a mean of touches of c, and s is a variance of touches of c.
        // If user touches c1, c2, .., cn, the joint distribution is
        //   p(x1, y1 | c1) * p(x2, y2 | c2) * ... * p(xn, yn | cn)
        // We consider the logarithm of this value, that is
        //     sum_i log p(x_i, y_i | c_i) + const
        //   = sum_i ((x_i - m_x)^2 + (y_i - m_y)^2) / (2 * s^2) + const
        // Thus, we use the sum of squared distance as a score of the word.
        static const int UPPER = WORDS_WITH_EQUIVALENT_CHAR_STRONGEST_PROMOTION_RATE;
        static const int LOWER = WORDS_WITH_EQUIVALENT_CHAR_WEAKEST_DEMOTION_RATE;
        static const int MIDDLE = 100;
        static const int SHIFT = ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
        const int expected = mEquivalentCharCount << SHIFT;
        // factor is a function as described below:
        // U\            .
        //   \           .
        // M  \          .
        //     \         .
        // L    \------- .
        //  0 e
        // (x-axis is mSumOfDistance, y-axis is rate,
        //  and e, U, M, L are expected, UPPER, MIDDLE, LOWER respectively.
        const int factor =
                max((UPPER * expected - (UPPER - MIDDLE) * mSumOfDistance) / expected, LOWER);
        multiplyRate(factor, &finalFreq);
    }

    const int errorCount = adjustedProximityMatchedCount > 0
+2 −3
Original line number Diff line number Diff line
@@ -127,9 +127,8 @@ private:
    int mOutputIndex;
    int mInputIndex;

    int mEquivalentCharStrongCount;
    int mEquivalentCharNormalCount;
    int mEquivalentCharWeakCount;
    int mEquivalentCharCount;
    int mSumOfDistance;
    int mProximityCount;
    int mExcessiveCount;
    int mTransposedCount;
+4 −6
Original line number Diff line number Diff line
@@ -29,9 +29,8 @@ struct CorrectionState {
    uint16_t mChildCount;
    uint8_t mInputIndex;

    uint8_t mEquivalentCharStrongCount;
    uint8_t mEquivalentCharNormalCount;
    uint8_t mEquivalentCharWeakCount;
    int32_t mSumOfDistance;
    uint8_t mEquivalentCharCount;
    uint8_t mProximityCount;
    uint8_t mTransposedCount;
    uint8_t mExcessiveCount;
@@ -66,9 +65,8 @@ inline static void initCorrectionState(CorrectionState *state, const int rootPos
    state->mExcessivePos = -1;
    state->mSkipPos = -1;

    state->mEquivalentCharStrongCount = 0;
    state->mEquivalentCharNormalCount = 0;
    state->mEquivalentCharWeakCount = 0;
    state->mSumOfDistance = 0;
    state->mEquivalentCharCount = 0;
    state->mProximityCount = 0;
    state->mTransposedCount = 0;
    state->mExcessiveCount = 0;
+3 −2
Original line number Diff line number Diff line
@@ -162,6 +162,7 @@ static void dumpWord(const unsigned short* word, const int length) {
#define NEW_DICTIONARY_HEADER_SIZE 5
#define NOT_VALID_WORD -99
#define NOT_A_CHARACTER -1
#define NOT_A_DISTANCE -1

#define KEYCODE_SPACE ' '

@@ -180,8 +181,8 @@ static void dumpWord(const unsigned short* word, const int length) {
#define WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE 75
#define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75
#define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60
#define WORDS_WITH_EQUIVALENT_CHAR_STRONG_PROMOTION_RATE 105
#define WORDS_WITH_EQUIVALENT_CHAR_WEAK_DEMOTION_RATE 95
#define WORDS_WITH_EQUIVALENT_CHAR_STRONGEST_PROMOTION_RATE 110
#define WORDS_WITH_EQUIVALENT_CHAR_WEAKEST_DEMOTION_RATE 90
#define FULL_MATCHED_WORDS_PROMOTION_RATE 120
#define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
#define WORDS_WITH_MATCH_SKIP_PROMOTION_RATE 105
+17 −29
Original line number Diff line number Diff line
@@ -115,41 +115,42 @@ void ProximityInfo::setInputParams(const int* inputCodes, const int inputLength,
    }
    mPrimaryInputWord[inputLength] = 0;
    for (int i = 0; i < mInputLength; ++i) {
        mSweetSpotTypes[i] = calculateSweetSpotType(i);
        float normalizedSquaredDistance = calculateNormalizedSquaredDistance(i);
        if (normalizedSquaredDistance >= 0.0f) {
            mNormalizedSquaredDistance[i] =
                (int)(normalizedSquaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
        } else {
            mNormalizedSquaredDistance[i] = NOT_A_DISTANCE;
        }
    }
}

inline float square(const float x) { return x * x; }

ProximityInfo::SweetSpotType ProximityInfo::calculateSweetSpotType(int index) const {
float ProximityInfo::calculateNormalizedSquaredDistance(int index) const {
    static const float NOT_A_DISTANCE_FLOAT = -1.0f;
    if (KEY_COUNT == 0 || !mInputXCoordinates || !mInputYCoordinates) {
        // We do not have the coordinate data
        return UNKNOWN;
        return NOT_A_DISTANCE_FLOAT;
    }
    const int currentChar = getPrimaryCharAt(index);
    const unsigned short baseLowerC = Dictionary::toBaseLowerCase(currentChar);
    if (baseLowerC > MAX_CHAR_CODE) {
        return UNKNOWN;
        return NOT_A_DISTANCE_FLOAT;
    }
    const int keyIndex = mCodeToKeyIndex[baseLowerC];
    if (keyIndex < 0) {
        return UNKNOWN;
        return NOT_A_DISTANCE_FLOAT;
    }
    const float radius = mSweetSpotRadii[keyIndex];
    if (radius <= 0.0) {
        // When there are no calibration data for a key,
        // the radius of the key is assigned to zero.
        return UNKNOWN;
        return NOT_A_DISTANCE;
    }
    const float squaredRadius = square(radius);
    const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(keyIndex, index);
    if (squaredDistance <= squaredRadius) {
        return IN_SWEET_SPOT;
    }
    if (squaredDistance <= square(NEUTRAL_AREA_RADIUS_RATIO) * squaredRadius) {
        return IN_NEUTRAL_AREA;
    }
    return OUT_OF_NEUTRAL_AREA;
    return squaredDistance / squaredRadius;
}

float ProximityInfo::calculateSquaredDistanceFromSweetSpotCenter(
@@ -213,22 +214,7 @@ ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(
    // The first char in the array is what user typed. If it matches right away,
    // that means the user typed that same char for this pos.
    if (firstChar == baseLowerC || firstChar == c) {
        if (CALIBRATE_SCORE_BY_TOUCH_COORDINATES) {
            switch (mSweetSpotTypes[index]) {
            case UNKNOWN:
                return EQUIVALENT_CHAR_NORMAL;
            case IN_SWEET_SPOT:
                return EQUIVALENT_CHAR_STRONG;
            case IN_NEUTRAL_AREA:
                return EQUIVALENT_CHAR_NORMAL;
            case OUT_OF_NEUTRAL_AREA:
                return EQUIVALENT_CHAR_WEAK;
            default:
                assert(false);
            }
        } else {
            return EQUIVALENT_CHAR_NORMAL;
        }
        return EQUIVALENT_CHAR;
    }

    if (!checkProximityChars) return UNRELATED_CHAR;
@@ -266,6 +252,8 @@ bool ProximityInfo::sameAsTyped(const unsigned short *word, int length) const {
    return true;
}

const int ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
const int ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
const int ProximityInfo::MAX_KEY_COUNT_IN_A_KEYBOARD;
const int ProximityInfo::MAX_CHAR_CODE;

Loading