File indexing completed on 2024-04-21 03:51:11

0001 /*
0002     SPDX-FileCopyrightText: 2015 Inge Wallin <inge@lysator.liu.se>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 // Own
0007 #include "utils.h"
0008 
0009 // Qt
0010 #include <QPainter>
0011 #include <QPainterPath>
0012 #include <QRect>
0013 
0014 // KEduVocDocument library
0015 #include <KEduVocDocument>
0016 #include <KEduVocExpression>
0017 
0018 // Parley
0019 #include "prefs.h"
0020 
0021 // ----------------------------------------------------------------
0022 //                         class WordCount
0023 
0024 WordCount::WordCount()
0025 {
0026     clear();
0027 }
0028 
0029 void WordCount::clear()
0030 {
0031     for (int i = 0; i <= KV_MAX_GRADE; ++i) {
0032         grades[i] = 0;
0033         pregrades[i] = 0;
0034     }
0035     invalid = 0;
0036 
0037     initialWords = 0;
0038     totalWords = 0;
0039 }
0040 
0041 int WordCount::percentageCompleted() const
0042 {
0043     // To calculate the percentage done we add:
0044     //  * 1..KV_MAX_GRADE points for words in the initial phase (grade = 0, pregrade > 0)
0045     //  * KV_MAX_GRADE * (1..KV_MAX_GRADE) points for words in the long-term phase (grade>0)
0046     // So the maximum number of points is KV_MAX_GRADE^2 per word.
0047     //
0048     // In the final calculation, we exclude all invalid words from the percentage.
0049     int points = 0;
0050     for (int i = 0; i < KV_MAX_GRADE + 1; ++i) {
0051         points += pregrades[i] * i;
0052         points += grades[i] * KV_MAX_GRADE * i;
0053     }
0054 
0055     if (totalWords - invalid == 0) {
0056         // Don't divide by 0.
0057         return 0;
0058     } else {
0059         return 100 * points / ((totalWords - invalid) * KV_MAX_GRADE * KV_MAX_GRADE);
0060     }
0061 }
0062 
0063 void WordCount::fillFromContainer(KEduVocContainer &container, int translationIndex, KEduVocContainer::EnumEntriesRecursive recursive)
0064 {
0065     clear();
0066 
0067     const QList<KEduVocExpression *> entries = container.entries(recursive);
0068     for (KEduVocExpression *entry : entries) {
0069         KEduVocTranslation &translation(*entry->translation(translationIndex));
0070         evaluateWord(translation, translation.text());
0071     }
0072 }
0073 
0074 void WordCount::fillFromContainerForPracticeMode(KEduVocContainer &container,
0075                                                  int translationIndex,
0076                                                  const QStringList &activeConjugationTenses,
0077                                                  KEduVocContainer::EnumEntriesRecursive recursive)
0078 {
0079     KEduVocWordFlags wordTypeToProcess(KEduVocWordFlag::NoInformation);
0080     switch (Prefs::practiceMode()) {
0081     case Prefs::EnumPracticeMode::GenderPractice:
0082         wordTypeToProcess = KEduVocWordFlag::Noun;
0083         break;
0084     case Prefs::EnumPracticeMode::ConjugationPractice:
0085         wordTypeToProcess = KEduVocWordFlag::Verb;
0086         break;
0087     case Prefs::EnumPracticeMode::ComparisonPractice:
0088         wordTypeToProcess = KEduVocWordFlag::Adjective | KEduVocWordFlag::Adverb;
0089         break;
0090     default:
0091         fillFromContainer(container, translationIndex, recursive);
0092         return;
0093     }
0094 
0095     clear();
0096 
0097     const QList<KEduVocExpression *> entries = container.entries(recursive);
0098     for (KEduVocExpression *entry : entries) {
0099         KEduVocTranslation &translation(*entry->translation(translationIndex));
0100         if (isValidForProcessing(translation, wordTypeToProcess)) {
0101             switch (wordTypeToProcess) {
0102             case KEduVocWordFlag::Noun: {
0103                 KEduVocText article = translation.article();
0104                 evaluateWord(article, translation.text());
0105             } break;
0106             case KEduVocWordFlag::Verb: {
0107                 QStringList conjugationTenses = translation.conjugationTenses();
0108                 for (const QString &activeTense : qAsConst(activeConjugationTenses)) {
0109                     if (conjugationTenses.contains(activeTense)) {
0110                         KEduVocConjugation conj = translation.getConjugation(activeTense);
0111                         const QList<KEduVocWordFlags> keys = conj.keys();
0112                         for (KEduVocWordFlags key : keys) {
0113                             KEduVocText person = conj.conjugation(key);
0114                             evaluateWord(person, person.text());
0115                         }
0116                     }
0117                 }
0118             } break;
0119             case KEduVocWordFlag::Adjective | KEduVocWordFlag::Adverb: {
0120                 KEduVocText comparative = translation.comparativeForm();
0121                 evaluateWord(comparative, comparative.text());
0122                 KEduVocText superlative = translation.superlativeForm();
0123                 evaluateWord(superlative, superlative.text());
0124             } break;
0125             }
0126         }
0127     }
0128 }
0129 
0130 bool WordCount::isValidForProcessing(KEduVocTranslation &trans, KEduVocWordFlags wordType) const
0131 {
0132     return !trans.isEmpty() && (trans.wordType() != nullptr) && ((trans.wordType()->wordType() & wordType) != 0);
0133 }
0134 
0135 void WordCount::evaluateWord(const KEduVocText &item, const QString &text)
0136 {
0137     ++totalWords;
0138     if (text.isEmpty()) {
0139         ++invalid;
0140     } else if (item.preGrade() > 0) {
0141         // Initial phase (we assume correctness, i.e. if pregrade>0 then grade = 0)
0142         ++initialWords;
0143         ++pregrades[item.preGrade()];
0144     } else {
0145         // Long term or unpracticed
0146         ++grades[item.grade()];
0147     }
0148 }
0149 
0150 // ----------------------------------------------------------------
0151 //                         class confidenceColors
0152 
0153 ConfidenceColors::ConfidenceColors(ColorScheme colorScheme)
0154 {
0155     initColors(colorScheme);
0156 }
0157 
0158 void ConfidenceColors::initColors(ColorScheme colorScheme)
0159 {
0160     switch (colorScheme) {
0161     case MultiColorScheme:
0162     default: // Not default at the last line.  Hope this works...
0163 
0164         longTermColors[0] = QColor(25, 38, 41);
0165         // longTermColors[1] = QColor(Qt::yellow);
0166         longTermColors[1] = QColor(25, 38, 41, 64);
0167         longTermColors[2] = QColor(237, 21, 21);
0168         longTermColors[3] = QColor(246, 116, 0);
0169         longTermColors[4] = QColor(201, 206, 59);
0170         longTermColors[5] = QColor(28, 220, 154);
0171         longTermColors[6] = QColor(17, 209, 22);
0172         longTermColors[7] = QColor(61, 174, 253);
0173 
0174         initialTermColor = QColor(25, 38, 41, 64); // Find something else
0175 
0176         invalidColor = QColor(Qt::red);
0177         break;
0178 
0179     case ProgressiveColorScheme: {
0180         static const int AlphaMax = 255;
0181         static const int AlphaStep = ((AlphaMax - 10) / KV_MAX_GRADE);
0182 
0183         QColor color;
0184 
0185         // Confidence 1..max
0186         for (int grade = 1; grade <= KV_MAX_GRADE; ++grade) {
0187             color = Prefs::gradeColor();
0188             color.setAlpha(AlphaMax - (KV_MAX_GRADE - grade) * AlphaStep);
0189 
0190             longTermColors[grade] = color;
0191         }
0192 
0193         // Unpracticed (confidence 0)
0194         color = QColor("#FFFFFF");
0195         color.setAlpha(AlphaMax);
0196         longTermColors[0] = color;
0197 
0198         // Use one color for all initial phase values
0199         color = Prefs::preGradeColor();
0200         color.setAlpha(AlphaMax);
0201         initialTermColor = color;
0202 
0203         // Invalid
0204         invalidColor = Prefs::invalidUnitColor();
0205 
0206         break;
0207     }
0208     }
0209 
0210     // These two are placeholders for the wordcloud background color.
0211     frontEndColors[0] = QColor(255, 221, 217);
0212     frontEndColors[1] = QColor(238, 232, 213);
0213 }
0214 
0215 // ----------------------------------------------------------------
0216 //                         Various utility functions
0217 
0218 void paintColorBar(QPainter &painter, const QRect &rect, const WordCount &wordCount, const ConfidenceColors &colors)
0219 {
0220     // The outline of the total bar.
0221     QRectF roundedRect(rect);
0222     roundedRect.adjust(1.0, 1.0, -1.0, -1.0);
0223 
0224     // Set a rounded clipping region to paint the bar segments in
0225     QPainterPath clippingPath;
0226     clippingPath.addRoundedRect(roundedRect, 2.0, 2.0);
0227     painter.setClipPath(clippingPath);
0228 
0229     qreal xPosition = 0.0;
0230 
0231     // >0: grade, 0: initial, -1: untrained, -2: invalid
0232     for (int i = KV_MAX_GRADE; i >= -2; --i) {
0233         qreal fraction;
0234         QColor color;
0235 
0236         // Get the fraction and the color
0237         if (i > 0) {
0238             // long term
0239             fraction = qreal(wordCount.grades[i]) / qreal(wordCount.totalWords);
0240             color = colors.longTermColors[i];
0241         } else if (i == 0) {
0242             // initial term
0243             fraction = qreal(wordCount.initialWords) / qreal(wordCount.totalWords);
0244             color = colors.initialTermColor;
0245         } else if (i == -1) {
0246             // untrained (stored in longterm[0])
0247             fraction = qreal(wordCount.grades[0]) / qreal(wordCount.totalWords);
0248             color = colors.longTermColors[0];
0249         } else {
0250             fraction = qreal(wordCount.invalid) / qreal(wordCount.totalWords);
0251             color = colors.invalidColor;
0252         }
0253 
0254         // Create a rect from the current fraction
0255         qreal barElementWidth = fraction * roundedRect.width();
0256         QRectF barElement(roundedRect.x() + xPosition, roundedRect.y(), barElementWidth, roundedRect.height());
0257         xPosition += barElementWidth;
0258 
0259         // Paint!
0260         painter.setBrush(QBrush(color));
0261         painter.drawRect(barElement);
0262     }
0263 
0264     // Draw the outline
0265     painter.setClipping(false);
0266     painter.setBrush(Qt::NoBrush);
0267     painter.drawRoundedRect(roundedRect, 2.0, 2.0);
0268 }