From 7b40f5597315b27a06c5de8d3e30406d7518789a Mon Sep 17 00:00:00 2001 From: maximilianwalter <62749521+maximilianwalter@users.noreply.github.com> Date: Wed, 15 Apr 2020 18:56:36 +0200 Subject: [PATCH 01/85] added equivalents --- apps/calculation/base.de.i18n | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/calculation/base.de.i18n b/apps/calculation/base.de.i18n index e71cdc958..18aab0374 100644 --- a/apps/calculation/base.de.i18n +++ b/apps/calculation/base.de.i18n @@ -1,9 +1,9 @@ CalculApp = "Berechnung" CalculAppCapital = "BERECHNUNG" -AdditionalResults = "????" -DecimalBase = "????" -HexadecimalBase = "????" -BinaryBase = "????" -PrimeFactors = "????" -MixedFraction = "????" -EuclideanDivision = "????" +AdditionalResults = "Weitere Ergebnisse" +DecimalBase = "Dezimal" +HexadecimalBase = "Hexadezimal" +BinaryBase = "Binär" +PrimeFactors = "Primfaktoren" +MixedFraction = "Gemischte Zahl" +EuclideanDivision = "Division mit Rest" From 37b876a941ac99d0649248e31a54b0d87a2be197 Mon Sep 17 00:00:00 2001 From: maximilianwalter <62749521+maximilianwalter@users.noreply.github.com> Date: Wed, 15 Apr 2020 19:03:45 +0200 Subject: [PATCH 02/85] added equivalents --- apps/home/base.de.i18n | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/home/base.de.i18n b/apps/home/base.de.i18n index 7cb389438..c6a24187c 100644 --- a/apps/home/base.de.i18n +++ b/apps/home/base.de.i18n @@ -1,4 +1,4 @@ Apps = "Anwendungen" AppsCapital = "ANWENDUNGEN" -ForbidenAppInExamMode1 = "This application is" -ForbidenAppInExamMode2 = "forbidden in exam mode" +ForbidenAppInExamMode1 = "Diese Anwendung ist im" +ForbidenAppInExamMode2 = "Prüfungsmodus nicht erlaubt." From de7aaeb1880120a0a2fedf0de73da0f2c86099e4 Mon Sep 17 00:00:00 2001 From: maximilianwalter <62749521+maximilianwalter@users.noreply.github.com> Date: Wed, 15 Apr 2020 19:08:35 +0200 Subject: [PATCH 03/85] Update base.de.i18n --- apps/probability/base.de.i18n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/probability/base.de.i18n b/apps/probability/base.de.i18n index 45d52dd65..5072301dd 100644 --- a/apps/probability/base.de.i18n +++ b/apps/probability/base.de.i18n @@ -8,7 +8,7 @@ Normal = "Normal" ChiSquared = "Chi-Quadrat" UniformDistribution = "Uniformverteilung" ExponentialDistribution = "Exponentialverteilung" -GeometricDistribution = "Geometrischeverteilung" +GeometricDistribution = "Geometrische Verteilung" PoissonDistribution = "Poisson-Verteilung" ChiSquaredDistribution = "Chi-Quadrat-Verteilung" StudentDistribution = "Student-Verteilung" From b87674ab73c7fed799e0a25b15a23820b3ae6128 Mon Sep 17 00:00:00 2001 From: maximilianwalter <62749521+maximilianwalter@users.noreply.github.com> Date: Wed, 15 Apr 2020 19:25:07 +0200 Subject: [PATCH 04/85] Update toolbox.de.i18n --- apps/toolbox.de.i18n | 98 ++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/apps/toolbox.de.i18n b/apps/toolbox.de.i18n index 40b21996e..81b3ea391 100644 --- a/apps/toolbox.de.i18n +++ b/apps/toolbox.de.i18n @@ -1,61 +1,61 @@ -Unit = "Units" -UnitTimeMenu = "Time" -UnitTimeSecondMenu = "Second" -UnitTimeSecond = "Second" -UnitTimeSecondMilli = "Millisecond" -UnitTimeSecondMicro = "Microsecond" -UnitTimeSecondNano = "Nanosecond" +Unit = "Einheiten" +UnitTimeMenu = "Zeit" +UnitTimeSecondMenu = "Sekunde" +UnitTimeSecond = "Sekunde" +UnitTimeSecondMilli = "Millisekunde" +UnitTimeSecondMicro = "Mikrosekunde" +UnitTimeSecondNano = "Nanosekunde" UnitTimeMinute = "Minute" -UnitTimeHour = "Hour" -UnitTimeDay = "Day" -UnitTimeWeek = "Week" -UnitTimeMonth = "Month" -UnitTimeYear = "Year" -UnitDistanceMenu = "Distance" +UnitTimeHour = "Stunde" +UnitTimeDay = "Tag" +UnitTimeWeek = "Woche" +UnitTimeMonth = "Monat" +UnitTimeYear = "Jahr" +UnitDistanceMenu = "Entfernung" UnitDistanceMeterMenu = "Meter" UnitDistanceMeterKilo = "Kilometer" UnitDistanceMeter = "Meter" UnitDistanceMeterMilli = "Millimeter" UnitDistanceMeterMicro = "Micrometer" UnitDistanceMeterNano = "Nanometer" -UnitDistanceMeterPico = "Picometer" -UnitDistanceAstronomicalUnit = "Astronomical unit" -UnitDistanceLightYear = "Light year" +UnitDistanceMeterPico = "Pikometer" +UnitDistanceAstronomicalUnit = "Astronomische Einheit" +UnitDistanceLightYear = "Lichtjahr" UnitDistanceParsec = "Parsec" -UnitMassMenu = "Mass" -UnitMassGramKilo = "Kilogram" -UnitMassGram = "Gram" -UnitMassGramMilli = "Milligram" -UnitMassGramMicro = "Microgram" -UnitMassGramNano = "Nanogram" +UnitMassMenu = "Masse" +UnitMassGramKilo = "Kilogramm" +UnitMassGram = "Gramm" +UnitMassGramMilli = "Milligramm" +UnitMassGramMicro = "Mikrogramm" +UnitMassGramNano = "Nanogramm" UnitMassTonne = "Tonne" -UnitCurrentMenu = "Electric current" +UnitCurrentMenu = "Electrischer Strom" UnitCurrentAmpere = "Ampere" UnitCurrentAmpereMilli = "Milliampere" -UnitCurrentAmpereMicro = "Microampere" -UnitTemperatureMenu = "Temperature" +UnitCurrentAmpereMicro = "Mikroampere" +UnitTemperatureMenu = "Temperatur" UnitTemperatureKelvin = "Kelvin" -UnitAmountMenu = "Amount of substance" -UnitAmountMole = "Mole" -UnitAmountMoleMilli = "Millimole" -UnitAmountMoleMicro = "Micromole" -UnitLuminousIntensityMenu = "Luminous intensity" +UnitAmountMenu = "Stoffmenge" +UnitAmountMole = "Mol" +UnitAmountMoleMilli = "Millimol" +UnitAmountMoleMicro = "Mikromol" +UnitLuminousIntensityMenu = "Lichtstärke" UnitLuminousIntensityCandela = "Candela" -UnitFrequencyMenu = "Frequency" +UnitFrequencyMenu = "Frequenz" UnitFrequencyHertzGiga = "Gigahertz" UnitFrequencyHertzMega = "Megahertz" UnitFrequencyHertzKilo = "Kilohertz" UnitFrequencyHertz = "Hertz" -UnitForceMenu = "Force" +UnitForceMenu = "Kraft" UnitForceNewtonKilo = "Kilonewton" UnitForceNewton = "Newton" UnitForceNewtonMilli = "Millinewton" -UnitPressureMenu = "Pressure" +UnitPressureMenu = "Druck" UnitPressurePascal = "Pascal" -UnitPressurePascalHecto = "Hectopascal" +UnitPressurePascalHecto = "Hektopascal" UnitPressureBar = "Bar" UnitPressureAtm = "Atmosphere" -UnitEnergyMenu = "Energy" +UnitEnergyMenu = "Energie" UnitEnergyJouleMenu = "Joule" UnitEnergyJouleKilo = "Kilojoule" UnitEnergyJoule = "Joule" @@ -65,39 +65,39 @@ UnitEnergyElectronVoltMega = "Megaelectronvolt" UnitEnergyElectronVoltKilo = "Kiloelectronvolt" UnitEnergyElectronVolt = "Electronvolt" UnitEnergyElectronVoltMilli = "Millielectronvolt" -UnitPowerMenu = "Power" +UnitPowerMenu = "Leistung" UnitPowerWattGiga = "Gigawatt" UnitPowerWattMega = "Megawatt" UnitPowerWattKilo = "Kilowatt" UnitPowerWatt = "Watt" UnitPowerWattMilli = "Milliwatt" -UnitPowerWattMicro = "Microwatt" -UnitElectricChargeMenu = "Electric charge" +UnitPowerWattMicro = "Mikrowatt" +UnitElectricChargeMenu = "Elektrische Ladung" UnitChargeCoulomb = "Coulomb" -UnitPotentialMenu = "Electric potential" +UnitPotentialMenu = "Elektrische Spannung" UnitPotentialVoltKilo = "Kilovolt" UnitPotentialVolt = "Volt" UnitPotentialVoltMilli = "Millivolt" -UnitPotentialVoltMicro = "Microvolt" -UnitCapacitanceMenu = "Electrical capacitance" +UnitPotentialVoltMicro = "Mikrovolt" +UnitCapacitanceMenu = "Elektrische Kapazität" UnitCapacitanceFarad = "Farad" UnitCapacitanceFaradMilli = "Millifarad" -UnitCapacitanceFaradMicro = "Microfarad" -UnitResistanceMenu = "Electrical resistance" +UnitCapacitanceFaradMicro = "Mikrofarad" +UnitResistanceMenu = "Elektrischer Widerstand" UnitResistanceOhmKilo = "Kiloohm" UnitResistanceOhm = "Ohm" UnitConductanceMenu = "Electrical conductance" UnitConductanceSiemens = "Siemens" UnitConductanceSiemensMilli = "Millisiemens" -UnitMagneticFieldMenu = "Magnetic field" +UnitMagneticFieldMenu = "Magnetfeld" UnitMagneticFieldTesla = "Tesla" -InductanceMenu = "Electrical inductance" +InductanceMenu = "Elektrische Induktivität" UnitInductanceHenry = "Henry" -UnitSurfaceMenu = "Area" -UnitSurfaceHectar = "Hectare" -UnitVolumeMenu = "Volume" +UnitSurfaceMenu = "Fläche" +UnitSurfaceHectar = "Hektar" +UnitVolumeMenu = "Volumen" UnitVolumeLiter = "Liter" -UnitVolumeLiterDeci = "Deciliter" +UnitVolumeLiterDeci = "Deziliter" UnitVolumeLiterCenti = "Centiliter" UnitVolumeLiterMilli = "Milliliter" Toolbox = "Werkzeugkasten" From 0b488bc5487840a06fa30ae25bc7195187c63889 Mon Sep 17 00:00:00 2001 From: maximilianwalter <62749521+maximilianwalter@users.noreply.github.com> Date: Wed, 15 Apr 2020 19:31:26 +0200 Subject: [PATCH 05/85] Update toolbox.de.i18n --- apps/toolbox.de.i18n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/toolbox.de.i18n b/apps/toolbox.de.i18n index 81b3ea391..eff98dbe8 100644 --- a/apps/toolbox.de.i18n +++ b/apps/toolbox.de.i18n @@ -29,7 +29,7 @@ UnitMassGramMilli = "Milligramm" UnitMassGramMicro = "Mikrogramm" UnitMassGramNano = "Nanogramm" UnitMassTonne = "Tonne" -UnitCurrentMenu = "Electrischer Strom" +UnitCurrentMenu = "Elektrischer Strom" UnitCurrentAmpere = "Ampere" UnitCurrentAmpereMilli = "Milliampere" UnitCurrentAmpereMicro = "Mikroampere" From 2d0d4a3a8df7115939c5bcd5a16403d85d80c7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 21 Apr 2020 16:06:27 +0200 Subject: [PATCH 06/85] [apps] Fix misc errors in German translation --- apps/probability/base.de.i18n | 2 +- apps/settings/base.de.i18n | 6 +++--- apps/shared.de.i18n | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/probability/base.de.i18n b/apps/probability/base.de.i18n index 5072301dd..5c7c68fba 100644 --- a/apps/probability/base.de.i18n +++ b/apps/probability/base.de.i18n @@ -3,7 +3,7 @@ ProbaAppCapital = "WAHRSCHEINLICHKEIT" ChooseDistribution = "Wählen Sie eine Verteilung" Binomial = "Binomial" Geometric = "Geometrische" -Uniforme = "Uniform" +Uniforme = "Gleichverteilung" Normal = "Normal" ChiSquared = "Chi-Quadrat" UniformDistribution = "Uniformverteilung" diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index 74f24e284..4f853e325 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -5,7 +5,7 @@ DisplayMode = "Zahlenformat" EditionMode = "Eingabe" EditionLinear = "Linear " Edition2D = "Natürlich " -ComplexFormat = "Komplex" +ComplexFormat = "Komplex Zahlen" ExamMode = "Prüfungsmodus" ExamModeActive = "Modus erneut starten" ToDeactivateExamMode1 = "Um den Prüfungsmodus auszuschalten," @@ -29,10 +29,10 @@ Engineering = "Technisch " Scientific = "Wissenschaftlich " SignificantFigures = "Signifikante Stellen " Real = "Reell " -Cartesian = "Algebraische " +Cartesian = "Kartesisch " Polar = "Polar " Brightness = "Helligkeit" -FontSizes = "Python Schriftgröße" +FontSizes = "Python-Schriftgröße" LargeFont = "Große " SmallFont = "Kleine " SoftwareVersion = "Softwareversion" diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index f6ae43bfd..5220c43b4 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -7,7 +7,7 @@ ActiveExamModeMessage3 = "Prüfungsmodus einschalten." ActiveDutchExamModeMessage1 = "All your data will be deleted when" ActiveDutchExamModeMessage2 = "you activate the exam mode. Python" ActiveDutchExamModeMessage3 = "application will be unavailable." -Axis = "Achsen" +Axis = "Achse" Cancel = "Abbrechen" ClearColumn = "Spalte löschen" ColumnOptions = "Optionen der Spalte" @@ -63,7 +63,7 @@ StatTab = "Stats" StandardDeviation = "Standardabweichung" Step = "Schrittwert" StorageMemoryFull1 = "Der Speicher ist voll. Löschen Sie" -StorageMemoryFull2 = "von Daten und versuchen Sie es erneut." +StorageMemoryFull2 = "einige Daten und versuchen Sie es erneut." StoreExpressionNotAllowed = "'store' ist verboten" SyntaxError = "Syntaxfehler" TEnd = "T Endwert" From 2768c143c8acb0beac60aad9b050b49c75769877 Mon Sep 17 00:00:00 2001 From: TheTrueBrot Date: Mon, 27 Apr 2020 20:53:26 +0200 Subject: [PATCH 07/85] Fix a mistake in German translation --- apps/settings/base.de.i18n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index 4f853e325..679dcf2f1 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -5,7 +5,7 @@ DisplayMode = "Zahlenformat" EditionMode = "Eingabe" EditionLinear = "Linear " Edition2D = "Natürlich " -ComplexFormat = "Komplex Zahlen" +ComplexFormat = "Komplexe Zahlen" ExamMode = "Prüfungsmodus" ExamModeActive = "Modus erneut starten" ToDeactivateExamMode1 = "Um den Prüfungsmodus auszuschalten," From 061fe05b404e3873d0f7db8085e696c9dfe227f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 28 Apr 2020 11:11:38 +0200 Subject: [PATCH 08/85] [poincare/normal_distribution] Fix StandardNormalCumulDistFuncAtAbs --- poincare/src/normal_distribution.cpp | 4 ++-- poincare/test/approximation.cpp | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/poincare/src/normal_distribution.cpp b/poincare/src/normal_distribution.cpp index ba62b1dca..710dfacb8 100644 --- a/poincare/src/normal_distribution.cpp +++ b/poincare/src/normal_distribution.cpp @@ -88,8 +88,8 @@ T NormalDistribution::StandardNormalCumulativeDistributiveFunctionAtAbscissa(T a if (std::isnan(abscissa)) { return NAN; } - if (std::isinf(abscissa) || abscissa > k_boundStandardNormalDistribution) { - return (T)1.0; + if (std::isinf(abscissa) || std::fabs(abscissa) > k_boundStandardNormalDistribution) { + return abscissa > (T)0.0 ? (T)1.0 : (T)0.0; } if (abscissa == (T)0.0) { return (T)0.5; diff --git a/poincare/test/approximation.cpp b/poincare/test/approximation.cpp index d985b1c8e..ee3d66795 100644 --- a/poincare/test/approximation.cpp +++ b/poincare/test/approximation.cpp @@ -303,6 +303,10 @@ QUIZ_CASE(poincare_approximation_function) { assert_expression_approximates_to("normcdf(1.2, 3.4, 31.36)", "0.3472125"); assert_expression_approximates_to("normcdf(1.2, 3.4, 31.36)", "3.4721249841587ᴇ-1"); + assert_expression_approximates_to("normcdf(-1ᴇ99,3.4,31.36)", "0"); + assert_expression_approximates_to("normcdf(1ᴇ99,3.4,31.36)", "1"); + assert_expression_approximates_to("normcdf(-6,0,1)", "0"); + assert_expression_approximates_to("normcdf(6,0,1)", "1"); assert_expression_approximates_to("normcdf2(0.5, 3.6, 1.3, 11.56)", "0.3436388"); assert_expression_approximates_to("normcdf2(0.5, 3.6, 1.3, 11.56)", "3.4363881299147ᴇ-1"); From e76abcf67eec1bc92466b9702e3f82eae63577a9 Mon Sep 17 00:00:00 2001 From: Joachim LF Date: Sat, 25 Apr 2020 13:54:16 +0200 Subject: [PATCH 09/85] [Python] universal colors --- python/port/genhdr/qstrdefs.in.h | 1 + python/port/mod/kandinsky/modkandinsky.cpp | 28 ++----- python/port/mod/turtle/modturtle.cpp | 88 ++++++++++++++-------- python/port/mod/turtle/modturtle.h | 1 + python/port/mod/turtle/modturtle_table.c | 2 + python/port/mod/turtle/turtle.cpp | 21 ------ python/port/mod/turtle/turtle.h | 21 ++---- python/port/port.cpp | 80 ++++++++++++++++++++ python/port/port.h | 27 +++++++ python/src/py/obj.h | 2 + python/src/py/objfloat.c | 18 +++++ python/src/py/objint.c | 14 ++++ 12 files changed, 216 insertions(+), 87 deletions(-) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index db21ede99..71a87feee 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -430,6 +430,7 @@ Q(st) Q(hideturtle) Q(ht) Q(isvisible) +Q(colormode) // utime QSTRs Q(time) diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index 27b7ec5b1..66f2aaf15 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -1,26 +1,10 @@ extern "C" { #include "modkandinsky.h" -#include #include } #include #include "port.h" -static KDColor ColorForTuple(mp_obj_t tuple) { - size_t len; - mp_obj_t * elem; - - mp_obj_get_array(tuple, &len, &elem); - if (len != 3) { - mp_raise_TypeError("color needs 3 components"); - } - - return KDColor::RGB888( - mp_obj_get_int(elem[0]), - mp_obj_get_int(elem[1]), - mp_obj_get_int(elem[2]) - ); -} static mp_obj_t TupleForRGB(uint8_t r, uint8_t g, uint8_t b) { mp_obj_tuple_t * t = static_cast(MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL))); @@ -56,19 +40,20 @@ mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y) { return TupleForRGB(c.red(), c.green(), c.blue()); } -mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color) { +mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t input) { KDPoint point(mp_obj_get_int(x), mp_obj_get_int(y)); - KDColor kdColor = ColorForTuple(color); + KDColor kdColor = MicroPython::ColorParser::ParseColor(input); MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDIonContext::sharedContext()->setPixel(point, kdColor); return mp_const_none; } +//TODO Use good colors mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t * args) { const char * text = mp_obj_str_get_str(args[0]); KDPoint point(mp_obj_get_int(args[1]), mp_obj_get_int(args[2])); - KDColor textColor = (n_args >= 4) ? ColorForTuple(args[3]) : KDColorBlack; - KDColor backgroundColor = (n_args >= 5) ? ColorForTuple(args[4]) : KDColorWhite; + KDColor textColor = (n_args >= 4) ? MicroPython::ColorParser::ParseColor(args[3]) : KDColorBlack; + KDColor backgroundColor = (n_args >= 5) ? MicroPython::ColorParser::ParseColor(args[4]) : KDColorWhite; MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDIonContext::sharedContext()->drawString(text, point, KDFont::LargeFont, textColor, backgroundColor); /* Before and after execution of "modkandinsky_draw_string", @@ -98,8 +83,7 @@ mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) { y = y - height; } KDRect rect(x, y, width, height); - KDColor color = ColorForTuple(args[4]); - + KDColor color = MicroPython::ColorParser::ParseColor(args[4]); MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDIonContext::sharedContext()->fillRect(rect, color); // Cf comment on modkandinsky_draw_string diff --git a/python/port/mod/turtle/modturtle.cpp b/python/port/mod/turtle/modturtle.cpp index 913372a3f..35c960ec3 100644 --- a/python/port/mod/turtle/modturtle.cpp +++ b/python/port/mod/turtle/modturtle.cpp @@ -1,6 +1,8 @@ extern "C" { #include "modturtle.h" #include +#include +#include } #include "turtle.h" #include "../../port.h" @@ -131,37 +133,63 @@ mp_obj_t modturtle_isdown() { } mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) { - if (n_args == 0) { - // pencolor() - KDColor c = sTurtle.color(); - mp_obj_t mp_col[3]; - mp_col[0] = mp_obj_new_int_from_uint(c.red()); - mp_col[1] = mp_obj_new_int_from_uint(c.green()); - mp_col[2] = mp_obj_new_int_from_uint(c.blue()); - return mp_obj_new_tuple(3, mp_col); - } - if (n_args == 1) { - if (MP_OBJ_IS_STR(args[0])) { - // pencolor("blue") - size_t l; - sTurtle.setColor(mp_obj_str_get_data(args[0], &l)); - } else { - // pencolor((r, g, b)) - mp_obj_t * rgb; - mp_obj_get_array_fixed_n(args[0], 3, &rgb); - sTurtle.setColor( - KDColor::RGB888( - mp_obj_get_int(rgb[0]), - mp_obj_get_int(rgb[1]), - mp_obj_get_int(rgb[2]))); + switch(n_args){ + case 0:{ + // pencolor() + KDColor c = sTurtle.color(); + mp_obj_t mp_col[3]; + if(sTurtle.colorMode() == MicroPython::ColorParser::ColorModes::MaxIntensity255){ + mp_col[0] = mp_obj_new_int_from_uint(c.red()); + mp_col[1] = mp_obj_new_int_from_uint(c.green()); + mp_col[2] = mp_obj_new_int_from_uint(c.blue()); + } else { + mp_col[0] = mp_obj_new_float(c.red() / 255.0); + mp_col[1] = mp_obj_new_float(c.green() / 255.0); + mp_col[2] = mp_obj_new_float(c.blue() / 255.0); + } + return mp_obj_new_tuple(3, mp_col); + } + case 1:{ + sTurtle.setColor(MicroPython::ColorParser::ParseColor(args[0], sTurtle.colorMode())); + break; + } + case 3:{ + // pencolor(r, g, b) + if(sTurtle.colorMode() == MicroPython::ColorParser::ColorModes::MaxIntensity255){ + sTurtle.setColor( + KDColor::RGB888( + mp_obj_get_int(args[0]), + mp_obj_get_int(args[1]), + mp_obj_get_int(args[2]))); + } else { + sTurtle.setColor( + KDColor::RGB888( + mp_obj_get_int(args[0]) * 255, + mp_obj_get_int(args[1]) * 255, + mp_obj_get_int(args[2]) * 255)); + } + break; + } + default:{ + assert(n_args == 2); + mp_raise_TypeError("pencolor() takes 0, 1 or 3 arguments"); + } + } + return mp_const_none; +} + +mp_obj_t modturtle_colormode(size_t n_args, const mp_obj_t *args){ + if(n_args == 0){ + return (sTurtle.colorMode() == MicroPython::ColorParser::ColorModes::MaxIntensity255) ? mp_obj_new_int_from_uint(255) : mp_obj_new_int_from_uint(1); + } else{ + int colorMode = mp_obj_get_int(args[0]); + if(colorMode == 1){ + sTurtle.setColorMode(MicroPython::ColorParser::ColorModes::MaxIntensity1); + } else if(colorMode == 255){ + sTurtle.setColorMode(MicroPython::ColorParser::ColorModes::MaxIntensity255); + } else { + mp_raise_ValueError("Colormodes can be 1 or 255"); } - } else if (n_args == 3) { - // pencolor(r, g, b) - sTurtle.setColor( - KDColor::RGB888( - mp_obj_get_int(args[0]), - mp_obj_get_int(args[1]), - mp_obj_get_int(args[2]))); } return mp_const_none; } diff --git a/python/port/mod/turtle/modturtle.h b/python/port/mod/turtle/modturtle.h index 84fda5010..303ea13d4 100644 --- a/python/port/mod/turtle/modturtle.h +++ b/python/port/mod/turtle/modturtle.h @@ -25,6 +25,7 @@ mp_obj_t modturtle_pensize(size_t n_args, const mp_obj_t *args); mp_obj_t modturtle_isvisible(); mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args); +mp_obj_t modturtle_colormode(size_t n_args, const mp_obj_t *args); mp_obj_t modturtle_showturtle(); mp_obj_t modturtle_hideturtle(); diff --git a/python/port/mod/turtle/modturtle_table.c b/python/port/mod/turtle/modturtle_table.c index c9f2658a9..c190cc540 100644 --- a/python/port/mod/turtle/modturtle_table.c +++ b/python/port/mod/turtle/modturtle_table.c @@ -18,6 +18,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modturtle_pensize_obj, 0, 1, modturtl STATIC MP_DEFINE_CONST_FUN_OBJ_0(modturtle_isdown_obj, modturtle_isdown); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modturtle_pencolor_obj, 0, 3, modturtle_pencolor); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modturtle_colormode_obj, 0, 1, modturtle_colormode); STATIC MP_DEFINE_CONST_FUN_OBJ_0(modturtle_reset_obj, modturtle_reset); @@ -64,6 +65,7 @@ STATIC const mp_rom_map_elem_t modturtle_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_color), (mp_obj_t)&modturtle_pencolor_obj }, { MP_ROM_QSTR(MP_QSTR_pencolor), (mp_obj_t)&modturtle_pencolor_obj }, + { MP_ROM_QSTR(MP_QSTR_colormode), (mp_obj_t)&modturtle_colormode_obj }, { MP_ROM_QSTR(MP_QSTR_reset), (mp_obj_t)&modturtle_reset_obj }, diff --git a/python/port/mod/turtle/turtle.cpp b/python/port/mod/turtle/turtle.cpp index 1a5c3abc2..811650c3b 100644 --- a/python/port/mod/turtle/turtle.cpp +++ b/python/port/mod/turtle/turtle.cpp @@ -175,27 +175,6 @@ void Turtle::setVisible(bool visible) { } } -void Turtle::setColor(const char * color) { - constexpr NameColorPair pairs[] = { - NameColorPair("blue", KDColorBlue), - NameColorPair("red", KDColorRed), - NameColorPair("green", Palette::Green), - NameColorPair("yellow", KDColorYellow), - NameColorPair("brown", Palette::Brown), - NameColorPair("black", KDColorBlack), - NameColorPair("white", KDColorWhite), - NameColorPair("pink", Palette::Pink), - NameColorPair("orange", Palette::Orange), - NameColorPair("purple", Palette::Purple), - NameColorPair("grey", Palette::GreyDark) - }; - for (NameColorPair p : pairs) { - if (strcmp(p.name(), color) == 0) { - m_color = p.color(); - return; - } - } -} void Turtle::viewDidDisappear() { m_drawn = false; diff --git a/python/port/mod/turtle/turtle.h b/python/port/mod/turtle/turtle.h index 87aa58e8f..71e4ae5fe 100644 --- a/python/port/mod/turtle/turtle.h +++ b/python/port/mod/turtle/turtle.h @@ -8,6 +8,7 @@ extern "C" { #include #include #include +#include /* We check for keyboard interruptions using micropython_port_vm_hook_loop and * micropython_port_interruptible_msleep, but even if we catch an interruption, @@ -29,6 +30,7 @@ public: m_y(0), m_heading(0), m_color(k_defaultColor), + m_colorMode(MicroPython::ColorParser::ColorModes::MaxIntensity255), m_penDown(true), m_visible(true), m_speed(k_defaultSpeed), @@ -71,7 +73,10 @@ public: void setColor(uint8_t r, uint8_t g, uint8_t b) { m_color = KDColor::RGB888(r, g, b); } - void setColor(const char * color); + MicroPython::ColorParser::ColorModes colorMode() const {return m_colorMode; } + void setColorMode(MicroPython::ColorParser::ColorModes colorMode){ + m_colorMode = colorMode; + } void viewDidDisappear(); @@ -102,19 +107,6 @@ private: Forward = 2 }; - class NameColorPair { - public: - constexpr NameColorPair(const char * name, KDColor color) : - m_name(name), - m_color(color) - {} - const char * name() const { return m_name; } - KDColor color() const { return m_color; } - private: - const char * m_name; - KDColor m_color; - }; - void setHeadingPrivate(mp_float_t angle); KDPoint position(mp_float_t x, mp_float_t y) const; KDPoint position() const { return position(m_x, m_y); } @@ -149,6 +141,7 @@ private: mp_float_t m_heading; KDColor m_color; + MicroPython::ColorParser::ColorModes m_colorMode; bool m_penDown; bool m_visible; diff --git a/python/port/port.cpp b/python/port/port.cpp index 12f5f5588..97fffd671 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -23,6 +23,8 @@ extern "C" { #include "mod/matplotlib/pyplot/modpyplot.h" } +#include + static MicroPython::ScriptProvider * sScriptProvider = nullptr; static MicroPython::ExecutionEnvironment * sCurrentExecutionEnvironment = nullptr; @@ -170,6 +172,83 @@ void MicroPython::collectRootsAtAddress(char * address, int byteLength) { #endif } +KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorModes ColorMode){ + if(MP_OBJ_IS_STR(input)){ + size_t l; + const char * color = mp_obj_str_get_data(input, &l); + // TODO add cyan + constexpr NameColorPair pairs[] = { + NameColorPair("blue", KDColorBlue), + NameColorPair("b", KDColorBlue), + NameColorPair("red", KDColorRed), + NameColorPair("r", KDColorRed), + NameColorPair("green", Palette::Green), + NameColorPair("g", Palette::Green), + NameColorPair("yellow", KDColorYellow), + NameColorPair("y", KDColorYellow), + NameColorPair("brown", Palette::Brown), + NameColorPair("black", KDColorBlack), + NameColorPair("k", KDColorBlack), + NameColorPair("white", KDColorWhite), + NameColorPair("w", KDColorWhite), + NameColorPair("pink", Palette::Pink), + NameColorPair("orange", Palette::Orange), + NameColorPair("purple", Palette::Purple), + NameColorPair("grey", Palette::GreyDark) + }; + for (NameColorPair p : pairs) { + if (strcmp(p.name(), color) == 0) { + return p.color(); + } + } + + if(color[0] == '#'){ + if(l != 7){ + mp_raise_ValueError("RGB hex values are 6 bytes long"); + } + uint32_t ColorInt = mp_obj_get_int(mp_obj_new_int_via_str(color+1, 16)); + return KDColor::RGB24(ColorInt); + } + + mp_float_t GreyLevel = mp_obj_float_get(mp_obj_new_float_via_str(color)); + if(GreyLevel >= 0 && GreyLevel <= 1){ + return KDColor::RGB888( + 255 * (float) GreyLevel, + 255 * (float) GreyLevel, + 255 * (float) GreyLevel + ); + } + mp_raise_ValueError("Grey levels are between 0.0 and 1.0"); + } else if(mp_obj_is_int(input)) { + mp_raise_TypeError("Int are not colors"); + //See https://github.com/numworks/epsilon/issues/1533#issuecomment-618443492 + } else { + size_t len; + mp_obj_t * elem; + + mp_obj_get_array(input, &len, &elem); + + if (len != 3) { + mp_raise_TypeError("color needs 3 components"); + } + + if(ColorMode == MicroPython::ColorParser::ColorModes::MaxIntensity1){ + return KDColor::RGB888( + mp_obj_get_float(elem[0]), + mp_obj_get_float(elem[1]), + mp_obj_get_float(elem[2]) + ); + } else { + return KDColor::RGB888( + mp_obj_get_int(elem[0]), + mp_obj_get_int(elem[1]), + mp_obj_get_int(elem[2]) + ); + } + } + mp_raise_TypeError("Color couldn't be parsed"); +} + void gc_collect(void) { void * python_stack_top = MP_STATE_THREAD(stack_top); assert(python_stack_top != NULL); @@ -260,3 +339,4 @@ const char * mp_hal_input(const char * prompt) { assert(sCurrentExecutionEnvironment != nullptr); return sCurrentExecutionEnvironment->inputText(prompt); } + diff --git a/python/port/port.h b/python/port/port.h index 9d14ab3a6..73bf7f810 100644 --- a/python/port/port.h +++ b/python/port/port.h @@ -3,9 +3,11 @@ extern "C" { #include +#include } #include + namespace MicroPython { class ScriptProvider { @@ -39,6 +41,31 @@ void deinit(); void registerScriptProvider(ScriptProvider * s); void collectRootsAtAddress(char * address, int len); +class ColorParser { + private: + class NameColorPair { + public: + constexpr NameColorPair(const char * name, KDColor color) : + m_name(name), + m_color(color) + {} + const char * name() const { return m_name; } + KDColor color() const { return m_color; } + private: + const char * m_name; + KDColor m_color; + }; + + public: + enum class ColorModes { + MaxIntensity1, + MaxIntensity255, + }; + + static KDColor ParseColor(mp_obj_t input, ColorModes ColorMode = ColorModes::MaxIntensity255); +}; + + } #endif diff --git a/python/src/py/obj.h b/python/src/py/obj.h index 5b54892ce..e2d2b584a 100644 --- a/python/src/py/obj.h +++ b/python/src/py/obj.h @@ -90,6 +90,7 @@ static inline bool mp_obj_is_qstr(mp_const_obj_t o) { return ((((mp_int_t)(o)) & 3) == 2); } #define MP_OBJ_QSTR_VALUE(o) (((mp_uint_t)(o)) >> 2) #define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 2) | 2)) +mp_obj_t mp_obj_new_int_via_str(const char* value, int base); #if MICROPY_PY_BUILTINS_FLOAT #define mp_const_float_e MP_ROM_PTR(&mp_const_float_e_obj) @@ -100,6 +101,7 @@ extern const struct _mp_obj_float_t mp_const_float_pi_obj; #define mp_obj_is_float(o) mp_obj_is_type((o), &mp_type_float) mp_float_t mp_obj_float_get(mp_obj_t self_in); mp_obj_t mp_obj_new_float(mp_float_t value); +mp_obj_t mp_obj_new_float_via_str(const char* value); #endif static inline bool mp_obj_is_obj(mp_const_obj_t o) diff --git a/python/src/py/objfloat.c b/python/src/py/objfloat.c index 3da549bb2..c638796ac 100644 --- a/python/src/py/objfloat.c +++ b/python/src/py/objfloat.c @@ -201,6 +201,24 @@ mp_obj_t mp_obj_new_float(mp_float_t value) { return MP_OBJ_FROM_PTR(o); } +mp_obj_t mp_obj_new_float_via_str(const char* value) { + mp_obj_float_t *o = m_new(mp_obj_float_t, 1); + o->base.type = &mp_type_float; + //Avoid \x0 + int length = 0; + for(int i = 0; i <= sizeof(value); i++){ + if(value[i] == '\x0'){ + length = i; + break; + } + }; + if(length == 0){ + length = (int) sizeof(value); + } + o->value = mp_obj_float_get(mp_parse_num_decimal(value, length, false, false, NULL)); + return MP_OBJ_FROM_PTR(o); +} + mp_float_t mp_obj_float_get(mp_obj_t self_in) { assert(mp_obj_is_float(self_in)); mp_obj_float_t *self = MP_OBJ_TO_PTR(self_in); diff --git a/python/src/py/objint.c b/python/src/py/objint.c index 2fdcf5864..b139b09bc 100644 --- a/python/src/py/objint.c +++ b/python/src/py/objint.c @@ -462,3 +462,17 @@ const mp_obj_type_t mp_type_int = { .binary_op = mp_obj_int_binary_op, .locals_dict = (mp_obj_dict_t*)&int_locals_dict, }; + +mp_obj_t mp_obj_new_int_via_str(const char* value, int base) { + int length = 0; + for(int i = 0; i <= sizeof(value); i++){ + if(value[i] == '\x0'){ + length = i; + break; + } + }; + if(length == 0){ + length = (int) sizeof(value); + } + return mp_parse_num_integer(value, length, base, NULL); +} From f5e8d4d9bbfd754572e88f853f7b471867b581fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Apr 2020 14:04:49 +0200 Subject: [PATCH 10/85] [python] Avoid modifying micropython external source, use parsenum.h methods directly --- python/port/port.cpp | 12 ++++++++++-- python/src/py/obj.h | 2 -- python/src/py/objfloat.c | 18 ------------------ python/src/py/objint.c | 14 -------------- 4 files changed, 10 insertions(+), 36 deletions(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index 97fffd671..a91fe7e86 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -7,6 +7,13 @@ #include #include +/* py/parsenum.h is a C header which uses C keyword restrict. + * It does not exist in C++ so we define it here in order to be able to include + * py/parsenum.h header. */ +#ifdef __cplusplus +#define restrict // disable +#endif + extern "C" { #include "py/builtin.h" #include "py/compile.h" @@ -15,6 +22,7 @@ extern "C" { #include "py/mperrno.h" #include "py/mphal.h" #include "py/nlr.h" +#include "py/parsenum.h" #include "py/repl.h" #include "py/runtime.h" #include "py/stackctrl.h" @@ -206,11 +214,11 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorModes ColorMod if(l != 7){ mp_raise_ValueError("RGB hex values are 6 bytes long"); } - uint32_t ColorInt = mp_obj_get_int(mp_obj_new_int_via_str(color+1, 16)); + uint32_t ColorInt = mp_obj_get_int(mp_parse_num_integer(color+1, strlen(color+1), 16, NULL)); return KDColor::RGB24(ColorInt); } - mp_float_t GreyLevel = mp_obj_float_get(mp_obj_new_float_via_str(color)); + mp_float_t GreyLevel = mp_obj_float_get(mp_parse_num_decimal(color, strlen(color), false, false, NULL)); if(GreyLevel >= 0 && GreyLevel <= 1){ return KDColor::RGB888( 255 * (float) GreyLevel, diff --git a/python/src/py/obj.h b/python/src/py/obj.h index e2d2b584a..5b54892ce 100644 --- a/python/src/py/obj.h +++ b/python/src/py/obj.h @@ -90,7 +90,6 @@ static inline bool mp_obj_is_qstr(mp_const_obj_t o) { return ((((mp_int_t)(o)) & 3) == 2); } #define MP_OBJ_QSTR_VALUE(o) (((mp_uint_t)(o)) >> 2) #define MP_OBJ_NEW_QSTR(qst) ((mp_obj_t)((((mp_uint_t)(qst)) << 2) | 2)) -mp_obj_t mp_obj_new_int_via_str(const char* value, int base); #if MICROPY_PY_BUILTINS_FLOAT #define mp_const_float_e MP_ROM_PTR(&mp_const_float_e_obj) @@ -101,7 +100,6 @@ extern const struct _mp_obj_float_t mp_const_float_pi_obj; #define mp_obj_is_float(o) mp_obj_is_type((o), &mp_type_float) mp_float_t mp_obj_float_get(mp_obj_t self_in); mp_obj_t mp_obj_new_float(mp_float_t value); -mp_obj_t mp_obj_new_float_via_str(const char* value); #endif static inline bool mp_obj_is_obj(mp_const_obj_t o) diff --git a/python/src/py/objfloat.c b/python/src/py/objfloat.c index c638796ac..3da549bb2 100644 --- a/python/src/py/objfloat.c +++ b/python/src/py/objfloat.c @@ -201,24 +201,6 @@ mp_obj_t mp_obj_new_float(mp_float_t value) { return MP_OBJ_FROM_PTR(o); } -mp_obj_t mp_obj_new_float_via_str(const char* value) { - mp_obj_float_t *o = m_new(mp_obj_float_t, 1); - o->base.type = &mp_type_float; - //Avoid \x0 - int length = 0; - for(int i = 0; i <= sizeof(value); i++){ - if(value[i] == '\x0'){ - length = i; - break; - } - }; - if(length == 0){ - length = (int) sizeof(value); - } - o->value = mp_obj_float_get(mp_parse_num_decimal(value, length, false, false, NULL)); - return MP_OBJ_FROM_PTR(o); -} - mp_float_t mp_obj_float_get(mp_obj_t self_in) { assert(mp_obj_is_float(self_in)); mp_obj_float_t *self = MP_OBJ_TO_PTR(self_in); diff --git a/python/src/py/objint.c b/python/src/py/objint.c index b139b09bc..2fdcf5864 100644 --- a/python/src/py/objint.c +++ b/python/src/py/objint.c @@ -462,17 +462,3 @@ const mp_obj_type_t mp_type_int = { .binary_op = mp_obj_int_binary_op, .locals_dict = (mp_obj_dict_t*)&int_locals_dict, }; - -mp_obj_t mp_obj_new_int_via_str(const char* value, int base) { - int length = 0; - for(int i = 0; i <= sizeof(value); i++){ - if(value[i] == '\x0'){ - length = i; - break; - } - }; - if(length == 0){ - length = (int) sizeof(value); - } - return mp_parse_num_integer(value, length, base, NULL); -} From 3d1b3f9a0b4a3afb74204ca0bbba697ec8a3264f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Apr 2020 16:52:34 +0200 Subject: [PATCH 11/85] [python] Factorize implementation of pencode (Turtle module) --- python/port/mod/turtle/modturtle.cpp | 65 +++++++++++----------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/python/port/mod/turtle/modturtle.cpp b/python/port/mod/turtle/modturtle.cpp index 35c960ec3..45658c360 100644 --- a/python/port/mod/turtle/modturtle.cpp +++ b/python/port/mod/turtle/modturtle.cpp @@ -133,48 +133,33 @@ mp_obj_t modturtle_isdown() { } mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) { - switch(n_args){ - case 0:{ - // pencolor() - KDColor c = sTurtle.color(); - mp_obj_t mp_col[3]; - if(sTurtle.colorMode() == MicroPython::ColorParser::ColorModes::MaxIntensity255){ - mp_col[0] = mp_obj_new_int_from_uint(c.red()); - mp_col[1] = mp_obj_new_int_from_uint(c.green()); - mp_col[2] = mp_obj_new_int_from_uint(c.blue()); - } else { - mp_col[0] = mp_obj_new_float(c.red() / 255.0); - mp_col[1] = mp_obj_new_float(c.green() / 255.0); - mp_col[2] = mp_obj_new_float(c.blue() / 255.0); - } - return mp_obj_new_tuple(3, mp_col); - } - case 1:{ - sTurtle.setColor(MicroPython::ColorParser::ParseColor(args[0], sTurtle.colorMode())); - break; - } - case 3:{ - // pencolor(r, g, b) - if(sTurtle.colorMode() == MicroPython::ColorParser::ColorModes::MaxIntensity255){ - sTurtle.setColor( - KDColor::RGB888( - mp_obj_get_int(args[0]), - mp_obj_get_int(args[1]), - mp_obj_get_int(args[2]))); - } else { - sTurtle.setColor( - KDColor::RGB888( - mp_obj_get_int(args[0]) * 255, - mp_obj_get_int(args[1]) * 255, - mp_obj_get_int(args[2]) * 255)); - } - break; - } - default:{ - assert(n_args == 2); - mp_raise_TypeError("pencolor() takes 0, 1 or 3 arguments"); + if (n_args == 0) { + // pencolor() + KDColor c = sTurtle.color(); + mp_obj_t mp_col[3]; + if(sTurtle.colorMode() == MicroPython::ColorParser::ColorModes::MaxIntensity255){ + mp_col[0] = mp_obj_new_int_from_uint(c.red()); + mp_col[1] = mp_obj_new_int_from_uint(c.green()); + mp_col[2] = mp_obj_new_int_from_uint(c.blue()); + } else { + mp_col[0] = mp_obj_new_float(c.red() / 255.0); + mp_col[1] = mp_obj_new_float(c.green() / 255.0); + mp_col[2] = mp_obj_new_float(c.blue() / 255.0); } + return mp_obj_new_tuple(3, mp_col); } + if (n_args == 2) { + mp_raise_TypeError("pencolor() takes 0, 1 or 3 arguments"); + return mp_const_none; + } + mp_obj_t color; + if (n_args == 1) { + color = args[0]; + } else { + assert(n_args == 3); + color = mp_obj_new_tuple(3, args); + } + sTurtle.setColor(MicroPython::ColorParser::ParseColor(color, sTurtle.colorMode())); return mp_const_none; } From a564efcaa57a542bd72daaa3dfea0c9e2bfbc567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Apr 2020 16:54:38 +0200 Subject: [PATCH 12/85] [python] Fix bug in ParseColor --- python/port/port.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index a91fe7e86..7b25d57bc 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -242,9 +242,9 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorModes ColorMod if(ColorMode == MicroPython::ColorParser::ColorModes::MaxIntensity1){ return KDColor::RGB888( - mp_obj_get_float(elem[0]), - mp_obj_get_float(elem[1]), - mp_obj_get_float(elem[2]) + 255 * mp_obj_get_float(elem[0]), + 255 * mp_obj_get_float(elem[1]), + 255 * mp_obj_get_float(elem[2]) ); } else { return KDColor::RGB888( From 995cc0453362fb13bf312399bc552b1d3a9992f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Apr 2020 17:01:03 +0200 Subject: [PATCH 13/85] [python] Use of mp_obj_is_str instead of MP_OBJ_IS_STR for more consistency --- python/port/port.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index 7b25d57bc..5edc4dece 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -181,7 +181,7 @@ void MicroPython::collectRootsAtAddress(char * address, int byteLength) { } KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorModes ColorMode){ - if(MP_OBJ_IS_STR(input)){ + if(mp_obj_is_str(input)){ size_t l; const char * color = mp_obj_str_get_data(input, &l); // TODO add cyan From c920df1f765057ba0afd074053e60ac5996375db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Apr 2020 17:02:30 +0200 Subject: [PATCH 14/85] [python] Change enum class name: ColorModes --> ColorMode --- python/port/mod/turtle/modturtle.cpp | 8 ++++---- python/port/mod/turtle/turtle.h | 8 ++++---- python/port/port.cpp | 4 ++-- python/port/port.h | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/python/port/mod/turtle/modturtle.cpp b/python/port/mod/turtle/modturtle.cpp index 45658c360..36c3c9e19 100644 --- a/python/port/mod/turtle/modturtle.cpp +++ b/python/port/mod/turtle/modturtle.cpp @@ -137,7 +137,7 @@ mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) { // pencolor() KDColor c = sTurtle.color(); mp_obj_t mp_col[3]; - if(sTurtle.colorMode() == MicroPython::ColorParser::ColorModes::MaxIntensity255){ + if(sTurtle.colorMode() == MicroPython::ColorParser::ColorMode::MaxIntensity255){ mp_col[0] = mp_obj_new_int_from_uint(c.red()); mp_col[1] = mp_obj_new_int_from_uint(c.green()); mp_col[2] = mp_obj_new_int_from_uint(c.blue()); @@ -165,13 +165,13 @@ mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) { mp_obj_t modturtle_colormode(size_t n_args, const mp_obj_t *args){ if(n_args == 0){ - return (sTurtle.colorMode() == MicroPython::ColorParser::ColorModes::MaxIntensity255) ? mp_obj_new_int_from_uint(255) : mp_obj_new_int_from_uint(1); + return (sTurtle.colorMode() == MicroPython::ColorParser::ColorMode::MaxIntensity255) ? mp_obj_new_int_from_uint(255) : mp_obj_new_int_from_uint(1); } else{ int colorMode = mp_obj_get_int(args[0]); if(colorMode == 1){ - sTurtle.setColorMode(MicroPython::ColorParser::ColorModes::MaxIntensity1); + sTurtle.setColorMode(MicroPython::ColorParser::ColorMode::MaxIntensity1); } else if(colorMode == 255){ - sTurtle.setColorMode(MicroPython::ColorParser::ColorModes::MaxIntensity255); + sTurtle.setColorMode(MicroPython::ColorParser::ColorMode::MaxIntensity255); } else { mp_raise_ValueError("Colormodes can be 1 or 255"); } diff --git a/python/port/mod/turtle/turtle.h b/python/port/mod/turtle/turtle.h index 71e4ae5fe..2c49c6b1d 100644 --- a/python/port/mod/turtle/turtle.h +++ b/python/port/mod/turtle/turtle.h @@ -30,7 +30,7 @@ public: m_y(0), m_heading(0), m_color(k_defaultColor), - m_colorMode(MicroPython::ColorParser::ColorModes::MaxIntensity255), + m_colorMode(MicroPython::ColorParser::ColorMode::MaxIntensity255), m_penDown(true), m_visible(true), m_speed(k_defaultSpeed), @@ -73,8 +73,8 @@ public: void setColor(uint8_t r, uint8_t g, uint8_t b) { m_color = KDColor::RGB888(r, g, b); } - MicroPython::ColorParser::ColorModes colorMode() const {return m_colorMode; } - void setColorMode(MicroPython::ColorParser::ColorModes colorMode){ + MicroPython::ColorParser::ColorMode colorMode() const {return m_colorMode; } + void setColorMode(MicroPython::ColorParser::ColorMode colorMode){ m_colorMode = colorMode; } @@ -141,7 +141,7 @@ private: mp_float_t m_heading; KDColor m_color; - MicroPython::ColorParser::ColorModes m_colorMode; + MicroPython::ColorParser::ColorMode m_colorMode; bool m_penDown; bool m_visible; diff --git a/python/port/port.cpp b/python/port/port.cpp index 5edc4dece..ffd46218e 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -180,7 +180,7 @@ void MicroPython::collectRootsAtAddress(char * address, int byteLength) { #endif } -KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorModes ColorMode){ +KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode){ if(mp_obj_is_str(input)){ size_t l; const char * color = mp_obj_str_get_data(input, &l); @@ -240,7 +240,7 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorModes ColorMod mp_raise_TypeError("color needs 3 components"); } - if(ColorMode == MicroPython::ColorParser::ColorModes::MaxIntensity1){ + if(ColorMode == MicroPython::ColorParser::ColorMode::MaxIntensity1){ return KDColor::RGB888( 255 * mp_obj_get_float(elem[0]), 255 * mp_obj_get_float(elem[1]), diff --git a/python/port/port.h b/python/port/port.h index 73bf7f810..b337c1ef1 100644 --- a/python/port/port.h +++ b/python/port/port.h @@ -57,12 +57,12 @@ class ColorParser { }; public: - enum class ColorModes { + enum class ColorMode { MaxIntensity1, MaxIntensity255, }; - static KDColor ParseColor(mp_obj_t input, ColorModes ColorMode = ColorModes::MaxIntensity255); + static KDColor ParseColor(mp_obj_t input, ColorMode ColorMode = ColorMode::MaxIntensity255); }; From 8f5fa50f223cd44bac9d644fbc007bac6768adce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 29 Apr 2020 10:10:12 +0200 Subject: [PATCH 15/85] [python] Color: avoid magic number 255, clean code of ColorParser and Turtle::colormode --- python/port/mod/turtle/modturtle.cpp | 15 +++++++------- python/port/port.cpp | 31 +++++++++++----------------- python/port/port.h | 4 ++-- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/python/port/mod/turtle/modturtle.cpp b/python/port/mod/turtle/modturtle.cpp index 36c3c9e19..fe2da5659 100644 --- a/python/port/mod/turtle/modturtle.cpp +++ b/python/port/mod/turtle/modturtle.cpp @@ -165,18 +165,17 @@ mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) { mp_obj_t modturtle_colormode(size_t n_args, const mp_obj_t *args){ if(n_args == 0){ - return (sTurtle.colorMode() == MicroPython::ColorParser::ColorMode::MaxIntensity255) ? mp_obj_new_int_from_uint(255) : mp_obj_new_int_from_uint(1); + return mp_obj_new_int_from_uint(static_cast(sTurtle.colorMode())); } else{ int colorMode = mp_obj_get_int(args[0]); - if(colorMode == 1){ - sTurtle.setColorMode(MicroPython::ColorParser::ColorMode::MaxIntensity1); - } else if(colorMode == 255){ - sTurtle.setColorMode(MicroPython::ColorParser::ColorMode::MaxIntensity255); - } else { - mp_raise_ValueError("Colormodes can be 1 or 255"); + if (colorMode != static_cast(MicroPython::ColorParser::ColorMode::MaxIntensity1) && + colorMode != static_cast(MicroPython::ColorParser::ColorMode::MaxIntensity255)) { + mp_raise_ValueError("Colormode can be 1 or 255"); + return mp_const_none; } + sTurtle.setColorMode(static_cast(colorMode)); + return mp_const_none; } - return mp_const_none; } mp_obj_t modturtle_showturtle() { diff --git a/python/port/port.cpp b/python/port/port.cpp index ffd46218e..c2ee568f9 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -181,7 +181,8 @@ void MicroPython::collectRootsAtAddress(char * address, int byteLength) { } KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode){ - if(mp_obj_is_str(input)){ + static constexpr int maxColorIntensity = static_cast(ColorMode::MaxIntensity255); + if (mp_obj_is_str(input)) { size_t l; const char * color = mp_obj_str_get_data(input, &l); // TODO add cyan @@ -219,11 +220,11 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode } mp_float_t GreyLevel = mp_obj_float_get(mp_parse_num_decimal(color, strlen(color), false, false, NULL)); - if(GreyLevel >= 0 && GreyLevel <= 1){ + if(GreyLevel >= 0.0 && GreyLevel <= 1.0){ return KDColor::RGB888( - 255 * (float) GreyLevel, - 255 * (float) GreyLevel, - 255 * (float) GreyLevel + maxColorIntensity * (float) GreyLevel, + maxColorIntensity * (float) GreyLevel, + maxColorIntensity * (float) GreyLevel ); } mp_raise_ValueError("Grey levels are between 0.0 and 1.0"); @@ -237,22 +238,14 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode mp_obj_get_array(input, &len, &elem); if (len != 3) { - mp_raise_TypeError("color needs 3 components"); + mp_raise_TypeError("Color needs 3 components"); } - - if(ColorMode == MicroPython::ColorParser::ColorMode::MaxIntensity1){ - return KDColor::RGB888( - 255 * mp_obj_get_float(elem[0]), - 255 * mp_obj_get_float(elem[1]), - 255 * mp_obj_get_float(elem[2]) + int intensityFactor = maxColorIntensity/static_cast(ColorMode); + return KDColor::RGB888( + intensityFactor * mp_obj_get_float(elem[0]), + intensityFactor * mp_obj_get_float(elem[1]), + intensityFactor * mp_obj_get_float(elem[2]) ); - } else { - return KDColor::RGB888( - mp_obj_get_int(elem[0]), - mp_obj_get_int(elem[1]), - mp_obj_get_int(elem[2]) - ); - } } mp_raise_TypeError("Color couldn't be parsed"); } diff --git a/python/port/port.h b/python/port/port.h index b337c1ef1..3ab3a603f 100644 --- a/python/port/port.h +++ b/python/port/port.h @@ -58,8 +58,8 @@ class ColorParser { public: enum class ColorMode { - MaxIntensity1, - MaxIntensity255, + MaxIntensity1 = 1, + MaxIntensity255 = 255, }; static KDColor ParseColor(mp_obj_t input, ColorMode ColorMode = ColorMode::MaxIntensity255); From 1b0b9e48f52f58d998bee62269d64a9b98f98502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 29 Apr 2020 10:11:04 +0200 Subject: [PATCH 16/85] [python] ColorParser: add a TODO --- python/port/port.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/python/port/port.cpp b/python/port/port.cpp index c2ee568f9..0554528fd 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -212,6 +212,7 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode } if(color[0] == '#'){ + // TODO handle #abc as #aabbcc (see matplotlib spec) if(l != 7){ mp_raise_ValueError("RGB hex values are 6 bytes long"); } From af0cdbcc1b6effc40bc573aae419ff3296c39069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 29 Apr 2020 10:14:51 +0200 Subject: [PATCH 17/85] Coding style --- python/port/mod/turtle/modturtle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/port/mod/turtle/modturtle.cpp b/python/port/mod/turtle/modturtle.cpp index fe2da5659..b865ee521 100644 --- a/python/port/mod/turtle/modturtle.cpp +++ b/python/port/mod/turtle/modturtle.cpp @@ -163,7 +163,7 @@ mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) { return mp_const_none; } -mp_obj_t modturtle_colormode(size_t n_args, const mp_obj_t *args){ +mp_obj_t modturtle_colormode(size_t n_args, const mp_obj_t *args) { if(n_args == 0){ return mp_obj_new_int_from_uint(static_cast(sTurtle.colorMode())); } else{ From e7df25d558ef6c48d27d6599635c54d6f3551005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 29 Apr 2020 10:24:22 +0200 Subject: [PATCH 18/85] [python] Kandinsky module: color accepts "#FF0000", "red" kinds of arguments --- python/port/mod/kandinsky/modkandinsky.cpp | 28 +++++++++++-------- python/port/mod/kandinsky/modkandinsky.h | 2 +- .../port/mod/kandinsky/modkandinsky_table.c | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index 66f2aaf15..369d04b94 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -6,11 +6,11 @@ extern "C" { #include "port.h" -static mp_obj_t TupleForRGB(uint8_t r, uint8_t g, uint8_t b) { +static mp_obj_t TupleForRGB(KDColor c) { mp_obj_tuple_t * t = static_cast(MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL))); - t->items[0] = MP_OBJ_NEW_SMALL_INT(r); - t->items[1] = MP_OBJ_NEW_SMALL_INT(g); - t->items[2] = MP_OBJ_NEW_SMALL_INT(b); + t->items[0] = MP_OBJ_NEW_SMALL_INT(c.red()); + t->items[1] = MP_OBJ_NEW_SMALL_INT(c.green()); + t->items[2] = MP_OBJ_NEW_SMALL_INT(c.blue()); return MP_OBJ_FROM_PTR(t); } @@ -20,12 +20,18 @@ static mp_obj_t TupleForRGB(uint8_t r, uint8_t g, uint8_t b) { * the stackViewController and forces the window to redraw itself. * KDIonContext::sharedContext is set to the frame of the last object drawn. */ -mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue) { - return TupleForRGB( - mp_obj_get_int(red), - mp_obj_get_int(green), - mp_obj_get_int(blue) - ); +mp_obj_t modkandinsky_color(size_t n_args, const mp_obj_t *args) { + mp_obj_t color; + if (n_args == 1) { + color = args[0]; + } else if (n_args == 2) { + mp_raise_TypeError("color takes 1 or 3 arguments"); + return mp_const_none; + } else { + assert(n_args == 3); + color = mp_obj_new_tuple(n_args, args); + } + return TupleForRGB(MicroPython::ColorParser::ParseColor(color)); } /* Calling ExecutionEnvironment::displaySandbox() hides the console and switches @@ -37,7 +43,7 @@ mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y) { KDPoint point(mp_obj_get_int(x), mp_obj_get_int(y)); KDColor c; KDIonContext::sharedContext()->getPixel(point, &c); - return TupleForRGB(c.red(), c.green(), c.blue()); + return TupleForRGB(c); } mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t input) { diff --git a/python/port/mod/kandinsky/modkandinsky.h b/python/port/mod/kandinsky/modkandinsky.h index c346650cd..95f0366aa 100644 --- a/python/port/mod/kandinsky/modkandinsky.h +++ b/python/port/mod/kandinsky/modkandinsky.h @@ -1,6 +1,6 @@ #include -mp_obj_t modkandinsky_color(mp_obj_t red, mp_obj_t green, mp_obj_t blue); +mp_obj_t modkandinsky_color(size_t n_args, const mp_obj_t *args); mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y); mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color); mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t *args); diff --git a/python/port/mod/kandinsky/modkandinsky_table.c b/python/port/mod/kandinsky/modkandinsky_table.c index 802bee555..ff916f915 100644 --- a/python/port/mod/kandinsky/modkandinsky_table.c +++ b/python/port/mod/kandinsky/modkandinsky_table.c @@ -1,6 +1,6 @@ #include "modkandinsky.h" -STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_color_obj, modkandinsky_color); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_color_obj, 1, 3, modkandinsky_color); STATIC MP_DEFINE_CONST_FUN_OBJ_2(modkandinsky_get_pixel_obj, modkandinsky_get_pixel); STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_set_pixel_obj, modkandinsky_set_pixel); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_draw_string_obj, 3, 5, modkandinsky_draw_string); From 686a1493e5f1d78ee396ed661cf87f03753b7a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 29 Apr 2020 10:25:04 +0200 Subject: [PATCH 19/85] [python] Coding style --- python/port/mod/turtle/modturtle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/port/mod/turtle/modturtle.cpp b/python/port/mod/turtle/modturtle.cpp index b865ee521..f61b977d0 100644 --- a/python/port/mod/turtle/modturtle.cpp +++ b/python/port/mod/turtle/modturtle.cpp @@ -157,7 +157,7 @@ mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) { color = args[0]; } else { assert(n_args == 3); - color = mp_obj_new_tuple(3, args); + color = mp_obj_new_tuple(n_args, args); } sTurtle.setColor(MicroPython::ColorParser::ParseColor(color, sTurtle.colorMode())); return mp_const_none; From 4f99aa709ae718d26c3aa37671519321485df71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 29 Apr 2020 11:00:19 +0200 Subject: [PATCH 20/85] [apps/code] Add colormode to Toolbox: in Catalog and Turtle module --- apps/code/catalog.de.i18n | 1 + apps/code/catalog.en.i18n | 1 + apps/code/catalog.es.i18n | 1 + apps/code/catalog.fr.i18n | 1 + apps/code/catalog.pt.i18n | 1 + apps/code/catalog.universal.i18n | 2 ++ apps/code/python_toolbox.cpp | 2 ++ 7 files changed, 9 insertions(+) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index 81358ca48..0a9e9c71d 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -167,6 +167,7 @@ PythonTurtleBlue = "Blue color" PythonTurtleBrown = "Brown color" PythonTurtleCircle = "Circle of radius r pixels" PythonTurtleColor = "Set the pen color" +PythonTurtleColorMode = "Set the color mode to 1.0 or 255" PythonTurtleForward = "Move forward by x pixels" PythonTurtleFunction = "turtle module function prefix" PythonTurtleGoto = "Move to (x,y) coordinates" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 2a9d49f08..a480d11b1 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -167,6 +167,7 @@ PythonTurtleBlue = "Blue color" PythonTurtleBrown = "Brown color" PythonTurtleCircle = "Circle of radius r pixels" PythonTurtleColor = "Set the pen color" +PythonTurtleColorMode = "Set the color mode to 1.0 or 255" PythonTurtleForward = "Move forward by x pixels" PythonTurtleFunction = "turtle module function prefix" PythonTurtleGoto = "Move to (x,y) coordinates" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index 2a9d49f08..a480d11b1 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -167,6 +167,7 @@ PythonTurtleBlue = "Blue color" PythonTurtleBrown = "Brown color" PythonTurtleCircle = "Circle of radius r pixels" PythonTurtleColor = "Set the pen color" +PythonTurtleColorMode = "Set the color mode to 1.0 or 255" PythonTurtleForward = "Move forward by x pixels" PythonTurtleFunction = "turtle module function prefix" PythonTurtleGoto = "Move to (x,y) coordinates" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index 5be9abcc9..72819e6ab 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -167,6 +167,7 @@ PythonTurtleBlue = "Couleur bleue" PythonTurtleBrown = "Couleur marron" PythonTurtleCircle = "Cercle de rayon r pixels" PythonTurtleColor = "Modifie la couleur du tracé" +PythonTurtleColorMode = "Met le mode de couleur à 1.0 ou 255" PythonTurtleForward = "Avance de x pixels" PythonTurtleFunction = "Préfixe fonction du module turtle" PythonTurtleGoto = "Va au point de coordonnées (x,y)" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index 9350afcb0..f9cce93fc 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -167,6 +167,7 @@ PythonTurtleBlue = "Blue color" PythonTurtleBrown = "Brown color" PythonTurtleCircle = "Circle of radius r pixels" PythonTurtleColor = "Set the pen color" +PythonTurtleColorMode = "Set the color mode to 1.0 or 255" PythonTurtleForward = "Move forward by x pixels" PythonTurtleFunction = "turtle module function prefix" PythonTurtleGoto = "Move to (x,y) coordinates" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index 64e99af9a..c18370633 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -208,6 +208,8 @@ PythonTurtleCommandBrown = "'brown'" PythonTurtleCommandCircle = "circle(r)" PythonTurtleCommandColor = "color('c')/color(r,g,b)" PythonTurtleCommandColorWithoutArg = "color(\x11)" +PythonTurtleCommandColorMode = "colormode(x)" +PythonTurtleCommandColorModeWithoutArg = "colormode(\x11)" PythonTurtleCommandForward = "forward(x)" PythonTurtleCommandGoto = "goto(x,y)" PythonTurtleCommandGreen = "'green'" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 5043c7f7f..798d701b7 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -145,6 +145,7 @@ const ToolboxMessageTree TurtleModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandShowturtle, I18n::Message::PythonTurtleShowturtle, false), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandHideturtle, I18n::Message::PythonTurtleHideturtle, false), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColor, I18n::Message::PythonTurtleColor, false, I18n::Message::PythonTurtleCommandColorWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode, false, I18n::Message::PythonTurtleCommandColorModeWithoutArg), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlue, I18n::Message::PythonTurtleBlue, false), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRed, I18n::Message::PythonTurtleRed, false), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGreen, I18n::Message::PythonTurtleGreen, false), @@ -285,6 +286,7 @@ const ToolboxMessageTree catalogChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandCircle, I18n::Message::PythonTurtleCircle), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCmathFunction, I18n::Message::PythonCmathFunction, false, I18n::Message::PythonCommandCmathFunctionWithoutArg), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColor, I18n::Message::PythonColor), + ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandComplex, I18n::Message::PythonComplex), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCopySign, I18n::Message::PythonCopySign), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCos, I18n::Message::PythonCos), From ba154158472cf865a6afe860f9f07334cc9e2a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Apr 2020 13:41:17 +0200 Subject: [PATCH 21/85] [python] modturtle: avoid unexpected cast --- python/port/mod/turtle/modturtle.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python/port/mod/turtle/modturtle.cpp b/python/port/mod/turtle/modturtle.cpp index f61b977d0..e6bafba70 100644 --- a/python/port/mod/turtle/modturtle.cpp +++ b/python/port/mod/turtle/modturtle.cpp @@ -132,6 +132,8 @@ mp_obj_t modturtle_isdown() { return sTurtle.isPenDown() ? mp_const_true : mp_const_false; } +mp_float_t uint8tColorToDouble(uint8_t c) { return static_cast(c)/255.0; } + mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { // pencolor() @@ -142,9 +144,9 @@ mp_obj_t modturtle_pencolor(size_t n_args, const mp_obj_t *args) { mp_col[1] = mp_obj_new_int_from_uint(c.green()); mp_col[2] = mp_obj_new_int_from_uint(c.blue()); } else { - mp_col[0] = mp_obj_new_float(c.red() / 255.0); - mp_col[1] = mp_obj_new_float(c.green() / 255.0); - mp_col[2] = mp_obj_new_float(c.blue() / 255.0); + mp_col[0] = mp_obj_new_float(uint8tColorToDouble(c.red())); + mp_col[1] = mp_obj_new_float(uint8tColorToDouble(c.green())); + mp_col[2] = mp_obj_new_float(uint8tColorToDouble(c.blue())); } return mp_obj_new_tuple(3, mp_col); } From 7c24f2fc289f94edb011798cc3566816e1847194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Apr 2020 13:42:22 +0200 Subject: [PATCH 22/85] [python] Coding style --- python/port/port.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index 0554528fd..29be768e4 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -211,9 +211,9 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode } } - if(color[0] == '#'){ + if (color[0] == '#') { // TODO handle #abc as #aabbcc (see matplotlib spec) - if(l != 7){ + if (l != 7) { mp_raise_ValueError("RGB hex values are 6 bytes long"); } uint32_t ColorInt = mp_obj_get_int(mp_parse_num_integer(color+1, strlen(color+1), 16, NULL)); From 0811221943560790817abff63b3104c4ea6183fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Apr 2020 13:42:39 +0200 Subject: [PATCH 23/85] [python] Clean port ColorParser --- python/port/port.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index 29be768e4..027543d10 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -221,12 +221,9 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode } mp_float_t GreyLevel = mp_obj_float_get(mp_parse_num_decimal(color, strlen(color), false, false, NULL)); - if(GreyLevel >= 0.0 && GreyLevel <= 1.0){ - return KDColor::RGB888( - maxColorIntensity * (float) GreyLevel, - maxColorIntensity * (float) GreyLevel, - maxColorIntensity * (float) GreyLevel - ); + if (GreyLevel >= 0.0 && GreyLevel <= 1.0) { + uint8_t color = maxColorIntensity * (float) GreyLevel; + return KDColor::RGB888(color, color, color); } mp_raise_ValueError("Grey levels are between 0.0 and 1.0"); } else if(mp_obj_is_int(input)) { From 4bca330f0d502e8da3d4ff0faedc887bb1b2c74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Apr 2020 13:43:34 +0200 Subject: [PATCH 24/85] [python] Coding style --- python/port/port.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index 027543d10..be16aece9 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -216,13 +216,13 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode if (l != 7) { mp_raise_ValueError("RGB hex values are 6 bytes long"); } - uint32_t ColorInt = mp_obj_get_int(mp_parse_num_integer(color+1, strlen(color+1), 16, NULL)); - return KDColor::RGB24(ColorInt); + uint32_t colorInt = mp_obj_get_int(mp_parse_num_integer(color+1, strlen(color+1), 16, NULL)); + return KDColor::RGB24(colorInt); } - mp_float_t GreyLevel = mp_obj_float_get(mp_parse_num_decimal(color, strlen(color), false, false, NULL)); - if (GreyLevel >= 0.0 && GreyLevel <= 1.0) { - uint8_t color = maxColorIntensity * (float) GreyLevel; + mp_float_t greyLevel = mp_obj_float_get(mp_parse_num_decimal(color, strlen(color), false, false, NULL)); + if (greyLevel >= 0.0 && greyLevel <= 1.0) { + uint8_t color = maxColorIntensity * (float) greyLevel; return KDColor::RGB888(color, color, color); } mp_raise_ValueError("Grey levels are between 0.0 and 1.0"); From 25b2ff31c240730e13ede91d89b91dd44eec4b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Apr 2020 13:47:05 +0200 Subject: [PATCH 25/85] [apps/code] Simplify colormode entry in toolbox --- apps/code/catalog.universal.i18n | 1 - apps/code/python_toolbox.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index c18370633..1e2ecc96e 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -209,7 +209,6 @@ PythonTurtleCommandCircle = "circle(r)" PythonTurtleCommandColor = "color('c')/color(r,g,b)" PythonTurtleCommandColorWithoutArg = "color(\x11)" PythonTurtleCommandColorMode = "colormode(x)" -PythonTurtleCommandColorModeWithoutArg = "colormode(\x11)" PythonTurtleCommandForward = "forward(x)" PythonTurtleCommandGoto = "goto(x,y)" PythonTurtleCommandGreen = "'green'" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 798d701b7..bdb79c4db 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -145,7 +145,7 @@ const ToolboxMessageTree TurtleModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandShowturtle, I18n::Message::PythonTurtleShowturtle, false), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandHideturtle, I18n::Message::PythonTurtleHideturtle, false), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColor, I18n::Message::PythonTurtleColor, false, I18n::Message::PythonTurtleCommandColorWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode, false, I18n::Message::PythonTurtleCommandColorModeWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandColorMode, I18n::Message::PythonTurtleColorMode), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandBlue, I18n::Message::PythonTurtleBlue, false), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandRed, I18n::Message::PythonTurtleRed, false), ToolboxMessageTree::Leaf(I18n::Message::PythonTurtleCommandGreen, I18n::Message::PythonTurtleGreen, false), From 9b419acf4039f2730d9763cf1831a24613b2797a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Apr 2020 13:48:54 +0200 Subject: [PATCH 26/85] [python] kandinsky port: rename TupleForRGB to TupleForKDColor --- python/port/mod/kandinsky/modkandinsky.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index 369d04b94..97b070e51 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -6,7 +6,7 @@ extern "C" { #include "port.h" -static mp_obj_t TupleForRGB(KDColor c) { +static mp_obj_t TupleForKDColor(KDColor c) { mp_obj_tuple_t * t = static_cast(MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL))); t->items[0] = MP_OBJ_NEW_SMALL_INT(c.red()); t->items[1] = MP_OBJ_NEW_SMALL_INT(c.green()); @@ -31,7 +31,7 @@ mp_obj_t modkandinsky_color(size_t n_args, const mp_obj_t *args) { assert(n_args == 3); color = mp_obj_new_tuple(n_args, args); } - return TupleForRGB(MicroPython::ColorParser::ParseColor(color)); + return TupleForKDColor(MicroPython::ColorParser::ParseColor(color)); } /* Calling ExecutionEnvironment::displaySandbox() hides the console and switches @@ -43,7 +43,7 @@ mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y) { KDPoint point(mp_obj_get_int(x), mp_obj_get_int(y)); KDColor c; KDIonContext::sharedContext()->getPixel(point, &c); - return TupleForRGB(c); + return TupleForKDColor(c); } mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t input) { From a52202fa90b93fdb5807e239ac3c41f1fb433282 Mon Sep 17 00:00:00 2001 From: sagessylu Date: Sun, 3 May 2020 11:22:01 +0200 Subject: [PATCH 27/85] put accents in toolbox French translation --- apps/toolbox.fr.i18n | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/toolbox.fr.i18n b/apps/toolbox.fr.i18n index ccff63806..0eabb39c1 100644 --- a/apps/toolbox.fr.i18n +++ b/apps/toolbox.fr.i18n @@ -43,7 +43,7 @@ UnitLuminousIntensityMenu = "Intensité lumineuse" UnitLuminousIntensityCandela = "Candela" UnitFrequencyMenu = "Fréquence" UnitFrequencyHertzGiga = "Gigahertz" -UnitFrequencyHertzMega = "Megahertz" +UnitFrequencyHertzMega = "Mégahertz" UnitFrequencyHertzKilo = "Kilohertz" UnitFrequencyHertz = "Hertz" UnitForceMenu = "Force" @@ -60,14 +60,14 @@ UnitEnergyJouleMenu = "Joule" UnitEnergyJouleKilo = "Kilojoule" UnitEnergyJoule = "Joule" UnitEnergyJouleMilli = "Millijoule" -UnitEnergyEletronVoltMenu = "Electronvolt" -UnitEnergyElectronVoltMega = "Megaelectronvolt" -UnitEnergyElectronVoltKilo = "Kiloelectronvolt" -UnitEnergyElectronVolt = "Electronvolt" -UnitEnergyElectronVoltMilli = "Millielectronvolt" +UnitEnergyEletronVoltMenu = "Électronvolt" +UnitEnergyElectronVoltMega = "Mégaélectronvolt" +UnitEnergyElectronVoltKilo = "Kiloélectronvolt" +UnitEnergyElectronVolt = "Électronvolt" +UnitEnergyElectronVoltMilli = "Milliélectronvolt" UnitPowerMenu = "Puissance" UnitPowerWattGiga = "Gigawatt" -UnitPowerWattMega = "Megawatt" +UnitPowerWattMega = "Mégawatt" UnitPowerWattKilo = "Kilowatt" UnitPowerWatt = "Watt" UnitPowerWattMilli = "Milliwatt" @@ -97,7 +97,7 @@ UnitSurfaceMenu = "Superficie" UnitSurfaceHectar = "Hectare" UnitVolumeMenu = "Volume" UnitVolumeLiter = "Litre" -UnitVolumeLiterDeci = "Decilitre" +UnitVolumeLiterDeci = "Décilitre" UnitVolumeLiterCenti = "Centilitre" UnitVolumeLiterMilli = "Millilitre" Toolbox = "Boîte à outils" From f250311856b2a435eea42fdda2204d3daa6f7991 Mon Sep 17 00:00:00 2001 From: Joachim LF Date: Fri, 1 May 2020 14:20:25 +0200 Subject: [PATCH 28/85] [Interval] Patch the need recomputing --- apps/shared/interval.cpp | 4 ++++ apps/shared/interval.h | 2 ++ apps/shared/interval_parameter_controller.cpp | 1 + 3 files changed, 7 insertions(+) diff --git a/apps/shared/interval.cpp b/apps/shared/interval.cpp index 4754feb3d..d39270635 100644 --- a/apps/shared/interval.cpp +++ b/apps/shared/interval.cpp @@ -53,6 +53,10 @@ void Interval::setElement(int i, double f) { } } +void Interval::setNeedRecompute(bool needrecompute) { + m_needCompute = needrecompute; +} + void Interval::reset() { m_start = 0.0; m_end = 10.0; diff --git a/apps/shared/interval.h b/apps/shared/interval.h index 4fed90449..bb2151c40 100644 --- a/apps/shared/interval.h +++ b/apps/shared/interval.h @@ -18,6 +18,8 @@ public: void setEnd(double f); void setStep(double f); void setElement(int i, double f); + bool NeedRecompute() const { return m_needCompute; } + void setNeedRecompute(bool needrecompute); void reset(); void clear(); // TODO: decide the max number of elements after optimization diff --git a/apps/shared/interval_parameter_controller.cpp b/apps/shared/interval_parameter_controller.cpp index 7ab9e275c..c5255e386 100644 --- a/apps/shared/interval_parameter_controller.cpp +++ b/apps/shared/interval_parameter_controller.cpp @@ -85,6 +85,7 @@ int IntervalParameterController::reusableParameterCellCount(int type) { } void IntervalParameterController::buttonAction() { + m_interval->setNeedRecompute(true); StackViewController * stack = stackController(); stack->pop(); if (stack->depth() > 1) { From 43bceac2c9fd974035fa5780da7406adb1fc9559 Mon Sep 17 00:00:00 2001 From: Joachim LF Date: Fri, 1 May 2020 17:35:09 +0200 Subject: [PATCH 29/85] [Interval] Coding style on compute --- apps/shared/interval.cpp | 4 ---- apps/shared/interval.h | 4 ++-- apps/shared/interval_parameter_controller.cpp | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/shared/interval.cpp b/apps/shared/interval.cpp index d39270635..4754feb3d 100644 --- a/apps/shared/interval.cpp +++ b/apps/shared/interval.cpp @@ -53,10 +53,6 @@ void Interval::setElement(int i, double f) { } } -void Interval::setNeedRecompute(bool needrecompute) { - m_needCompute = needrecompute; -} - void Interval::reset() { m_start = 0.0; m_end = 10.0; diff --git a/apps/shared/interval.h b/apps/shared/interval.h index bb2151c40..3022c99f6 100644 --- a/apps/shared/interval.h +++ b/apps/shared/interval.h @@ -18,8 +18,8 @@ public: void setEnd(double f); void setStep(double f); void setElement(int i, double f); - bool NeedRecompute() const { return m_needCompute; } - void setNeedRecompute(bool needrecompute); + bool needCompute() const { return m_needCompute; } + void setNeedCompute(bool compute){ m_needCompute = compute;} void reset(); void clear(); // TODO: decide the max number of elements after optimization diff --git a/apps/shared/interval_parameter_controller.cpp b/apps/shared/interval_parameter_controller.cpp index c5255e386..3c928f786 100644 --- a/apps/shared/interval_parameter_controller.cpp +++ b/apps/shared/interval_parameter_controller.cpp @@ -85,7 +85,7 @@ int IntervalParameterController::reusableParameterCellCount(int type) { } void IntervalParameterController::buttonAction() { - m_interval->setNeedRecompute(true); + m_interval->setNeedCompute(true); StackViewController * stack = stackController(); stack->pop(); if (stack->depth() > 1) { From 9e8229e7678414bee397b7d006ae15c6542cf79e Mon Sep 17 00:00:00 2001 From: Joachim Le Fournis <43498612+RedGl0w@users.noreply.github.com> Date: Mon, 4 May 2020 09:28:10 +0200 Subject: [PATCH 30/85] [Interval] Removed useless getter See https://github.com/numworks/epsilon/pull/1538#discussion_r419252679 --- apps/shared/interval.h | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/shared/interval.h b/apps/shared/interval.h index 3022c99f6..57ced33e8 100644 --- a/apps/shared/interval.h +++ b/apps/shared/interval.h @@ -18,7 +18,6 @@ public: void setEnd(double f); void setStep(double f); void setElement(int i, double f); - bool needCompute() const { return m_needCompute; } void setNeedCompute(bool compute){ m_needCompute = compute;} void reset(); void clear(); From 480459ab19a643ab57f6c4790971e9950d9549b4 Mon Sep 17 00:00:00 2001 From: Joachim Le Fournis <43498612+RedGl0w@users.noreply.github.com> Date: Mon, 4 May 2020 09:33:43 +0200 Subject: [PATCH 31/85] Changed setRecompute() to forceRecompute() See https://github.com/numworks/epsilon/pull/1538#discussion_r419255425 --- apps/shared/interval.h | 2 +- apps/shared/interval_parameter_controller.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/shared/interval.h b/apps/shared/interval.h index 57ced33e8..3be28dc93 100644 --- a/apps/shared/interval.h +++ b/apps/shared/interval.h @@ -18,7 +18,7 @@ public: void setEnd(double f); void setStep(double f); void setElement(int i, double f); - void setNeedCompute(bool compute){ m_needCompute = compute;} + void forceRecompute(){ m_needCompute = true;} void reset(); void clear(); // TODO: decide the max number of elements after optimization diff --git a/apps/shared/interval_parameter_controller.cpp b/apps/shared/interval_parameter_controller.cpp index 3c928f786..cc8a41bca 100644 --- a/apps/shared/interval_parameter_controller.cpp +++ b/apps/shared/interval_parameter_controller.cpp @@ -85,7 +85,7 @@ int IntervalParameterController::reusableParameterCellCount(int type) { } void IntervalParameterController::buttonAction() { - m_interval->setNeedCompute(true); + m_interval->forceRecompute(); StackViewController * stack = stackController(); stack->pop(); if (stack->depth() > 1) { From bbb8308a6dd0b04ea717dc5e6a01322ab5ac6926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 5 May 2020 15:30:32 +0200 Subject: [PATCH 32/85] [poincare/normal_distribution] More values are computed We limited the precision too much before. --- poincare/include/poincare/normal_distribution.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poincare/include/poincare/normal_distribution.h b/poincare/include/poincare/normal_distribution.h index 4806a1467..fe30513f8 100644 --- a/poincare/include/poincare/normal_distribution.h +++ b/poincare/include/poincare/normal_distribution.h @@ -16,11 +16,11 @@ public: * The result of the verification is *result. */ static bool ExpressionMuAndVarAreOK(bool * result, const Expression & mu, const Expression & sigma, Context * context); private: - /* For the standard normal distribution, P(X < y) > 0.9999995 for y >= 4.892 so the + /* For the standard normal distribution, P(X < y) > 0.99999995 for y >= 5.33 so the * value displayed is 1. But this is dependent on the fact that we display * only 7 decimal values! */ static_assert(Preferences::LargeNumberOfSignificantDigits == 7, "k_boundStandardNormalDistribution is ill-defined compared to LargeNumberOfSignificantDigits"); - constexpr static double k_boundStandardNormalDistribution = 4.892; + constexpr static double k_boundStandardNormalDistribution = 5.33; template static T StandardNormalCumulativeDistributiveFunctionAtAbscissa(T abscissa); template static T StandardNormalCumulativeDistributiveInverseForProbability(T probability); }; From efe5b4d6f89e99a4dfcc5daeb6bf499ed4c1ec43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 7 May 2020 14:11:17 +0200 Subject: [PATCH 33/85] [ion/storage] Private constructor and cleaner sharedStorage() --- ion/include/ion/storage.h | 4 ++-- ion/src/shared/storage.cpp | 35 +++++++++++++++++------------------ 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/ion/include/ion/storage.h b/ion/include/ion/storage.h index 851222b53..e18607612 100644 --- a/ion/include/ion/storage.h +++ b/ion/include/ion/storage.h @@ -86,8 +86,6 @@ public: uint32_t m_fullNameCRC32; }; - Storage(); - #if ION_STORAGE_LOG void log(); #endif @@ -126,6 +124,8 @@ private: constexpr static uint32_t Magic = 0xEE0BDDBA; constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8); + Storage(); + /* Getters/Setters on recordID */ const char * fullNameOfRecord(const Record record); Record::ErrorStatus setFullNameOfRecord(const Record record, const char * fullName); diff --git a/ion/src/shared/storage.cpp b/ion/src/shared/storage.cpp index 75fd7d747..95a9fd29d 100644 --- a/ion/src/shared/storage.cpp +++ b/ion/src/shared/storage.cpp @@ -28,10 +28,7 @@ constexpr char Storage::seqExtension[]; constexpr char Storage::eqExtension[]; Storage * Storage::sharedStorage() { - static Storage * storage = nullptr; - if (storage == nullptr) { - storage = new (staticStorageArea) Storage(); - } + static Storage * storage = new (staticStorageArea) Storage(); return storage; } @@ -90,20 +87,6 @@ Storage::Record::Record(const char * basename, int basenameLength, const char * // STORAGE -Storage::Storage() : - m_magicHeader(Magic), - m_buffer(), - m_magicFooter(Magic), - m_delegate(nullptr), - m_lastRecordRetrieved(nullptr), - m_lastRecordRetrievedPointer(nullptr) -{ - assert(m_magicHeader == Magic); - assert(m_magicFooter == Magic); - // Set the size of the first record to 0 - overrideSizeAtPosition(m_buffer, 0); -} - #if ION_STORAGE_LOG void Storage::log() { for (char * p : *this) { @@ -305,6 +288,22 @@ void Storage::destroyRecordsWithExtension(const char * extension) { } } +// PRIVATE + +Storage::Storage() : + m_magicHeader(Magic), + m_buffer(), + m_magicFooter(Magic), + m_delegate(nullptr), + m_lastRecordRetrieved(nullptr), + m_lastRecordRetrievedPointer(nullptr) +{ + assert(m_magicHeader == Magic); + assert(m_magicFooter == Magic); + // Set the size of the first record to 0 + overrideSizeAtPosition(m_buffer, 0); +} + const char * Storage::fullNameOfRecord(const Record record) { char * p = pointerOfRecord(record); if (p != nullptr) { From fc4b0a8d35bbc3695c5ef8ff04501709a7ba7782 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Tue, 7 Apr 2020 22:29:19 -0400 Subject: [PATCH 34/85] [escher] Add an insertionField in LayoutField --- escher/include/escher/layout_field.h | 9 +++++++++ escher/src/layout_field.cpp | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 663458c20..2db62c47b 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -60,6 +60,7 @@ private: void scrollRightOfLayout(Poincare::Layout layoutR); void scrollToBaselinedRect(KDRect rect, KDCoordinate baseline); void insertLayoutAtCursor(Poincare::Layout layoutR, Poincare::Expression correspondingExpression, bool forceCursorRightOfLayout = false); + bool eventShouldUpdateInsertionCursor(Ion::Events::Event event) { return event == Ion::Events::Up; } class ContentView : public View { public: @@ -83,14 +84,22 @@ private: void copySelection(Poincare::Context * context); bool selectionIsEmpty() const; void deleteSelection(); + void invalidateInsertionCursor() { m_insertionCursor = Poincare::LayoutCursor(); } + void updateInsertionCursor() { + if (!m_insertionCursor.isDefined()) { + m_insertionCursor = m_cursor; + } + } private: int numberOfSubviews() const override { return 2; } View * subviewAtIndex(int index) override; void layoutSubviews(bool force = false) override; void layoutCursorSubview(bool force); + void useInsertionCursor(); KDRect selectionRect() const; Poincare::LayoutCursor m_cursor; + Poincare::LayoutCursor m_insertionCursor; ExpressionView m_expressionView; TextCursorView m_cursorView; /* The selection starts on the left of m_selectionStart, and ends on the diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index 4002038c3..2e9094fe4 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -11,6 +11,7 @@ using namespace Poincare; LayoutField::ContentView::ContentView() : m_cursor(), + m_insertionCursor(), m_expressionView(0.0f, 0.5f, KDColorBlack, KDColorWhite, &m_selectionStart, &m_selectionEnd), m_cursorView(), m_selectionStart(), @@ -30,12 +31,21 @@ bool LayoutField::ContentView::setEditing(bool isEditing) { m_expressionView.layout().invalidAllSizesPositionsAndBaselines(); return true; } + } else { + // We're leaving the edition of the current layout + useInsertionCursor(); } layoutSubviews(); markRectAsDirty(bounds()); return false; } +void LayoutField::ContentView::useInsertionCursor() { + if (m_insertionCursor.isDefined()) { + m_cursor = m_insertionCursor; + } +} + void LayoutField::ContentView::clearLayout() { HorizontalLayout h = HorizontalLayout::Builder(); if (m_expressionView.setLayout(h)) { @@ -318,6 +328,12 @@ bool LayoutField::handleEventWithText(const char * text, bool indentation, bool * - the text added after a toolbox selection * - the result of a copy-paste. */ + /* This routing can be called even if no actual underlying event has been + * dispatched on the LayoutField. For instance, when someone wants to insert + * text in the field from the outside. In this scenario, let's make sure the + * insertionCursor is invalidated. */ + m_contentView.invalidateInsertionCursor(); + // Delete the selected layouts if needed deleteSelection(); @@ -382,6 +398,9 @@ bool LayoutField::handleEvent(Ion::Events::Event event) { KDSize previousSize = minimalSizeForOptimalDisplay(); bool shouldRecomputeLayout = m_contentView.cursor()->showEmptyLayoutIfNeeded(); bool moveEventChangedLayout = false; + if (!eventShouldUpdateInsertionCursor(event)) { + m_contentView.invalidateInsertionCursor(); + } if (privateHandleMoveEvent(event, &moveEventChangedLayout)) { if (!isEditing()) { setEditing(true); @@ -554,6 +573,9 @@ bool LayoutField::privateHandleMoveEvent(Ion::Events::Event event, bool * should LayoutCursor result; result = m_contentView.cursor()->cursorAtDirection(DirectionForMoveEvent(event), shouldRecomputeLayout); if (result.isDefined()) { + if (eventShouldUpdateInsertionCursor(event)) { + m_contentView.updateInsertionCursor(); + } m_contentView.setCursor(result); return true; } From 213d80002c524439ef9931c5efd39fc375a08d39 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 8 Apr 2020 21:31:32 -0400 Subject: [PATCH 35/85] [escher/layout_field] Insertion event can be customized --- escher/include/escher/layout_field.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 2db62c47b..7642d8b2a 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -19,6 +19,7 @@ public: ScrollableView(parentResponder, &m_contentView, this), EditableField(inputEventHandlerDelegate), m_contentView(), + m_insertionCursorEvent(Ion::Events::None), m_delegate(delegate) {} void setDelegates(InputEventHandlerDelegate * inputEventHandlerDelegate, LayoutFieldDelegate * delegate) { m_inputEventHandlerDelegate = inputEventHandlerDelegate; m_delegate = delegate; } @@ -33,6 +34,7 @@ public: Poincare::Layout layout() const { return m_contentView.expressionView()->layout(); } CodePoint XNTCodePoint(CodePoint defaultXNTCodePoint) override; void putCursorRightOfLayout(); + void setInsertionCursorEvent(Ion::Events::Event event) { m_insertionCursorEvent = event; } // ScrollableView void setBackgroundColor(KDColor c) override { @@ -60,7 +62,7 @@ private: void scrollRightOfLayout(Poincare::Layout layoutR); void scrollToBaselinedRect(KDRect rect, KDCoordinate baseline); void insertLayoutAtCursor(Poincare::Layout layoutR, Poincare::Expression correspondingExpression, bool forceCursorRightOfLayout = false); - bool eventShouldUpdateInsertionCursor(Ion::Events::Event event) { return event == Ion::Events::Up; } + bool eventShouldUpdateInsertionCursor(Ion::Events::Event event) { return event == m_insertionCursorEvent; } class ContentView : public View { public: @@ -109,6 +111,7 @@ private: bool m_isEditing; }; ContentView m_contentView; + Ion::Events::Event m_insertionCursorEvent; LayoutFieldDelegate * m_delegate; }; From 884e53f75d775f5074a0de85becb3653069dd3d8 Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 8 Apr 2020 21:31:49 -0400 Subject: [PATCH 36/85] [escher/layout_field] Add a comment --- escher/include/escher/layout_field.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 7642d8b2a..436298b18 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -101,6 +101,14 @@ private: void useInsertionCursor(); KDRect selectionRect() const; Poincare::LayoutCursor m_cursor; + /* The insertion cursor is a secondary cursor that determines where text + * should be inserted. Most of the time this cursor is useless (and is + * therefore disabled), but in an interface where the user can navigate out + * of the field, it's important to keep track of where inserted text should + * go even if the main cursor was moved. + * For instance, this is useful in the Calculation app when the user wants + * to type a division and scroll up the history to insert something at the + * denominator. */ Poincare::LayoutCursor m_insertionCursor; ExpressionView m_expressionView; TextCursorView m_cursorView; From a07ab8015e99bde5e60b771cf8965b74a353596f Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 8 Apr 2020 21:34:53 -0400 Subject: [PATCH 37/85] [apps/calculation] Correct insert in fractions --- apps/calculation/expression_field.h | 5 ++++- escher/include/escher/expression_field.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/calculation/expression_field.h b/apps/calculation/expression_field.h index a7fe0e20e..284404628 100644 --- a/apps/calculation/expression_field.h +++ b/apps/calculation/expression_field.h @@ -7,7 +7,10 @@ namespace Calculation { class ExpressionField : public ::ExpressionField { public: - using ::ExpressionField::ExpressionField; + ExpressionField(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandler, TextFieldDelegate * textFieldDelegate, LayoutFieldDelegate * layoutFieldDelegate) : + ::ExpressionField(parentResponder, inputEventHandler, textFieldDelegate, layoutFieldDelegate) { + setLayoutInsertionCursorEvent(Ion::Events::Up); + } protected: bool handleEvent(Ion::Events::Event event) override; }; diff --git a/escher/include/escher/expression_field.h b/escher/include/escher/expression_field.h index bdbec1afa..a79da0bdb 100644 --- a/escher/include/escher/expression_field.h +++ b/escher/include/escher/expression_field.h @@ -25,6 +25,7 @@ public: bool isEmpty() const; bool inputViewHeightDidChange(); bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false); + void setLayoutInsertionCursorEvent(Ion::Events::Event event) { m_layoutField.setInsertionCursorEvent(event); } /* View */ int numberOfSubviews() const override { return 1; } From c955f454575430647a5fb86967650e1d1e403ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 5 May 2020 10:51:41 +0200 Subject: [PATCH 38/85] [escher] View: remove useless virtual destructor --- escher/include/escher/view.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/escher/include/escher/view.h b/escher/include/escher/view.h index e0cd46d34..94d84c3f3 100644 --- a/escher/include/escher/view.h +++ b/escher/include/escher/view.h @@ -31,18 +31,11 @@ class View { friend class Shared::RoundCursorView; public: View() : m_frame(KDRectZero), m_superview(nullptr), m_dirtyRect(KDRectZero) {} - virtual ~View() { - for (int i = 0; i < numberOfSubviews(); i++) { - View * subview = subviewAtIndex(i); - if (subview != nullptr) { - subview->m_superview = nullptr; - } - } - } View(View&& other) = default; View(const View& other) = delete; View& operator=(const View& other) = delete; View& operator=(View&& other) = delete; + void resetSuperview() { m_superview = nullptr; } @@ -90,6 +83,12 @@ private: KDPoint absoluteOrigin() const; KDRect absoluteVisibleFrame() const; + /* At destruction, subviews aren't notified that their own pointer + * 'm_superview' is outdated. This is not an issue since all view hierarchy + * is created or destroyed at once: when the app is packed or unpacked. The + * view and its subviews are then destroyed concomitantly. + * Otherwise, we would just have to implement the destructor to notify + * subviews that 'm_superview = nullptr'. */ View * m_superview; KDRect m_dirtyRect; }; From 833f6ec778672326dacbf14ca5c99cfba414424a Mon Sep 17 00:00:00 2001 From: Joachim LF Date: Wed, 29 Apr 2020 07:29:38 +0200 Subject: [PATCH 39/85] [Regression] Add affine regression --- apps/regression/Makefile | 1 + apps/regression/base.de.i18n | 1 + apps/regression/base.en.i18n | 1 + apps/regression/base.es.i18n | 1 + apps/regression/base.fr.i18n | 1 + apps/regression/base.pt.i18n | 1 + apps/regression/base.universal.i18n | 1 + apps/regression/graph_controller.cpp | 9 ++-- apps/regression/model/affine_model.cpp | 51 +++++++++++++++++++++++ apps/regression/model/affine_model.h | 24 +++++++++++ apps/regression/model/model.h | 19 +++++---- apps/regression/regression_controller.cpp | 2 +- apps/regression/regression_controller.h | 2 +- apps/regression/store.cpp | 4 +- apps/regression/store.h | 2 + 15 files changed, 103 insertions(+), 17 deletions(-) create mode 100644 apps/regression/model/affine_model.cpp create mode 100644 apps/regression/model/affine_model.h diff --git a/apps/regression/Makefile b/apps/regression/Makefile index 2fd71b5a0..b23db2450 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -8,6 +8,7 @@ app_regression_test_src += $(addprefix apps/regression/,\ ) app_regression_test_src += $(addprefix apps/regression/model/,\ + affine_model.cpp \ cubic_model.cpp \ exponential_model.cpp \ linear_model.cpp \ diff --git a/apps/regression/base.de.i18n b/apps/regression/base.de.i18n index 2e06f3bba..25ea97908 100644 --- a/apps/regression/base.de.i18n +++ b/apps/regression/base.de.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Wert in diesem Fenster nicht erreicht" NumberOfDots = "Punktanzahl" Covariance = "Kovarianz" Linear = "Lineare" +Affine = "Affine" Quadratic = "Quadratische" Cubic = "Kubische" Quartic = "Biquadratische" diff --git a/apps/regression/base.en.i18n b/apps/regression/base.en.i18n index 891c30eb3..58cac5578 100644 --- a/apps/regression/base.en.i18n +++ b/apps/regression/base.en.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Value not reached in this window" NumberOfDots = "Number of points" Covariance = "Covariance" Linear = "Linear" +Affine = "Affine" Quadratic = "Quadratic" Cubic = "Cubic" Quartic = "Quartic" diff --git a/apps/regression/base.es.i18n b/apps/regression/base.es.i18n index 15b0fff73..d1c5ec86b 100644 --- a/apps/regression/base.es.i18n +++ b/apps/regression/base.es.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valor no alcanzado en esta ventana" NumberOfDots = "Número de puntos" Covariance = "Covarianza" Linear = "Lineal" +Affine = "Affine" Quadratic = "Cuadrática" Cubic = "Cúbica" Quartic = "Cuártica" diff --git a/apps/regression/base.fr.i18n b/apps/regression/base.fr.i18n index 0d24b1bd8..be93f4aa8 100644 --- a/apps/regression/base.fr.i18n +++ b/apps/regression/base.fr.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valeur non atteinte dans cette fenêtre" NumberOfDots = "Nombre de points" Covariance = "Covariance" Linear = "Linéaire" +Affine = "Affine" Quadratic = "Quadratique" Cubic = "Cubique" Quartic = "Quartique" diff --git a/apps/regression/base.pt.i18n b/apps/regression/base.pt.i18n index 7ec8ab7b5..9590cb914 100644 --- a/apps/regression/base.pt.i18n +++ b/apps/regression/base.pt.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valor não alcançado nesta janela" NumberOfDots = "Número de pontos" Covariance = "Covariancia" Linear = "Linear" +Affine = "Affine" Quadratic = "Quadrática" Cubic = "Cúbica" Quartic = "Quarto grau" diff --git a/apps/regression/base.universal.i18n b/apps/regression/base.universal.i18n index bb896dc36..a1bf08724 100644 --- a/apps/regression/base.universal.i18n +++ b/apps/regression/base.universal.i18n @@ -1,3 +1,4 @@ +AffineRegressionFormula = " y=a·x " QuadraticRegressionFormula = " y=a·x^2+b·x+c " CubicRegressionFormula = " y=a·x^3+b·x^2+c·x+d " QuarticRegressionFormula = " y=a·x^4+b·x^3+c·x^2+d·x+e " diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index f582d315e..d2e6cdef2 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -187,14 +187,15 @@ void GraphController::reloadBannerView() { coefficientName++; } - if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear) { + if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear || m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Affine) { + int index = model->numberOfCoefficients(); // Set "r=..." numberOfChar = 0; legend = " r="; double r = m_store->correlationCoefficient(*m_selectedSeriesIndex); numberOfChar += strlcpy(buffer, legend, bufferSize); numberOfChar += PoincareHelpers::ConvertFloatToText(r, buffer + numberOfChar, bufferSize - numberOfChar, Preferences::LargeNumberOfSignificantDigits); - m_bannerView.subTextAtIndex(2)->setText(buffer); + m_bannerView.subTextAtIndex(0+index)->setText(buffer); // Set "r2=..." numberOfChar = 0; @@ -202,11 +203,11 @@ void GraphController::reloadBannerView() { double r2 = m_store->squaredCorrelationCoefficient(*m_selectedSeriesIndex); numberOfChar += strlcpy(buffer, legend, bufferSize); numberOfChar += PoincareHelpers::ConvertFloatToText(r2, buffer + numberOfChar, bufferSize - numberOfChar, Preferences::LargeNumberOfSignificantDigits); - m_bannerView.subTextAtIndex(3)->setText(buffer); + m_bannerView.subTextAtIndex(1+index)->setText(buffer); // Clean the last subview buffer[0] = 0; - m_bannerView.subTextAtIndex(4)->setText(buffer); + m_bannerView.subTextAtIndex(2+index)->setText(buffer); } else { // Empty all non used subviews diff --git a/apps/regression/model/affine_model.cpp b/apps/regression/model/affine_model.cpp new file mode 100644 index 000000000..0786bafe5 --- /dev/null +++ b/apps/regression/model/affine_model.cpp @@ -0,0 +1,51 @@ +#include "affine_model.h" +#include "../store.h" +#include +#include +#include + +using namespace Poincare; + +namespace Regression { + +Layout AffineModel::layout() { + if (m_layout.isUninitialized()) { + const char * s = "a·X"; + m_layout = LayoutHelper::String(s, strlen(s), k_layoutFont); + } + return m_layout; +} + +double AffineModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + return a*x; +} + +double AffineModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + if (a == 0) { + return NAN; + } + return y-b; +} + +void AffineModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { + modelCoefficients[0] = store->slope(series); + modelCoefficients[1] = store->yIntercept(series); +} + +double AffineModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + if (derivateCoefficientIndex == 0) { + // Derivate: x + return x; + } + if (derivateCoefficientIndex == 1) { + // Derivate: 1; + return 1; + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/affine_model.h b/apps/regression/model/affine_model.h new file mode 100644 index 000000000..42dbf7a7e --- /dev/null +++ b/apps/regression/model/affine_model.h @@ -0,0 +1,24 @@ +#ifndef REGRESSION_AFFINE_MODEL_H +#define REGRESSION_AFFINE_MODEL_H + +#include "model.h" + +namespace Regression { + +class AffineModel : public Model { +public: + using Model::Model; + Poincare::Layout layout() override; + I18n::Message formulaMessage() const override { return I18n::Message::AffineRegressionFormula; } + double evaluate(double * modelCoefficients, double x) const override; + double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override; + void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + int numberOfCoefficients() const override { return 1; } + int bannerLinesCount() const override { return 2; } +}; + +} + + +#endif diff --git a/apps/regression/model/model.h b/apps/regression/model/model.h index c018a3528..e38cebb01 100644 --- a/apps/regression/model/model.h +++ b/apps/regression/model/model.h @@ -15,16 +15,17 @@ class Model { public: enum class Type : uint8_t { Linear = 0, - Quadratic = 1, - Cubic = 2, - Quartic = 3, - Logarithmic = 4, - Exponential = 5, - Power = 6, - Trigonometric = 7, - Logistic = 8 + Affine = 1, + Quadratic = 2, + Cubic = 3, + Quartic = 4, + Logarithmic = 5, + Exponential = 6, + Power = 7, + Trigonometric = 8, + Logistic = 9 }; - static constexpr int k_numberOfModels = 9; + static constexpr int k_numberOfModels = 10; static constexpr int k_maxNumberOfCoefficients = 5; // This has to verify: k_maxNumberOfCoefficients < Matrix::k_maxNumberOfCoefficients virtual ~Model() = default; virtual Poincare::Layout layout() = 0; diff --git a/apps/regression/regression_controller.cpp b/apps/regression/regression_controller.cpp index 9cc70181f..fb348cd56 100644 --- a/apps/regression/regression_controller.cpp +++ b/apps/regression/regression_controller.cpp @@ -82,7 +82,7 @@ HighlightCell * RegressionController::reusableCell(int index, int type) { void RegressionController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { assert(i == 0); assert(j >= 0 && j < k_numberOfRows); - I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic}; + I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Affine, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic}; MessageTableCellWithExpression * castedCell = static_cast(cell); castedCell->setMessage(messages[j]); castedCell->setLayout(m_store->regressionModel((Model::Type) j)->layout()); diff --git a/apps/regression/regression_controller.h b/apps/regression/regression_controller.h index f0875cc28..bb32f5dca 100644 --- a/apps/regression/regression_controller.h +++ b/apps/regression/regression_controller.h @@ -31,7 +31,7 @@ public: int numberOfRows() const override { return k_numberOfRows; } void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: - constexpr static int k_numberOfRows = 9; + constexpr static int k_numberOfRows = 10; constexpr static int k_numberOfCells = 6; // (240 - 70) / 35 MessageTableCellWithExpression m_regressionCells[k_numberOfCells]; SelectableTableView m_selectableTableView; diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index e7d827cb2..7a98b4f96 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -11,7 +11,7 @@ using namespace Shared; namespace Regression { -static_assert(Model::k_numberOfModels == 9, "Number of models changed, Regression::Store() needs to adapt"); +static_assert(Model::k_numberOfModels == 10, "Number of models changed, Regression::Store() needs to adapt"); static_assert(Store::k_numberOfSeries == 3, "Number of series changed, Regression::Store() needs to adapt (m_seriesChecksum)"); Store::Store() : @@ -285,7 +285,7 @@ double Store::squaredCorrelationCoefficient(int series) const { } Model * Store::regressionModel(int index) { - Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel}; + Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_affineModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel}; return models[index]; } diff --git a/apps/regression/store.h b/apps/regression/store.h index 2cc6f600f..bb97b2774 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -11,6 +11,7 @@ #include "model/quadratic_model.h" #include "model/quartic_model.h" #include "model/trigonometric_model.h" +#include "model/affine_model.h" #include "../shared/interactive_curve_view_range.h" #include "../shared/double_pair_store.h" #include @@ -79,6 +80,7 @@ private: uint32_t m_seriesChecksum[k_numberOfSeries]; Model::Type m_regressionTypes[k_numberOfSeries]; LinearModel m_linearModel; + AffineModel m_affineModel; QuadraticModel m_quadraticModel; CubicModel m_cubicModel; QuarticModel m_quarticModel; From 5297f28ffabdcd425796c175ceb63738efbc8eae Mon Sep 17 00:00:00 2001 From: Joachim LF Date: Wed, 29 Apr 2020 08:44:18 +0200 Subject: [PATCH 40/85] [Regression] Switched affine and linear --- apps/regression/model/affine_model.cpp | 9 +++++---- apps/regression/model/affine_model.h | 8 ++++---- apps/regression/model/linear_model.cpp | 9 ++++----- apps/regression/model/linear_model.h | 8 ++++---- apps/regression/store.cpp | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/regression/model/affine_model.cpp b/apps/regression/model/affine_model.cpp index 0786bafe5..fc18ac3aa 100644 --- a/apps/regression/model/affine_model.cpp +++ b/apps/regression/model/affine_model.cpp @@ -1,4 +1,4 @@ -#include "affine_model.h" +#include "linear_model.h" #include "../store.h" #include #include @@ -10,7 +10,7 @@ namespace Regression { Layout AffineModel::layout() { if (m_layout.isUninitialized()) { - const char * s = "a·X"; + const char * s = "a·X+b"; m_layout = LayoutHelper::String(s, strlen(s), k_layoutFont); } return m_layout; @@ -18,7 +18,8 @@ Layout AffineModel::layout() { double AffineModel::evaluate(double * modelCoefficients, double x) const { double a = modelCoefficients[0]; - return a*x; + double b = modelCoefficients[1]; + return a*x+b; } double AffineModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { @@ -27,7 +28,7 @@ double AffineModel::levelSet(double * modelCoefficients, double xMin, double ste if (a == 0) { return NAN; } - return y-b; + return (y-b)/a; } void AffineModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { diff --git a/apps/regression/model/affine_model.h b/apps/regression/model/affine_model.h index 42dbf7a7e..b2c0a7cd0 100644 --- a/apps/regression/model/affine_model.h +++ b/apps/regression/model/affine_model.h @@ -1,5 +1,5 @@ -#ifndef REGRESSION_AFFINE_MODEL_H -#define REGRESSION_AFFINE_MODEL_H +#ifndef REGRESSION_LINEAR_MODEL_H +#define REGRESSION_LINEAR_MODEL_H #include "model.h" @@ -14,8 +14,8 @@ public: double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override; void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override; double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; - int numberOfCoefficients() const override { return 1; } - int bannerLinesCount() const override { return 2; } + int numberOfCoefficients() const override { return 2; } + int bannerLinesCount() const override { return 3; } }; } diff --git a/apps/regression/model/linear_model.cpp b/apps/regression/model/linear_model.cpp index 3de85628f..824500e09 100644 --- a/apps/regression/model/linear_model.cpp +++ b/apps/regression/model/linear_model.cpp @@ -1,4 +1,4 @@ -#include "linear_model.h" +#include "affine_model.h" #include "../store.h" #include #include @@ -10,7 +10,7 @@ namespace Regression { Layout LinearModel::layout() { if (m_layout.isUninitialized()) { - const char * s = "a·X+b"; + const char * s = "a·X"; m_layout = LayoutHelper::String(s, strlen(s), k_layoutFont); } return m_layout; @@ -18,8 +18,7 @@ Layout LinearModel::layout() { double LinearModel::evaluate(double * modelCoefficients, double x) const { double a = modelCoefficients[0]; - double b = modelCoefficients[1]; - return a*x+b; + return a*x; } double LinearModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { @@ -28,7 +27,7 @@ double LinearModel::levelSet(double * modelCoefficients, double xMin, double ste if (a == 0) { return NAN; } - return (y-b)/a; + return y-b; } void LinearModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { diff --git a/apps/regression/model/linear_model.h b/apps/regression/model/linear_model.h index 4fd9d2988..4fb6fd886 100644 --- a/apps/regression/model/linear_model.h +++ b/apps/regression/model/linear_model.h @@ -1,5 +1,5 @@ -#ifndef REGRESSION_LINEAR_MODEL_H -#define REGRESSION_LINEAR_MODEL_H +#ifndef REGRESSION_AFFINE_MODEL_H +#define REGRESSION_AFFINE_MODEL_H #include "model.h" @@ -14,8 +14,8 @@ public: double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override; void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override; double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; - int numberOfCoefficients() const override { return 2; } - int bannerLinesCount() const override { return 3; } + int numberOfCoefficients() const override { return 1; } + int bannerLinesCount() const override { return 2; } }; } diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 7a98b4f96..f1ac3dd9a 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -285,7 +285,7 @@ double Store::squaredCorrelationCoefficient(int series) const { } Model * Store::regressionModel(int index) { - Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_affineModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel}; + Model * models[Model::k_numberOfModels] = {&m_affineModel, &m_linearModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel}; return models[index]; } From 740e9e9d120a3134f1ca9beb0d25faf556ec5ffc Mon Sep 17 00:00:00 2001 From: Joachim LF Date: Mon, 4 May 2020 11:22:42 +0200 Subject: [PATCH 41/85] [Regression] Fixed affine and linear name errors --- apps/graph/graph/banner_view.cpp | 2 +- apps/regression/base.universal.i18n | 2 +- apps/regression/model/affine_model.cpp | 2 +- apps/regression/model/affine_model.h | 4 ++-- apps/regression/model/linear_model.cpp | 12 +++--------- apps/regression/model/linear_model.h | 4 ++-- apps/shared.universal.i18n | 2 +- 7 files changed, 11 insertions(+), 17 deletions(-) diff --git a/apps/graph/graph/banner_view.cpp b/apps/graph/graph/banner_view.cpp index fa900c652..7033a1d3c 100644 --- a/apps/graph/graph/banner_view.cpp +++ b/apps/graph/graph/banner_view.cpp @@ -11,7 +11,7 @@ BannerView::BannerView( ) : Shared::XYBannerView(parentResponder, inputEventHandlerDelegate, textFieldDelegate), m_derivativeView(Font(), 0.5f, 0.5f, TextColor(), BackgroundColor()), - m_tangentEquationView(Font(), I18n::Message::LinearRegressionFormula, 0.0f, 0.5f, TextColor(), BackgroundColor()), + m_tangentEquationView(Font(), I18n::Message::AffineRegressionFormula, 0.0f, 0.5f, TextColor(), BackgroundColor()), m_aView(Font(), 0.5f, 0.5f, TextColor(), BackgroundColor()), m_bView(Font(), 0.5f, 0.5f, TextColor(), BackgroundColor()), m_numberOfSubviews(Shared::XYBannerView::k_numberOfSubviews) diff --git a/apps/regression/base.universal.i18n b/apps/regression/base.universal.i18n index a1bf08724..f04c13b35 100644 --- a/apps/regression/base.universal.i18n +++ b/apps/regression/base.universal.i18n @@ -1,4 +1,4 @@ -AffineRegressionFormula = " y=a·x " +LinearRegressionFormula = " y=a·x " QuadraticRegressionFormula = " y=a·x^2+b·x+c " CubicRegressionFormula = " y=a·x^3+b·x^2+c·x+d " QuarticRegressionFormula = " y=a·x^4+b·x^3+c·x^2+d·x+e " diff --git a/apps/regression/model/affine_model.cpp b/apps/regression/model/affine_model.cpp index fc18ac3aa..0356b7991 100644 --- a/apps/regression/model/affine_model.cpp +++ b/apps/regression/model/affine_model.cpp @@ -1,4 +1,4 @@ -#include "linear_model.h" +#include "affine_model.h" #include "../store.h" #include #include diff --git a/apps/regression/model/affine_model.h b/apps/regression/model/affine_model.h index b2c0a7cd0..868664c21 100644 --- a/apps/regression/model/affine_model.h +++ b/apps/regression/model/affine_model.h @@ -1,5 +1,5 @@ -#ifndef REGRESSION_LINEAR_MODEL_H -#define REGRESSION_LINEAR_MODEL_H +#ifndef REGRESSION_AFFINE_MODEL_H +#define REGRESSION_AFFINE_MODEL_H #include "model.h" diff --git a/apps/regression/model/linear_model.cpp b/apps/regression/model/linear_model.cpp index 824500e09..0fc663f45 100644 --- a/apps/regression/model/linear_model.cpp +++ b/apps/regression/model/linear_model.cpp @@ -1,4 +1,4 @@ -#include "affine_model.h" +#include "linear_model.h" #include "../store.h" #include #include @@ -23,16 +23,14 @@ double LinearModel::evaluate(double * modelCoefficients, double x) const { double LinearModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { double a = modelCoefficients[0]; - double b = modelCoefficients[1]; - if (a == 0) { + if (a == 0.0) { return NAN; } - return y-b; + return y/a; } void LinearModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { modelCoefficients[0] = store->slope(series); - modelCoefficients[1] = store->yIntercept(series); } double LinearModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { @@ -40,10 +38,6 @@ double LinearModel::partialDerivate(double * modelCoefficients, int derivateCoef // Derivate: x return x; } - if (derivateCoefficientIndex == 1) { - // Derivate: 1; - return 1; - } assert(false); return 0.0; } diff --git a/apps/regression/model/linear_model.h b/apps/regression/model/linear_model.h index 4fb6fd886..d98dbd756 100644 --- a/apps/regression/model/linear_model.h +++ b/apps/regression/model/linear_model.h @@ -1,5 +1,5 @@ -#ifndef REGRESSION_AFFINE_MODEL_H -#define REGRESSION_AFFINE_MODEL_H +#ifndef REGRESSION_LINEAR_MODEL_H +#define REGRESSION_LINEAR_MODEL_H #include "model.h" diff --git a/apps/shared.universal.i18n b/apps/shared.universal.i18n index 2aa997b88..344244ab2 100644 --- a/apps/shared.universal.i18n +++ b/apps/shared.universal.i18n @@ -119,7 +119,7 @@ InvSortCommandWithArg = "sort>(L)" K = "k" Lambda = "λ" LcmCommandWithArg = "lcm(p,q)" -LinearRegressionFormula = " y=a·x+b " +AffineRegressionFormula = " y=a·x+b " LogCommandWithArg = "log(x,a)" MatrixCommand = "[[\x11]]" MatrixCommandWithArg = "[[1,2][3,4]]" From 2e5e2a32580f697f9f26f7d64270a11f076ff77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 13 May 2020 13:41:05 +0200 Subject: [PATCH 42/85] [apps/regression] Rename Affine -> Linear and Linear -> Proportional --- apps/graph/graph/banner_view.cpp | 2 +- apps/regression/Makefile | 2 +- apps/regression/base.de.i18n | 2 +- apps/regression/base.en.i18n | 2 +- apps/regression/base.es.i18n | 2 +- apps/regression/base.fr.i18n | 2 +- apps/regression/base.pt.i18n | 2 +- apps/regression/base.universal.i18n | 2 +- apps/regression/graph_controller.cpp | 2 +- apps/regression/model/affine_model.cpp | 52 ------------------- apps/regression/model/linear_model.cpp | 15 ++++-- apps/regression/model/linear_model.h | 4 +- apps/regression/model/model.h | 18 +++---- apps/regression/model/proportional_model.cpp | 45 ++++++++++++++++ .../{affine_model.h => proportional_model.h} | 12 ++--- apps/regression/regression_controller.cpp | 2 +- apps/regression/store.cpp | 2 +- apps/regression/store.h | 4 +- apps/shared.universal.i18n | 2 +- 19 files changed, 87 insertions(+), 87 deletions(-) delete mode 100644 apps/regression/model/affine_model.cpp create mode 100644 apps/regression/model/proportional_model.cpp rename apps/regression/model/{affine_model.h => proportional_model.h} (69%) diff --git a/apps/graph/graph/banner_view.cpp b/apps/graph/graph/banner_view.cpp index 7033a1d3c..fa900c652 100644 --- a/apps/graph/graph/banner_view.cpp +++ b/apps/graph/graph/banner_view.cpp @@ -11,7 +11,7 @@ BannerView::BannerView( ) : Shared::XYBannerView(parentResponder, inputEventHandlerDelegate, textFieldDelegate), m_derivativeView(Font(), 0.5f, 0.5f, TextColor(), BackgroundColor()), - m_tangentEquationView(Font(), I18n::Message::AffineRegressionFormula, 0.0f, 0.5f, TextColor(), BackgroundColor()), + m_tangentEquationView(Font(), I18n::Message::LinearRegressionFormula, 0.0f, 0.5f, TextColor(), BackgroundColor()), m_aView(Font(), 0.5f, 0.5f, TextColor(), BackgroundColor()), m_bView(Font(), 0.5f, 0.5f, TextColor(), BackgroundColor()), m_numberOfSubviews(Shared::XYBannerView::k_numberOfSubviews) diff --git a/apps/regression/Makefile b/apps/regression/Makefile index b23db2450..e63a802cb 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -8,7 +8,6 @@ app_regression_test_src += $(addprefix apps/regression/,\ ) app_regression_test_src += $(addprefix apps/regression/model/,\ - affine_model.cpp \ cubic_model.cpp \ exponential_model.cpp \ linear_model.cpp \ @@ -16,6 +15,7 @@ app_regression_test_src += $(addprefix apps/regression/model/,\ logistic_model.cpp \ model.cpp \ power_model.cpp \ + proportional_model.cpp \ quadratic_model.cpp \ quartic_model.cpp \ trigonometric_model.cpp \ diff --git a/apps/regression/base.de.i18n b/apps/regression/base.de.i18n index 25ea97908..b49e91e7c 100644 --- a/apps/regression/base.de.i18n +++ b/apps/regression/base.de.i18n @@ -10,7 +10,7 @@ ValueNotReachedByRegression = "Wert in diesem Fenster nicht erreicht" NumberOfDots = "Punktanzahl" Covariance = "Kovarianz" Linear = "Lineare" -Affine = "Affine" +Proportional = "Proportional" Quadratic = "Quadratische" Cubic = "Kubische" Quartic = "Biquadratische" diff --git a/apps/regression/base.en.i18n b/apps/regression/base.en.i18n index 58cac5578..7b257c653 100644 --- a/apps/regression/base.en.i18n +++ b/apps/regression/base.en.i18n @@ -10,7 +10,7 @@ ValueNotReachedByRegression = "Value not reached in this window" NumberOfDots = "Number of points" Covariance = "Covariance" Linear = "Linear" -Affine = "Affine" +Proportional = "Proportional" Quadratic = "Quadratic" Cubic = "Cubic" Quartic = "Quartic" diff --git a/apps/regression/base.es.i18n b/apps/regression/base.es.i18n index d1c5ec86b..147304a40 100644 --- a/apps/regression/base.es.i18n +++ b/apps/regression/base.es.i18n @@ -10,7 +10,7 @@ ValueNotReachedByRegression = "Valor no alcanzado en esta ventana" NumberOfDots = "Número de puntos" Covariance = "Covarianza" Linear = "Lineal" -Affine = "Affine" +Proportional = "Proporcional" Quadratic = "Cuadrática" Cubic = "Cúbica" Quartic = "Cuártica" diff --git a/apps/regression/base.fr.i18n b/apps/regression/base.fr.i18n index be93f4aa8..c8f80d875 100644 --- a/apps/regression/base.fr.i18n +++ b/apps/regression/base.fr.i18n @@ -10,7 +10,7 @@ ValueNotReachedByRegression = "Valeur non atteinte dans cette fenêtre" NumberOfDots = "Nombre de points" Covariance = "Covariance" Linear = "Linéaire" -Affine = "Affine" +Proportional = "Proportionnelle" Quadratic = "Quadratique" Cubic = "Cubique" Quartic = "Quartique" diff --git a/apps/regression/base.pt.i18n b/apps/regression/base.pt.i18n index 9590cb914..4ff5a3eaf 100644 --- a/apps/regression/base.pt.i18n +++ b/apps/regression/base.pt.i18n @@ -10,7 +10,7 @@ ValueNotReachedByRegression = "Valor não alcançado nesta janela" NumberOfDots = "Número de pontos" Covariance = "Covariancia" Linear = "Linear" -Affine = "Affine" +Proportional = "Proporcional" Quadratic = "Quadrática" Cubic = "Cúbica" Quartic = "Quarto grau" diff --git a/apps/regression/base.universal.i18n b/apps/regression/base.universal.i18n index f04c13b35..a26602af1 100644 --- a/apps/regression/base.universal.i18n +++ b/apps/regression/base.universal.i18n @@ -1,4 +1,4 @@ -LinearRegressionFormula = " y=a·x " +ProportionalRegressionFormula = " y=a·x " QuadraticRegressionFormula = " y=a·x^2+b·x+c " CubicRegressionFormula = " y=a·x^3+b·x^2+c·x+d " QuarticRegressionFormula = " y=a·x^4+b·x^3+c·x^2+d·x+e " diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index d2e6cdef2..9db55a5b1 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -187,7 +187,7 @@ void GraphController::reloadBannerView() { coefficientName++; } - if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear || m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Affine) { + if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear || m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Proportional) { int index = model->numberOfCoefficients(); // Set "r=..." numberOfChar = 0; diff --git a/apps/regression/model/affine_model.cpp b/apps/regression/model/affine_model.cpp deleted file mode 100644 index 0356b7991..000000000 --- a/apps/regression/model/affine_model.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "affine_model.h" -#include "../store.h" -#include -#include -#include - -using namespace Poincare; - -namespace Regression { - -Layout AffineModel::layout() { - if (m_layout.isUninitialized()) { - const char * s = "a·X+b"; - m_layout = LayoutHelper::String(s, strlen(s), k_layoutFont); - } - return m_layout; -} - -double AffineModel::evaluate(double * modelCoefficients, double x) const { - double a = modelCoefficients[0]; - double b = modelCoefficients[1]; - return a*x+b; -} - -double AffineModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { - double a = modelCoefficients[0]; - double b = modelCoefficients[1]; - if (a == 0) { - return NAN; - } - return (y-b)/a; -} - -void AffineModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { - modelCoefficients[0] = store->slope(series); - modelCoefficients[1] = store->yIntercept(series); -} - -double AffineModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { - if (derivateCoefficientIndex == 0) { - // Derivate: x - return x; - } - if (derivateCoefficientIndex == 1) { - // Derivate: 1; - return 1; - } - assert(false); - return 0.0; -} - -} diff --git a/apps/regression/model/linear_model.cpp b/apps/regression/model/linear_model.cpp index 0fc663f45..3de85628f 100644 --- a/apps/regression/model/linear_model.cpp +++ b/apps/regression/model/linear_model.cpp @@ -10,7 +10,7 @@ namespace Regression { Layout LinearModel::layout() { if (m_layout.isUninitialized()) { - const char * s = "a·X"; + const char * s = "a·X+b"; m_layout = LayoutHelper::String(s, strlen(s), k_layoutFont); } return m_layout; @@ -18,19 +18,22 @@ Layout LinearModel::layout() { double LinearModel::evaluate(double * modelCoefficients, double x) const { double a = modelCoefficients[0]; - return a*x; + double b = modelCoefficients[1]; + return a*x+b; } double LinearModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { double a = modelCoefficients[0]; - if (a == 0.0) { + double b = modelCoefficients[1]; + if (a == 0) { return NAN; } - return y/a; + return (y-b)/a; } void LinearModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { modelCoefficients[0] = store->slope(series); + modelCoefficients[1] = store->yIntercept(series); } double LinearModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { @@ -38,6 +41,10 @@ double LinearModel::partialDerivate(double * modelCoefficients, int derivateCoef // Derivate: x return x; } + if (derivateCoefficientIndex == 1) { + // Derivate: 1; + return 1; + } assert(false); return 0.0; } diff --git a/apps/regression/model/linear_model.h b/apps/regression/model/linear_model.h index d98dbd756..4fd9d2988 100644 --- a/apps/regression/model/linear_model.h +++ b/apps/regression/model/linear_model.h @@ -14,8 +14,8 @@ public: double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override; void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override; double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; - int numberOfCoefficients() const override { return 1; } - int bannerLinesCount() const override { return 2; } + int numberOfCoefficients() const override { return 2; } + int bannerLinesCount() const override { return 3; } }; } diff --git a/apps/regression/model/model.h b/apps/regression/model/model.h index e38cebb01..78f14aab6 100644 --- a/apps/regression/model/model.h +++ b/apps/regression/model/model.h @@ -14,16 +14,16 @@ class Store; class Model { public: enum class Type : uint8_t { - Linear = 0, - Affine = 1, - Quadratic = 2, - Cubic = 3, - Quartic = 4, - Logarithmic = 5, - Exponential = 6, - Power = 7, + Linear = 0, + Proportional = 1, + Quadratic = 2, + Cubic = 3, + Quartic = 4, + Logarithmic = 5, + Exponential = 6, + Power = 7, Trigonometric = 8, - Logistic = 9 + Logistic = 9 }; static constexpr int k_numberOfModels = 10; static constexpr int k_maxNumberOfCoefficients = 5; // This has to verify: k_maxNumberOfCoefficients < Matrix::k_maxNumberOfCoefficients diff --git a/apps/regression/model/proportional_model.cpp b/apps/regression/model/proportional_model.cpp new file mode 100644 index 000000000..a6d1bdb1c --- /dev/null +++ b/apps/regression/model/proportional_model.cpp @@ -0,0 +1,45 @@ +#include "proportional_model.h" +#include "../store.h" +#include +#include +#include + +using namespace Poincare; + +namespace Regression { + +Layout ProportionalModel::layout() { + if (m_layout.isUninitialized()) { + const char * s = "a·X"; + m_layout = LayoutHelper::String(s, strlen(s), k_layoutFont); + } + return m_layout; +} + +double ProportionalModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + return a*x; +} + +double ProportionalModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { + double a = modelCoefficients[0]; + if (a == 0.0) { + return NAN; + } + return y/a; +} + +void ProportionalModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { + modelCoefficients[0] = store->slope(series); +} + +double ProportionalModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + if (derivateCoefficientIndex == 0) { + // Derivate: x + return x; + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/affine_model.h b/apps/regression/model/proportional_model.h similarity index 69% rename from apps/regression/model/affine_model.h rename to apps/regression/model/proportional_model.h index 868664c21..b2eca094f 100644 --- a/apps/regression/model/affine_model.h +++ b/apps/regression/model/proportional_model.h @@ -1,21 +1,21 @@ -#ifndef REGRESSION_AFFINE_MODEL_H -#define REGRESSION_AFFINE_MODEL_H +#ifndef REGRESSION_PROPORTIONAL_MODEL_H +#define REGRESSION_PROPORTIONAL_MODEL_H #include "model.h" namespace Regression { -class AffineModel : public Model { +class ProportionalModel : public Model { public: using Model::Model; Poincare::Layout layout() override; - I18n::Message formulaMessage() const override { return I18n::Message::AffineRegressionFormula; } + I18n::Message formulaMessage() const override { return I18n::Message::ProportionalRegressionFormula; } double evaluate(double * modelCoefficients, double x) const override; double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override; void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override; double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; - int numberOfCoefficients() const override { return 2; } - int bannerLinesCount() const override { return 3; } + int numberOfCoefficients() const override { return 1; } + int bannerLinesCount() const override { return 2; } }; } diff --git a/apps/regression/regression_controller.cpp b/apps/regression/regression_controller.cpp index fb348cd56..188007d6b 100644 --- a/apps/regression/regression_controller.cpp +++ b/apps/regression/regression_controller.cpp @@ -82,7 +82,7 @@ HighlightCell * RegressionController::reusableCell(int index, int type) { void RegressionController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { assert(i == 0); assert(j >= 0 && j < k_numberOfRows); - I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Affine, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic}; + I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Proportional, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic}; MessageTableCellWithExpression * castedCell = static_cast(cell); castedCell->setMessage(messages[j]); castedCell->setLayout(m_store->regressionModel((Model::Type) j)->layout()); diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index f1ac3dd9a..cfba25903 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -285,7 +285,7 @@ double Store::squaredCorrelationCoefficient(int series) const { } Model * Store::regressionModel(int index) { - Model * models[Model::k_numberOfModels] = {&m_affineModel, &m_linearModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel}; + Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_proportionalModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel}; return models[index]; } diff --git a/apps/regression/store.h b/apps/regression/store.h index bb97b2774..92acd0f8f 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -8,10 +8,10 @@ #include "model/logarithmic_model.h" #include "model/logistic_model.h" #include "model/power_model.h" +#include "model/proportional_model.h" #include "model/quadratic_model.h" #include "model/quartic_model.h" #include "model/trigonometric_model.h" -#include "model/affine_model.h" #include "../shared/interactive_curve_view_range.h" #include "../shared/double_pair_store.h" #include @@ -80,7 +80,7 @@ private: uint32_t m_seriesChecksum[k_numberOfSeries]; Model::Type m_regressionTypes[k_numberOfSeries]; LinearModel m_linearModel; - AffineModel m_affineModel; + ProportionalModel m_proportionalModel; QuadraticModel m_quadraticModel; CubicModel m_cubicModel; QuarticModel m_quarticModel; diff --git a/apps/shared.universal.i18n b/apps/shared.universal.i18n index 344244ab2..b313ff93c 100644 --- a/apps/shared.universal.i18n +++ b/apps/shared.universal.i18n @@ -119,7 +119,7 @@ InvSortCommandWithArg = "sort>(L)" K = "k" Lambda = "λ" LcmCommandWithArg = "lcm(p,q)" -AffineRegressionFormula = " y=a·x+b " +LinearRegressionFormula = " y=a·x+b " LogCommandWithArg = "log(x,a)" MatrixCommand = "[[\x11]]" MatrixCommandWithArg = "[[1,2][3,4]]" From 588533fadf5e7c7bfe4eff09950ec90d0ac7d28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 13 May 2020 13:45:57 +0200 Subject: [PATCH 43/85] [apps/regression] Remove r and r2 for Proportional regression --- apps/regression/graph_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 9db55a5b1..b207c3ed9 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -187,7 +187,7 @@ void GraphController::reloadBannerView() { coefficientName++; } - if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear || m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Proportional) { + if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear) { int index = model->numberOfCoefficients(); // Set "r=..." numberOfChar = 0; From 980a62da6c94a1e121d5c9ab75d23183e1b83f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 13 May 2020 13:50:25 +0200 Subject: [PATCH 44/85] [apps/regression] Clean proportional_model.cpp --- apps/regression/model/proportional_model.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/regression/model/proportional_model.cpp b/apps/regression/model/proportional_model.cpp index a6d1bdb1c..22f62e5a4 100644 --- a/apps/regression/model/proportional_model.cpp +++ b/apps/regression/model/proportional_model.cpp @@ -1,7 +1,6 @@ #include "proportional_model.h" #include "../store.h" #include -#include #include using namespace Poincare; @@ -17,12 +16,11 @@ Layout ProportionalModel::layout() { } double ProportionalModel::evaluate(double * modelCoefficients, double x) const { - double a = modelCoefficients[0]; - return a*x; + return modelCoefficients[0] * x; } double ProportionalModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { - double a = modelCoefficients[0]; + const double a = modelCoefficients[0]; if (a == 0.0) { return NAN; } @@ -34,12 +32,9 @@ void ProportionalModel::fit(Store * store, int series, double * modelCoefficient } double ProportionalModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { - if (derivateCoefficientIndex == 0) { - // Derivate: x - return x; - } - assert(false); - return 0.0; + assert(derivateCoefficientIndex == 0); + // Derivate: x + return x; } } From 21a4139c19d2a5a5c67a4f5e5a1a10e3636e91ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 13 May 2020 14:22:16 +0200 Subject: [PATCH 45/85] [apps/regression] Clean partialDerivate of the models --- apps/regression/model/cubic_model.cpp | 33 +++++++-------- apps/regression/model/exponential_model.cpp | 15 +++---- apps/regression/model/linear_model.cpp | 11 ++--- apps/regression/model/logarithmic_model.cpp | 13 +++--- apps/regression/model/logistic_model.cpp | 21 +++++----- apps/regression/model/power_model.cpp | 19 ++++----- apps/regression/model/quadratic_model.cpp | 13 +++--- apps/regression/model/quartic_model.cpp | 40 +++++++++---------- apps/regression/model/trigonometric_model.cpp | 27 ++++++------- 9 files changed, 83 insertions(+), 109 deletions(-) diff --git a/apps/regression/model/cubic_model.cpp b/apps/regression/model/cubic_model.cpp index 42fdb5c7d..393e2120e 100644 --- a/apps/regression/model/cubic_model.cpp +++ b/apps/regression/model/cubic_model.cpp @@ -55,24 +55,21 @@ double CubicModel::evaluate(double * modelCoefficients, double x) const { } double CubicModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { - if (derivateCoefficientIndex == 0) { - // Derivate: x^3 - return x*x*x; - } - if (derivateCoefficientIndex == 1) { - // Derivate: x^2 - return x*x; - } - if (derivateCoefficientIndex == 2) { - // Derivate: x - return x; - } - if (derivateCoefficientIndex == 3) { - // Derivate: 1 - return 1; - } - assert(false); - return 0.0; + switch (derivateCoefficientIndex) { + case 0: + // Derivate with respect to a: x^3 + return x*x*x; + case 1: + // Derivate with respect to b: x^2 + return x*x; + case 2: + // Derivate with respect to c: x + return x; + default: + // Derivate with respect to d: 1 + assert(derivateCoefficientIndex == 3); + return 1.0; + }; } Expression CubicModel::expression(double * modelCoefficients) { diff --git a/apps/regression/model/exponential_model.cpp b/apps/regression/model/exponential_model.cpp index bffb66c53..aecdc3cae 100644 --- a/apps/regression/model/exponential_model.cpp +++ b/apps/regression/model/exponential_model.cpp @@ -86,18 +86,15 @@ void ExponentialModel::fit(Store * store, int series, double * modelCoefficients } double ExponentialModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { - double a = modelCoefficients[0]; - double b = modelCoefficients[1]; + const double b = modelCoefficients[1]; if (derivateCoefficientIndex == 0) { - // Derivate: exp(b*x) + // Derivate with respect to a: exp(b*x) return exp(b*x); } - if (derivateCoefficientIndex == 1) { - // Derivate: a*x*exp(b*x) - return a*x*exp(b*x); - } - assert(false); - return 0.0; + assert(derivateCoefficientIndex == 1); + // Derivate with respect to b: a*x*exp(b*x) + double a = modelCoefficients[0]; + return a*x*exp(b*x); } } diff --git a/apps/regression/model/linear_model.cpp b/apps/regression/model/linear_model.cpp index 3de85628f..afe159ea2 100644 --- a/apps/regression/model/linear_model.cpp +++ b/apps/regression/model/linear_model.cpp @@ -38,15 +38,12 @@ void LinearModel::fit(Store * store, int series, double * modelCoefficients, Poi double LinearModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { if (derivateCoefficientIndex == 0) { - // Derivate: x + // Derivate with respect to a: x return x; } - if (derivateCoefficientIndex == 1) { - // Derivate: 1; - return 1; - } - assert(false); - return 0.0; + assert(derivateCoefficientIndex == 1); + // Derivate with respect to b: 1 + return 1.0; } } diff --git a/apps/regression/model/logarithmic_model.cpp b/apps/regression/model/logarithmic_model.cpp index f5c23a105..7beeb119c 100644 --- a/apps/regression/model/logarithmic_model.cpp +++ b/apps/regression/model/logarithmic_model.cpp @@ -33,16 +33,13 @@ double LogarithmicModel::levelSet(double * modelCoefficients, double xMin, doubl double LogarithmicModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { if (derivateCoefficientIndex == 0) { - // Derivate: ln(x) - assert(x >0); + // Derivate with respect to a: ln(x) + assert(x > 0); return log(x); } - if (derivateCoefficientIndex == 1) { - // Derivate: 1 - return 1; - } - assert(false); - return 0.0; + assert(derivateCoefficientIndex == 1); + // Derivate with respect to b: 1 + return 1.0; } bool LogarithmicModel::dataSuitableForFit(Store * store, int series) const { diff --git a/apps/regression/model/logistic_model.cpp b/apps/regression/model/logistic_model.cpp index 0876aa70d..ec9c50a48 100644 --- a/apps/regression/model/logistic_model.cpp +++ b/apps/regression/model/logistic_model.cpp @@ -61,21 +61,18 @@ double LogisticModel::partialDerivate(double * modelCoefficients, int derivateCo double a = modelCoefficients[0]; double b = modelCoefficients[1]; double c = modelCoefficients[2]; - double denominator = 1.0+a*exp(-b*x); + double denominator = 1.0 + a * exp(-b * x); if (derivateCoefficientIndex == 0) { - // Derivate: exp(-b*x)*(-1 * c/(1.0+a*exp(-b*x))^2) - return -exp(-b*x) * c/(denominator * denominator); + // Derivate with respect to a: exp(-b*x)*(-1 * c/(1.0+a*exp(-b*x))^2) + return -exp(-b * x) * c / (denominator * denominator); } if (derivateCoefficientIndex == 1) { - // Derivate: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2) - return x*a*exp(-b*x)*c/(denominator * denominator); + // Derivate with respect to b: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2) + return x * a * exp(-b * x) * c / (denominator * denominator); } - if (derivateCoefficientIndex == 2) { - // Derivate: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2) - return 1.0/denominator; - } - assert(false); - return 0.0; + assert(derivateCoefficientIndex == 2); + // Derivate with respect to c: (-x)*a*exp(-b*x)*(-1/(1.0+a*exp(-b*x))^2) + return 1.0 / denominator; } void LogisticModel::specializedInitCoefficientsForFit(double * modelCoefficients, double defaultValue, Store * store, int series) const { @@ -86,7 +83,7 @@ void LogisticModel::specializedInitCoefficientsForFit(double * modelCoefficients * and c. Twice the standard vertical deviation is a rough estimate of c * that is "close enough" to c to seed the coefficient, without being too * dependent on outliers.*/ - modelCoefficients[2] = 2.0*store->standardDeviationOfColumn(series, 1); + modelCoefficients[2] = 2.0 * store->standardDeviationOfColumn(series, 1); } diff --git a/apps/regression/model/power_model.cpp b/apps/regression/model/power_model.cpp index b7c0e09e0..a6b768f09 100644 --- a/apps/regression/model/power_model.cpp +++ b/apps/regression/model/power_model.cpp @@ -44,19 +44,16 @@ double PowerModel::partialDerivate(double * modelCoefficients, int derivateCoeff double a = modelCoefficients[0]; double b = modelCoefficients[1]; if (derivateCoefficientIndex == 0) { - // Derivate: pow(x,b) + // Derivate with respect to a: pow(x,b) return pow(x,b); } - if (derivateCoefficientIndex == 1) { - assert(x >= 0); - /* We assume all xi are positive. - * For x = 0, a*pow(x,b) = 0, the partial derivate along b is 0 - * For x > 0, a*pow(x,b) = a*exp(b*ln(x)), the partial derivate along b is - * ln(x)*a*pow(x,b) */ - return x == 0 ? 0 : log(x)*a*pow(x, b); - } - assert(false); - return 0.0; + assert(derivateCoefficientIndex == 1); + assert(x >= 0); + /* We assume all xi are positive. + * For x = 0, a*pow(x,b) = 0, the partial derivate with respect to b is 0 + * For x > 0, a*pow(x,b) = a*exp(b*ln(x)), the partial derivate with respect + * to b is ln(x)*a*pow(x,b) */ + return x == 0.0 ? 0.0 : log(x) * a * pow(x, b); } void PowerModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { diff --git a/apps/regression/model/quadratic_model.cpp b/apps/regression/model/quadratic_model.cpp index 66bc7cf0c..d258116fc 100644 --- a/apps/regression/model/quadratic_model.cpp +++ b/apps/regression/model/quadratic_model.cpp @@ -47,19 +47,16 @@ double QuadraticModel::evaluate(double * modelCoefficients, double x) const { double QuadraticModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { if (derivateCoefficientIndex == 0) { - // Derivate: x^2 + // Derivate with respect to a: x^2 return x*x; } if (derivateCoefficientIndex == 1) { - // Derivate: x + // Derivate with respect to b: x return x; } - if (derivateCoefficientIndex == 2) { - // Derivate: 1 - return 1; - } - assert(false); - return 0.0; + assert(derivateCoefficientIndex == 2); + // Derivate with respect to c: 1 + return 1.0; } Expression QuadraticModel::expression(double * modelCoefficients) { diff --git a/apps/regression/model/quartic_model.cpp b/apps/regression/model/quartic_model.cpp index 2b37e4423..3015b7f52 100644 --- a/apps/regression/model/quartic_model.cpp +++ b/apps/regression/model/quartic_model.cpp @@ -64,28 +64,24 @@ double QuarticModel::evaluate(double * modelCoefficients, double x) const { } double QuarticModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { - if (derivateCoefficientIndex == 0) { - // Derivate: x^4 - return x*x*x*x; - } - if (derivateCoefficientIndex == 1) { - // Derivate: x^3 - return x*x*x; - } - if (derivateCoefficientIndex == 2) { - // Derivate: x^2 - return x*x; - } - if (derivateCoefficientIndex == 3) { - // Derivate: x - return x; - } - if (derivateCoefficientIndex == 4) { - // Derivate: 1 - return 1; - } - assert(false); - return 0.0; + switch (derivateCoefficientIndex) { + case 0: + // Derivate with respect to a: x^4 + return x*x*x*x; + case 1: + // Derivate with respect to b: x^3 + return x*x*x; + case 2: + // Derivate with respect to c: x^2 + return x*x; + case 3: + // Derivate with respect to d: x + return x; + default: + assert(derivateCoefficientIndex == 4); + // Derivate with respect to e: 1 + return 1.0; + }; } Expression QuarticModel::expression(double * modelCoefficients) { diff --git a/apps/regression/model/trigonometric_model.cpp b/apps/regression/model/trigonometric_model.cpp index f32f2c66b..b6ddd6359 100644 --- a/apps/regression/model/trigonometric_model.cpp +++ b/apps/regression/model/trigonometric_model.cpp @@ -46,28 +46,27 @@ double TrigonometricModel::evaluate(double * modelCoefficients, double x) const } double TrigonometricModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + if (derivateCoefficientIndex == 3) { + // Derivate with respect to d: 1 + return 1.0; + } + double a = modelCoefficients[0]; double b = modelCoefficients[1]; double c = modelCoefficients[2]; double radianX = x * toRadians(Poincare::Preferences::sharedPreferences()->angleUnit()); + if (derivateCoefficientIndex == 0) { - // Derivate: sin(b*x+c) - return sin(b*radianX+c); + // Derivate with respect to a: sin(b*x+c) + return sin(b * radianX + c); } if (derivateCoefficientIndex == 1) { - // Derivate: x*a*cos(b*x+c); - return radianX*a*cos(b*radianX+c); + // Derivate with respect to b: x*a*cos(b*x+c); + return radianX * a * cos(b * radianX + c); } - if (derivateCoefficientIndex == 2) { - // Derivate: a*cos(b*x+c) - return a*cos(b*radianX+c); - } - if (derivateCoefficientIndex == 3) { - // Derivate: 1 - return 1.0; - } - assert(false); - return 0.0; + assert(derivateCoefficientIndex == 2); + // Derivatewith respect to c: a*cos(b*x+c) + return a * cos(b * radianX + c); } void TrigonometricModel::specializedInitCoefficientsForFit(double * modelCoefficients, double defaultValue, Store * store, int series) const { From 73c55f0437d1f9974645ee5e838f7b75227f016f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 13 May 2020 14:32:43 +0200 Subject: [PATCH 46/85] [apps/regression] Add test for proportional_regression --- apps/regression/test/model.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/regression/test/model.cpp b/apps/regression/test/model.cpp index b38d9936d..ff07521e7 100644 --- a/apps/regression/test/model.cpp +++ b/apps/regression/test/model.cpp @@ -43,6 +43,13 @@ QUIZ_CASE(linear_regression) { assert_regression_is(x, y, 4, Model::Type::Linear, coefficients); } +QUIZ_CASE(proportional_regression) { + double x[] = {7.0, 5.0, 1.0, 9.0, 3.0}; + double y[] = {-41.4851, -29.62186, -6.454245, -53.4976, -18.03325}; + double coefficients[] = {-5.89}; + assert_regression_is(x, y, 5, Model::Type::Proportional, coefficients); +} + QUIZ_CASE(quadratic_regression) { double x[] = {-34.0, -12.0, 5.0, 86.0, -2.0}; double y[] = {-8241.389, -1194.734, -59.163, - 46245.39, -71.774}; From 016f87c26614f2ee9a54c710ee9f9475b5af5f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 7 May 2020 17:56:11 +0200 Subject: [PATCH 47/85] [escher/layout_field] Reset the scroll when clearing the layout Scenario: Go to the graph app, create f(x) = 1 then create g(x) -> there is a weird big margin in the edition field --- escher/include/escher/layout_field.h | 2 +- escher/src/layout_field.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 436298b18..7476727d2 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -26,7 +26,7 @@ public: Poincare::Context * context() const; bool isEditing() const override { return m_contentView.isEditing(); } void setEditing(bool isEditing) override; - void clearLayout() { m_contentView.clearLayout(); } + void clearLayout(); void scrollToCursor() { scrollToBaselinedRect(m_contentView.cursorRect(), m_contentView.cursor()->baselineWithoutSelection()); } diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index 2e9094fe4..4575f5258 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -294,6 +294,11 @@ void LayoutField::setEditing(bool isEditing) { } } +void LayoutField::clearLayout() { + m_contentView.clearLayout(); // Replace the layout with an empty horizontal layout + reloadScroll(); // Put the scroll to offset 0 +} + Context * LayoutField::context() const { return (m_delegate != nullptr) ? m_delegate->context() : nullptr; } From 456cd3c73a43bffc39e5c1522428c1634a23fdfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 13 May 2020 17:51:06 +0200 Subject: [PATCH 48/85] Revert "[apps] Add FLT_EPSILON in float comparisons in interactive_curve_vw_rge" This reverts commit 18381fd33470761319c84fdee471873a2d7357e3. --- apps/shared/interactive_curve_view_range.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/shared/interactive_curve_view_range.cpp b/apps/shared/interactive_curve_view_range.cpp index dc1c6fd78..f549720c7 100644 --- a/apps/shared/interactive_curve_view_range.cpp +++ b/apps/shared/interactive_curve_view_range.cpp @@ -1,7 +1,6 @@ #include "interactive_curve_view_range.h" #include #include -#include #include #include #include @@ -203,24 +202,24 @@ void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) { void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRation, float leftMarginRation) { float xRange = xMax() - xMin(); float yRange = yMax() - yMin(); - if (x < xMin() + leftMarginRation*xRange - FLT_EPSILON && !std::isinf(x) && !std::isnan(x)) { + if (x < xMin() + leftMarginRation*xRange && !std::isinf(x) && !std::isnan(x)) { m_yAuto = false; float newXMin = x - leftMarginRation*xRange; m_xRange.setMax(newXMin + xRange, k_lowerMaxFloat, k_upperMaxFloat); MemoizedCurveViewRange::protectedSetXMin(newXMin, k_lowerMaxFloat, k_upperMaxFloat); } - if (x > xMax() - rightMarginRatio*xRange + FLT_EPSILON && !std::isinf(x) && !std::isnan(x)) { + if (x > xMax() - rightMarginRatio*xRange && !std::isinf(x) && !std::isnan(x)) { m_yAuto = false; m_xRange.setMax(x + rightMarginRatio*xRange, k_lowerMaxFloat, k_upperMaxFloat); MemoizedCurveViewRange::protectedSetXMin(xMax() - xRange, k_lowerMaxFloat, k_upperMaxFloat); } - if (y < yMin() + bottomMarginRation*yRange - FLT_EPSILON && !std::isinf(y) && !std::isnan(y)) { + if (y < yMin() + bottomMarginRation*yRange && !std::isinf(y) && !std::isnan(y)) { m_yAuto = false; float newYMin = y - bottomMarginRation*yRange; m_yRange.setMax(newYMin + yRange, k_lowerMaxFloat, k_upperMaxFloat); MemoizedCurveViewRange::protectedSetYMin(newYMin, k_lowerMaxFloat, k_upperMaxFloat); } - if (y > yMax() - topMarginRatio*yRange + FLT_EPSILON && !std::isinf(y) && !std::isnan(y)) { + if (y > yMax() - topMarginRatio*yRange && !std::isinf(y) && !std::isnan(y)) { m_yAuto = false; m_yRange.setMax(y + topMarginRatio*yRange, k_lowerMaxFloat, k_upperMaxFloat); MemoizedCurveViewRange::protectedSetYMin(yMax() - yRange, k_lowerMaxFloat, k_upperMaxFloat); From ee78948cf8da077c46f44fbf96d7ef944e43ffd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 14 May 2020 10:56:08 +0200 Subject: [PATCH 49/85] [apps/interactive_curve_view_range] Clean panToMakePointVisible --- apps/shared/interactive_curve_view_range.cpp | 54 +++++++++++--------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/apps/shared/interactive_curve_view_range.cpp b/apps/shared/interactive_curve_view_range.cpp index f549720c7..9e733b874 100644 --- a/apps/shared/interactive_curve_view_range.cpp +++ b/apps/shared/interactive_curve_view_range.cpp @@ -199,30 +199,38 @@ void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) { } } -void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRation, float leftMarginRation) { - float xRange = xMax() - xMin(); - float yRange = yMax() - yMin(); - if (x < xMin() + leftMarginRation*xRange && !std::isinf(x) && !std::isnan(x)) { - m_yAuto = false; - float newXMin = x - leftMarginRation*xRange; - m_xRange.setMax(newXMin + xRange, k_lowerMaxFloat, k_upperMaxFloat); - MemoizedCurveViewRange::protectedSetXMin(newXMin, k_lowerMaxFloat, k_upperMaxFloat); +void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRatio, float leftMarginRatio) { + if (!std::isinf(x) && !std::isnan(x)) { + const float xRange = xMax() - xMin(); + const float leftMargin = leftMarginRatio * xRange; + if (x < xMin() + leftMargin) { + m_yAuto = false; + const float newXMin = x - leftMargin; + m_xRange.setMax(newXMin + xRange, k_lowerMaxFloat, k_upperMaxFloat); + MemoizedCurveViewRange::protectedSetXMin(newXMin, k_lowerMaxFloat, k_upperMaxFloat); + } + const float rightMargin = rightMarginRatio * xRange; + if (x > xMax() - rightMargin) { + m_yAuto = false; + m_xRange.setMax(x + rightMargin, k_lowerMaxFloat, k_upperMaxFloat); + MemoizedCurveViewRange::protectedSetXMin(xMax() - xRange, k_lowerMaxFloat, k_upperMaxFloat); + } } - if (x > xMax() - rightMarginRatio*xRange && !std::isinf(x) && !std::isnan(x)) { - m_yAuto = false; - m_xRange.setMax(x + rightMarginRatio*xRange, k_lowerMaxFloat, k_upperMaxFloat); - MemoizedCurveViewRange::protectedSetXMin(xMax() - xRange, k_lowerMaxFloat, k_upperMaxFloat); - } - if (y < yMin() + bottomMarginRation*yRange && !std::isinf(y) && !std::isnan(y)) { - m_yAuto = false; - float newYMin = y - bottomMarginRation*yRange; - m_yRange.setMax(newYMin + yRange, k_lowerMaxFloat, k_upperMaxFloat); - MemoizedCurveViewRange::protectedSetYMin(newYMin, k_lowerMaxFloat, k_upperMaxFloat); - } - if (y > yMax() - topMarginRatio*yRange && !std::isinf(y) && !std::isnan(y)) { - m_yAuto = false; - m_yRange.setMax(y + topMarginRatio*yRange, k_lowerMaxFloat, k_upperMaxFloat); - MemoizedCurveViewRange::protectedSetYMin(yMax() - yRange, k_lowerMaxFloat, k_upperMaxFloat); + if (!std::isinf(y) && !std::isnan(y)) { + const float yRange = yMax() - yMin(); + const float bottomMargin = bottomMarginRatio * yRange; + if (y < yMin() + bottomMargin) { + m_yAuto = false; + const float newYMin = y - bottomMargin; + m_yRange.setMax(newYMin + yRange, k_lowerMaxFloat, k_upperMaxFloat); + MemoizedCurveViewRange::protectedSetYMin(newYMin, k_lowerMaxFloat, k_upperMaxFloat); + } + const float topMargin = topMarginRatio * yRange; + if (y > yMax() - topMargin) { + m_yAuto = false; + m_yRange.setMax(y + topMargin, k_lowerMaxFloat, k_upperMaxFloat); + MemoizedCurveViewRange::protectedSetYMin(yMax() - yRange, k_lowerMaxFloat, k_upperMaxFloat); + } } } From a0c5a1fe1c760b24f13de3e28c28adad149b915c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 14 May 2020 10:59:59 +0200 Subject: [PATCH 50/85] [apps] InteractCurveVwCtlr::addMargin adds more than the required margin --- apps/shared/interactive_curve_view_controller.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/shared/interactive_curve_view_controller.cpp b/apps/shared/interactive_curve_view_controller.cpp index 997149852..6daeacea0 100644 --- a/apps/shared/interactive_curve_view_controller.cpp +++ b/apps/shared/interactive_curve_view_controller.cpp @@ -57,7 +57,11 @@ float InteractiveCurveViewController::addMargin(float y, float range, bool isVer assert(topMarginRatio + bottomMarginRatio < 1); // Assertion so that the formula is correct float ratioDenominator = 1 - bottomMarginRatio - topMarginRatio; float ratio = isMin ? -bottomMarginRatio : topMarginRatio; - ratio = ratio / ratioDenominator; + /* We want to add slightly more than the required margin, so that + * InteractiveCurveViewRange::panToMakePointVisible does not think a point is + * invisible due to precision problems when checking if it is outside the + * required margin. This is why we add a 1.05f factor. */ + ratio = 1.05f * ratio / ratioDenominator; return y + ratio * range; } From e7988b9fa28116e770b7fae77d87ef1ca7cf03d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Thu, 14 May 2020 12:01:17 +0200 Subject: [PATCH 51/85] [apps/round_cursor_view] Add comment about flaw in algorithm --- apps/shared/round_cursor_view.cpp | 33 ++++++++++++++++++++++++++++-- kandinsky/include/kandinsky/size.h | 3 +++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/apps/shared/round_cursor_view.cpp b/apps/shared/round_cursor_view.cpp index 10b14e86b..74661c644 100644 --- a/apps/shared/round_cursor_view.cpp +++ b/apps/shared/round_cursor_view.cpp @@ -66,11 +66,40 @@ bool RoundCursorView::eraseCursorIfPossible() { return false; } // Erase the cursor - KDColor cursorWorkingBuffer[Dots::LargeDotDiameter*Dots::LargeDotDiameter]; + KDColor cursorWorkingBuffer[k_cursorSize * k_cursorSize]; KDContext * ctx = KDIonContext::sharedContext(); ctx->setOrigin(currentFrame.origin()); ctx->setClippingRect(currentFrame); - ctx->fillRectWithPixels(KDRect(0,0,k_cursorSize, k_cursorSize), m_underneathPixelBuffer, cursorWorkingBuffer); + KDSize cursorSize = KDSize(k_cursorSize, k_cursorSize); + + /* We assert that the visible frame is not cropped (indeed a cursor is always + * fully inside the window, thanks to panToMakeCursorVisible). Otherwise, we + * would need to change this algorithm. + * + * +---+ + * | |<- frame m_underneathPixelBuffer: +---+ + * +----+---+--------+ |000| + * | |xxx| |<- parentVisibleFrame |xxx| + * | +---+ | +---+ + * | | + * +-----------------+ + * + * +---+ + * |xxx|: absoluteVisibleFrame + * +---+ + * + * What we would draw with the current algorithm: + * +---+ + * | |<- frame + * +----+---+--------+ + * | |000| |<- parentVisibleFrame + * | +---+ | + * | | + * +-----------------+ + * + * */ + assert(currentFrame.size() == cursorSize); + ctx->fillRectWithPixels(KDRect(0, 0, cursorSize), m_underneathPixelBuffer, cursorWorkingBuffer); // TODO Restore the context to previous values? return true; } diff --git a/kandinsky/include/kandinsky/size.h b/kandinsky/include/kandinsky/size.h index 970b4f071..875719de4 100644 --- a/kandinsky/include/kandinsky/size.h +++ b/kandinsky/include/kandinsky/size.h @@ -9,6 +9,9 @@ public: m_width(width), m_height(height) {} constexpr KDCoordinate width() const { return m_width; } constexpr KDCoordinate height() const { return m_height; } + bool operator==(const KDSize &other) const { + return m_width == other.width() && m_height == other.height(); + } private: KDCoordinate m_width; KDCoordinate m_height; From 055f8bf1bdcb3b4a474fc52f7c6cd66b0d2abe32 Mon Sep 17 00:00:00 2001 From: TheTrueBrot Date: Wed, 13 May 2020 17:17:09 +0200 Subject: [PATCH 52/85] Fixed a small mistake in the german Translation --- apps/shared.de.i18n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index 5220c43b4..c470e3775 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -1,6 +1,6 @@ ActivateDeactivate = "Aktivieren/Deaktivieren" ActivateDutchExamMode = "Activate Dutch exam mode" -ActivateExamMode = "Starten Prüfungsmodus" +ActivateExamMode = "Prüfungsmodus starten" ActiveExamModeMessage1 = "Alle Ihre Daten werden " ActiveExamModeMessage2 = "gelöscht, wenn Sie den " ActiveExamModeMessage3 = "Prüfungsmodus einschalten." From 3f5487ca902fb07fe8e334633a2fddfd6cf18762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 13 May 2020 15:57:17 +0200 Subject: [PATCH 53/85] [python] Forbid inlining on gc_collect to avoid missing some roots --- python/port/port.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index be16aece9..385367101 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -248,7 +248,10 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode mp_raise_TypeError("Color couldn't be parsed"); } -void gc_collect(void) { +/* Forbid inlining to ensure regs to be at the top of the stack. Otherwise, + * LTO inlining can make regs lower on the stack than some just-allocated + * pointers. */ +__attribute__((noinline)) void gc_collect(void) { void * python_stack_top = MP_STATE_THREAD(stack_top); assert(python_stack_top != NULL); From 5a32006dcdb052cb76330fac9da66c89c56010cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 13 May 2020 16:28:45 +0200 Subject: [PATCH 54/85] [python] Revert: collectRoots does not need to look for unaligned pointers as they should not exist --- python/port/port.cpp | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index 385367101..d0d1f7224 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -123,7 +123,7 @@ void MicroPython::init(void * heapStart, void * heapEnd) { mp_pystack_init(pystack, &pystack[MP_ARRAY_SIZE(pystack)]); #endif - volatile int stackTop; + volatile uintptr_t stackTop; void * stackTopAddress = (void *)(&stackTop); /* We delimit the stack part that will be used by Python. The stackTop is the * address of the first object that can be allocated on Python stack. This @@ -155,29 +155,12 @@ void MicroPython::registerScriptProvider(ScriptProvider * s) { } void MicroPython::collectRootsAtAddress(char * address, int byteLength) { -#if __EMSCRIPTEN__ - // All objects are aligned, as asserted. + /* All pointers on the stack are aligned on sizeof(void *), as asserted. + * This is a consequence of the alignment requireduirements of compilers as long + * (Cf http://www.catb.org/esr/structure-packing/). */ assert(((unsigned long)address) % ((unsigned long)sizeof(void *)) == 0); assert(byteLength % sizeof(void *) == 0); gc_collect_root((void **)address, byteLength / sizeof(void *)); -#else - for (size_t i = 0; i < sizeof(void *); i++) { - /* Objects on the stack are not necessarily aligned on sizeof(void *), - * which is also true for pointers refering to the heap. MicroPython - * gc_collect_root expects a table of void * that will be scanned every - * sizeof(void *) step. So we have to scan the stack repetitively with a - * increasing offset to be sure to check every byte for a heap address. - * If some memory can be reinterpreted as a pointer in the heap, gc_collect_root - * will prevent the destruction of the pointed heap memory. At worst (if - * the interpreted pointer was in fact an unaligned object or uninitialized - * memory), we will just keep extra objects in the heap which is not optimal - * but does not cause any crash. */ - char * addressWithOffset = address + i; - // Ensure to round the length to the ceiling - size_t lengthInAddressSize = (byteLength - i + sizeof(void *) - 1)/sizeof(void *); - gc_collect_root((void **)addressWithOffset, lengthInAddressSize); - } -#endif } KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode){ From 3f6647f3ae44fb9467703d4979d05e9ffaec90f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 14 May 2020 16:22:51 +0200 Subject: [PATCH 55/85] [ion][python] Implement an architecture-dependant collect_registers. setjmp is not guaranteed to collect all registers without modification on all platforms. This fixes the following bug: when the pointer of a newly allocated object on the Python heap is stored in rpb registers on x86_64 arch, it was not collected by the garbarge collector. --- ion/include/ion.h | 4 ++ ion/src/device/Makefile | 2 + ion/src/shared/collect_registers.cpp | 15 ++++++ ion/src/simulator/android/Makefile | 2 + ion/src/simulator/ios/Makefile | 2 + ion/src/simulator/linux/Makefile | 2 + ion/src/simulator/macos/Makefile | 2 + .../simulator/shared/collect_registers.cpp | 17 +++++++ .../shared/collect_registers_x86_64.s | 25 +++++++++ ion/src/simulator/web/Makefile | 2 + ion/src/simulator/windows/Makefile | 2 + liba/include/setjmp.h | 3 +- python/port/port.cpp | 51 ++++++++----------- 13 files changed, 98 insertions(+), 31 deletions(-) create mode 100644 ion/src/shared/collect_registers.cpp create mode 100644 ion/src/simulator/shared/collect_registers.cpp create mode 100644 ion/src/simulator/shared/collect_registers_x86_64.s diff --git a/ion/include/ion.h b/ion/include/ion.h index ac327bbac..45a8e5559 100644 --- a/ion/include/ion.h +++ b/ion/include/ion.h @@ -17,6 +17,7 @@ #include #include #include +#include /* ION is not your regular library. It is a library you link against, but it * will take care of configuring the whole environment for you. In POSIX terms, @@ -48,6 +49,9 @@ void decompress(const uint8_t * src, uint8_t * dst, int srcSize, int dstSize); // Tells whether the stack pointer is within acceptable bounds bool stackSafe(); +// Collect registers in a buffer and returns the stack pointer +uintptr_t collectRegisters(jmp_buf regs); + } #endif diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index 4a3a96426..b4f6a6b11 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -10,6 +10,8 @@ ifeq ($(EPSILON_TELEMETRY),1) ion_src += ion/src/shared/telemetry_console.cpp endif +ion_src += ion/src/shared/collect_registers.cpp + ION_DEVICE_SFLAGS = -Iion/src/device/$(MODEL) -Iion/src/device/shared $(call object_for,$(ion_device_src) $(ion_device_flasher_src) $(ion_device_bench_src)): SFLAGS += $(ION_DEVICE_SFLAGS) diff --git a/ion/src/shared/collect_registers.cpp b/ion/src/shared/collect_registers.cpp new file mode 100644 index 000000000..2083f6969 --- /dev/null +++ b/ion/src/shared/collect_registers.cpp @@ -0,0 +1,15 @@ +#include +#include + +namespace Ion { + +/* Forbid inlining to ensure dummy to be at the top of the stack. Otherwise, + * LTO inlining can make regs lower on the stack than some just-allocated + * pointers. */ +__attribute__((noinline))uintptr_t collectRegisters(jmp_buf buf) { + setjmp(buf); + int dummy; + return (uintptr_t)&dummy; +} + +} diff --git a/ion/src/simulator/android/Makefile b/ion/src/simulator/android/Makefile index 9143b3dfe..d3fb36236 100644 --- a/ion/src/simulator/android/Makefile +++ b/ion/src/simulator/android/Makefile @@ -7,6 +7,8 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/language.cpp \ ) +ion_src += ion/src/shared/collect_registers.cpp + ifeq ($(EPSILON_TELEMETRY),1) ion_src += ion/src/simulator/android/src/cpp/telemetry.cpp endif diff --git a/ion/src/simulator/ios/Makefile b/ion/src/simulator/ios/Makefile index 2d85d1297..334e1d4d1 100644 --- a/ion/src/simulator/ios/Makefile +++ b/ion/src/simulator/ios/Makefile @@ -7,6 +7,8 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/callback.cpp \ ) +ion_src += ion/src/shared/collect_registers.cpp + $(call object_for,ion/src/simulator/shared/main.cpp) : SFLAGS += -DEPSILON_SDL_FULLSCREEN=1 ifeq ($(EPSILON_TELEMETRY),1) diff --git a/ion/src/simulator/linux/Makefile b/ion/src/simulator/linux/Makefile index f6cae470e..aa8500471 100644 --- a/ion/src/simulator/linux/Makefile +++ b/ion/src/simulator/linux/Makefile @@ -15,6 +15,8 @@ ion_src += $(addprefix ion/src/simulator/linux/, \ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/callback.cpp \ + collect_registers_x86_64.s \ + collect_registers.cpp \ ) ifeq ($(EPSILON_TELEMETRY),1) diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index 81b06f3d4..b00459f75 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -5,6 +5,8 @@ ion_src += $(addprefix ion/src/simulator/macos/, \ ion_src += $(addprefix ion/src/simulator/shared/, \ apple/language.m \ dummy/callback.cpp \ + collect_registers_x86_64.s \ + collect_registers.cpp \ ) ifeq ($(EPSILON_TELEMETRY),1) diff --git a/ion/src/simulator/shared/collect_registers.cpp b/ion/src/simulator/shared/collect_registers.cpp new file mode 100644 index 000000000..4fa4c86fd --- /dev/null +++ b/ion/src/simulator/shared/collect_registers.cpp @@ -0,0 +1,17 @@ +#include + +extern "C" { + +// define in assembly code +// Force the name as archs (linux/macos) don't mangle C names the same way +extern uintptr_t collect_registers(uintptr_t * regs) asm ("_collect_registers"); +} +namespace Ion { + +// Wrapper to avoid +uintptr_t collectRegisters(jmp_buf buf) { + uintptr_t * regs = (uintptr_t *)buf; + return collect_registers(regs); +} + +} diff --git a/ion/src/simulator/shared/collect_registers_x86_64.s b/ion/src/simulator/shared/collect_registers_x86_64.s new file mode 100644 index 000000000..4740681b4 --- /dev/null +++ b/ion/src/simulator/shared/collect_registers_x86_64.s @@ -0,0 +1,25 @@ +.text + +.global _collect_registers + +_collect_registers: + pushq %r15 + pushq %r14 + pushq %r13 + pushq %r12 + pushq %rbp + pushq %rbx + movq %rbx, (%rdi) + movq %rbp, 8(%rdi) + movq %r12, 16(%rdi) + popq %rbx + popq %rbp + popq %r12 + movq %r13, 24(%rdi) + movq %r14, 32(%rdi) + popq %r13 + movq %r15, 40(%rdi) + popq %r14 + popq %r15 + movq %rsp, %rax + ret diff --git a/ion/src/simulator/web/Makefile b/ion/src/simulator/web/Makefile index 89f41927a..61a7641dc 100644 --- a/ion/src/simulator/web/Makefile +++ b/ion/src/simulator/web/Makefile @@ -22,6 +22,8 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/language.cpp \ ) +ion_src += ion/src/shared/collect_registers.cpp + ifeq ($(EPSILON_TELEMETRY),1) ion_src += ion/src/simulator/shared/dummy/telemetry_init.cpp ion_src += ion/src/shared/telemetry_console.cpp diff --git a/ion/src/simulator/windows/Makefile b/ion/src/simulator/windows/Makefile index 897db8dca..50c6396ac 100644 --- a/ion/src/simulator/windows/Makefile +++ b/ion/src/simulator/windows/Makefile @@ -8,6 +8,8 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/callback.cpp \ ) +ion_src += ion/src/shared/collect_registers.cpp + ifeq ($(EPSILON_TELEMETRY),1) ion_src += ion/src/simulator/shared/dummy/telemetry_init.cpp ion_src += ion/src/shared/telemetry_console.cpp diff --git a/liba/include/setjmp.h b/liba/include/setjmp.h index 6a7ccf2e4..826b0cf5a 100644 --- a/liba/include/setjmp.h +++ b/liba/include/setjmp.h @@ -1,6 +1,7 @@ #ifndef LIBA_SETJMP_H #define LIBA_SETJMP_H +#include #include "private/macros.h" /* We are preseving registers: @@ -14,7 +15,7 @@ LIBA_BEGIN_DECLS -typedef int jmp_buf[31]; +typedef uintptr_t jmp_buf[31]; void longjmp(jmp_buf env, int val); int setjmp(jmp_buf env); diff --git a/python/port/port.cpp b/python/port/port.cpp index d0d1f7224..fd86492db 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -1,6 +1,6 @@ #include "port.h" -#include +#include #include #include @@ -231,63 +231,54 @@ KDColor MicroPython::ColorParser::ParseColor(mp_obj_t input, ColorMode ColorMode mp_raise_TypeError("Color couldn't be parsed"); } -/* Forbid inlining to ensure regs to be at the top of the stack. Otherwise, - * LTO inlining can make regs lower on the stack than some just-allocated - * pointers. */ -__attribute__((noinline)) void gc_collect(void) { +void gc_collect_regs_and_stack(void) { + // get the registers and the sp + jmp_buf regs; + uintptr_t sp = Ion::collectRegisters(regs); + void * python_stack_top = MP_STATE_THREAD(stack_top); assert(python_stack_top != NULL); - gc_collect_start(); - - modturtle_gc_collect(); - modpyplot_gc_collect(); - - /* get the registers. - * regs is the also the last object on the stack so the stack is bound by - * ®s and python_stack_top. */ - jmp_buf regs; - /* TODO: we use setjmp to get the registers values to look for python heap - * root. However, the 'setjmp' does not guarantee that it gets all registers - * values. We should check our setjmp implementation for the device and - * ensure that it also works for other platforms. */ - setjmp(regs); - - void **regs_ptr = (void**)®s; - /* On the device, the stack is stored in reverse order, but it might not be * the case on a computer. We thus have to take the absolute value of the * addresses difference. */ size_t stackLengthInByte; void ** scanStart; - if ((uintptr_t)python_stack_top > (uintptr_t)regs_ptr) { + if ((uintptr_t)python_stack_top > sp) { /* To compute the stack length: - * regs + * registers * <-----------> * STACK <- ...| | | | | |--|--|--|--| | | | | | | - * ^®s ^python_stack_top + * ^sp ^python_stack_top * */ - stackLengthInByte = (uintptr_t)python_stack_top - (uintptr_t)regs_ptr; - scanStart = regs_ptr; + stackLengthInByte = (uintptr_t)python_stack_top - sp; + scanStart = (void **)sp; } else { /* When computing the stack length, take into account regs' size. - * regs + * registers * <-----------> * STACK -> | | | | | | | | | | | |--|--|--|--| | | |... - * ^python_stack_top ^®s + * ^python_stack_top ^sp * */ - stackLengthInByte = (uintptr_t)regs_ptr - (uintptr_t)python_stack_top + sizeof(regs); + stackLengthInByte = sp - (uintptr_t)python_stack_top + sizeof(regs); scanStart = (void **)python_stack_top; } /* Memory error detectors might find an error here as they might split regs * and stack memory zones. */ MicroPython::collectRootsAtAddress((char *)scanStart, stackLengthInByte); +} + +void gc_collect(void) { + gc_collect_start(); + modturtle_gc_collect(); + modpyplot_gc_collect(); + gc_collect_regs_and_stack(); gc_collect_end(); } From 0f61b2ccf3169c62b10ff9641d56ea30cdfe0112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 15 May 2020 11:24:06 +0200 Subject: [PATCH 56/85] [ion] Add comment on default collectRegisters implementation --- ion/src/shared/collect_registers.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ion/src/shared/collect_registers.cpp b/ion/src/shared/collect_registers.cpp index 2083f6969..960fd0423 100644 --- a/ion/src/shared/collect_registers.cpp +++ b/ion/src/shared/collect_registers.cpp @@ -7,6 +7,10 @@ namespace Ion { * LTO inlining can make regs lower on the stack than some just-allocated * pointers. */ __attribute__((noinline))uintptr_t collectRegisters(jmp_buf buf) { + /* TODO: we use setjmp to get the registers values to look for python heap + * root. However, the 'setjmp' does not guarantee that it gets all registers + * values. We should check our setjmp implementation for the device and + * ensure that it also works for other platforms. */ setjmp(buf); int dummy; return (uintptr_t)&dummy; From 4cd0d2058580a48cb1ca2288a3888b3628050cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 15 May 2020 14:35:32 +0200 Subject: [PATCH 57/85] [ion] Fix comment --- ion/src/simulator/shared/collect_registers.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ion/src/simulator/shared/collect_registers.cpp b/ion/src/simulator/shared/collect_registers.cpp index 4fa4c86fd..0f73be4b7 100644 --- a/ion/src/simulator/shared/collect_registers.cpp +++ b/ion/src/simulator/shared/collect_registers.cpp @@ -5,10 +5,12 @@ extern "C" { // define in assembly code // Force the name as archs (linux/macos) don't mangle C names the same way extern uintptr_t collect_registers(uintptr_t * regs) asm ("_collect_registers"); + } namespace Ion { -// Wrapper to avoid +// Wrapper to avoid handling c++ name mangling when writing assembly code + uintptr_t collectRegisters(jmp_buf buf) { uintptr_t * regs = (uintptr_t *)buf; return collect_registers(regs); From 4e1f7c1cb8d329c95decfbe81a0b48d154433b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 15 May 2020 14:36:39 +0200 Subject: [PATCH 58/85] [python] Fix comment --- python/port/port.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index fd86492db..198110fb0 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -155,8 +155,8 @@ void MicroPython::registerScriptProvider(ScriptProvider * s) { } void MicroPython::collectRootsAtAddress(char * address, int byteLength) { - /* All pointers on the stack are aligned on sizeof(void *), as asserted. - * This is a consequence of the alignment requireduirements of compilers as long + /* All addresses stored on the stack are aligned on sizeof(void *), as + * asserted. This is a consequence of the alignment requirements of compilers * (Cf http://www.catb.org/esr/structure-packing/). */ assert(((unsigned long)address) % ((unsigned long)sizeof(void *)) == 0); assert(byteLength % sizeof(void *) == 0); From 7f3f67aba0778e9b28ffe9300aaee970b15e8ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 18 May 2020 10:22:46 +0200 Subject: [PATCH 59/85] [python] Clean type confusion --- python/port/port.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/port/port.cpp b/python/port/port.cpp index 198110fb0..bac7595ca 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -123,7 +123,7 @@ void MicroPython::init(void * heapStart, void * heapEnd) { mp_pystack_init(pystack, &pystack[MP_ARRAY_SIZE(pystack)]); #endif - volatile uintptr_t stackTop; + volatile int stackTop; void * stackTopAddress = (void *)(&stackTop); /* We delimit the stack part that will be used by Python. The stackTop is the * address of the first object that can be allocated on Python stack. This From f1209bcf764442a24fe7b1709bd22f2df09674c9 Mon Sep 17 00:00:00 2001 From: Joachim Le Fournis <43498612+RedGl0w@users.noreply.github.com> Date: Mon, 11 May 2020 15:41:42 +0200 Subject: [PATCH 60/85] [App/zoom_parameter_controller] Changed subview order This fixes #500 --- apps/shared/zoom_parameter_controller.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/shared/zoom_parameter_controller.cpp b/apps/shared/zoom_parameter_controller.cpp index fffc06680..33a7f30ce 100644 --- a/apps/shared/zoom_parameter_controller.cpp +++ b/apps/shared/zoom_parameter_controller.cpp @@ -59,9 +59,9 @@ int ZoomParameterController::ContentView::numberOfSubviews() const { View * ZoomParameterController::ContentView::subviewAtIndex(int index) { assert(index >= 0 && index < 2); if (index == 0) { - return m_curveView; + return &m_legendView; } - return &m_legendView; + return m_curveView; } void ZoomParameterController::ContentView::layoutSubviews(bool force) { From 67fc7502e22ad715df88d4b06f8e0373186a80f6 Mon Sep 17 00:00:00 2001 From: Joachim Le Fournis <43498612+RedGl0w@users.noreply.github.com> Date: Fri, 15 May 2020 14:11:23 +0200 Subject: [PATCH 61/85] [App/zoom_parameter_controller] Add comment See : https://github.com/numworks/epsilon/pull/1548#discussion_r425708055 --- apps/shared/zoom_parameter_controller.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/shared/zoom_parameter_controller.cpp b/apps/shared/zoom_parameter_controller.cpp index 33a7f30ce..55496dba4 100644 --- a/apps/shared/zoom_parameter_controller.cpp +++ b/apps/shared/zoom_parameter_controller.cpp @@ -58,6 +58,11 @@ int ZoomParameterController::ContentView::numberOfSubviews() const { View * ZoomParameterController::ContentView::subviewAtIndex(int index) { assert(index >= 0 && index < 2); + /* The order of subview is important here : + * If we redraw the curveView before the legendView, that can have some display issue, when exiting sleep mode, which + can be visible, if the redraw of curveView is long (with complicated curve), so we prefer to have legendView + at first subview. + */ if (index == 0) { return &m_legendView; } From b48d3d6e4a725d9e24da4b2f32be091707f4d4d1 Mon Sep 17 00:00:00 2001 From: 0b101 <0b101@users.noreply.github.com> Date: Fri, 21 Feb 2020 16:08:55 -0600 Subject: [PATCH 62/85] Added a new compact result display mode --- apps/calculation/calculation.cpp | 45 +++++++++++++++++++------ apps/calculation/calculation.h | 1 + apps/calculation/history_controller.cpp | 2 +- apps/calculation/history_controller.h | 2 +- apps/calculation/history_view_cell.cpp | 18 ++++++---- escher/include/escher/metric.h | 4 +-- 6 files changed, 51 insertions(+), 21 deletions(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 160770b77..3448afe59 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -134,6 +134,10 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre // Get input height Layout inputLayout = createInputLayout(); KDCoordinate inputHeight = inputLayout.layoutSize().height(); + KDCoordinate inputWidth = inputLayout.layoutSize().width(); + float singleMargin = 2 * Metric::CommonSmallMargin; + float doubleMargin = 4 * Metric::CommonSmallMargin; + bool singleLine = false; KDCoordinate inputBaseline = inputLayout.baseline(); // Get exact output height if needed @@ -155,11 +159,18 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre if (displayOutput(context) == DisplayOutput::ExactOnly) { KDCoordinate exactOutputHeight = exactLayout.layoutSize().height(); - if (allExpressionsInline) { + KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); + singleLine = exactOutputWidth + inputWidth < maxWidth - 2; + if (singleLine && !allExpressionsInline) { KDCoordinate exactOutputBaseline = exactLayout.baseline(); - result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline); + result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline) + singleMargin; } else { - result = inputHeight+exactOutputHeight; + if (allExpressionsInline) { + KDCoordinate exactOutputBaseline = exactLayout.baseline(); + result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline); + } else { + result = inputHeight + exactOutputHeight + doubleMargin; + } } } else { bool couldNotCreateApproximateLayout = false; @@ -181,23 +192,37 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre } KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height(); + KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); + singleLine = approximateOutputWidth + inputWidth < maxWidth; if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) { - if (allExpressionsInline) { + if (singleLine && !allExpressionsInline) { KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline); + result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline) + singleMargin; } else { - result = inputHeight+approximateOutputHeight; + if (allExpressionsInline) { + KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); + result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline); + } else { + result = inputHeight + approximateOutputHeight + doubleMargin; + } } } else { assert(displayOutput(context) == DisplayOutput::ExactAndApproximate || (displayOutput(context) == DisplayOutput::ExactAndApproximateToggle && expanded)); KDCoordinate exactOutputHeight = exactLayout.layoutSize().height(); KDCoordinate exactOutputBaseline = exactLayout.baseline(); + KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); + KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); + singleLine = exactOutputWidth + approximateOutputWidth + inputWidth < maxWidth - 30; // the 30 represents the = sign (example: sin(30)) KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - if (allExpressionsInline) { - result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)); + if (singleLine && !allExpressionsInline) { + result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)) + singleMargin; } else { - KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) + std::max(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline); - result = inputHeight + outputHeight; + if (allExpressionsInline) { + result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)); + } else { + KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) + std::max(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline); + result = inputHeight + outputHeight + doubleMargin; + } } } } diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index 4dbe4fc08..203d56568 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -95,6 +95,7 @@ public: // Additional Information AdditionalInformationType additionalInformationType(Poincare::Context * context); private: + static constexpr int maxWidth = Ion::Display::Width - (Metric::CommonSmallMargin * 2) - Metric::EllipsisCellWidth - 48; // 48 is the difference history_view_cell's width and calculation's static constexpr int k_numberOfExpressions = 4; static constexpr KDCoordinate k_heightComputationFailureHeight = 50; static constexpr const char * k_maximalIntegerWithAdditionalInformation = "10000000000000000"; diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 74b548e1a..292a6199e 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -208,7 +208,7 @@ KDCoordinate HistoryController::rowHeight(int j) { return 0; } Shared::ExpiringPointer calculation = calculationAtIndex(j); - return calculation->height(App::app()->localContext(), j == selectedRow() && selectedSubviewType() == SubviewType::Output) + 4 * Metric::CommonSmallMargin; + return calculation->height(App::app()->localContext(), j == selectedRow() && selectedSubviewType() == SubviewType::Output); } int HistoryController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/history_controller.h b/apps/calculation/history_controller.h index c208d8451..407e52805 100644 --- a/apps/calculation/history_controller.h +++ b/apps/calculation/history_controller.h @@ -38,7 +38,7 @@ private: CalculationSelectableTableView * selectableTableView(); bool calculationAtIndexToggles(int index); void historyViewCellDidChangeSelection(HistoryViewCell ** cell, HistoryViewCell ** previousCell, int previousSelectedCellX, int previousSelectedCellY, SubviewType type, SubviewType previousType) override; - constexpr static int k_maxNumberOfDisplayedRows = 5; + constexpr static int k_maxNumberOfDisplayedRows = 8; CalculationSelectableTableView m_selectableTableView; HistoryViewCell m_calculationHistory[k_maxNumberOfDisplayedRows]; CalculationStore * m_calculationStore; diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 0c35fb688..0d3c84da3 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -178,18 +178,22 @@ void HistoryViewCell::layoutSubviews(bool force) { m_ellipsis.setFrame(KDRectZero, force); // Required to mark previous rect as dirty } KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay(); + KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); + int singleLine = outputSize.width() + inputSize.width() < Ion::Display::Width - (Metric::CommonSmallMargin * 2) - Metric::EllipsisCellWidth; + int inputHeight = (singleLine && inputSize.height() < outputSize.height()) ? (outputSize.height() - inputSize.height()) : 0; m_inputView.setFrame(KDRect( - 0, 0, + 0, + inputHeight, std::min(maxFrameWidth, inputSize.width()), inputSize.height()), force); - KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); + int outputHeight = singleLine ? std::max(0, inputSize.height() - outputSize.height()) / 2 + std::max(0, (inputSize.height() - outputSize.height()) / 2) + 1 : inputSize.height(); m_scrollableOutputView.setFrame(KDRect( - std::max(0, maxFrameWidth - outputSize.width()), - inputSize.height(), - std::min(maxFrameWidth, outputSize.width()), - outputSize.height()), - force); + std::max(0, maxFrameWidth - outputSize.width()), + outputHeight, + std::min(maxFrameWidth, outputSize.width()), + outputSize.height()), + force); } void HistoryViewCell::resetMemoization() { diff --git a/escher/include/escher/metric.h b/escher/include/escher/metric.h index f514cb68c..0ac573c96 100644 --- a/escher/include/escher/metric.h +++ b/escher/include/escher/metric.h @@ -10,8 +10,8 @@ public: constexpr static KDCoordinate CommonRightMargin = 20; constexpr static KDCoordinate CommonTopMargin = 15; constexpr static KDCoordinate CommonBottomMargin = 15; - constexpr static KDCoordinate CommonLargeMargin = 10; - constexpr static KDCoordinate CommonSmallMargin = 5; + constexpr static KDCoordinate CommonLargeMargin = 12; + constexpr static KDCoordinate CommonSmallMargin = 12; constexpr static KDCoordinate TitleBarExternHorizontalMargin = 5; constexpr static KDCoordinate TitleBarHeight = 18; constexpr static KDCoordinate ParameterCellHeight = 35; From c3d8520e73566d42c96a4c9d6d51a72c0c4c04e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 15 May 2020 12:10:02 +0200 Subject: [PATCH 63/85] [apps/calculation] Remove unnecessary bool --- apps/calculation/calculation.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 3448afe59..a16f9d1b0 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -137,7 +137,6 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre KDCoordinate inputWidth = inputLayout.layoutSize().width(); float singleMargin = 2 * Metric::CommonSmallMargin; float doubleMargin = 4 * Metric::CommonSmallMargin; - bool singleLine = false; KDCoordinate inputBaseline = inputLayout.baseline(); // Get exact output height if needed @@ -160,7 +159,7 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre if (displayOutput(context) == DisplayOutput::ExactOnly) { KDCoordinate exactOutputHeight = exactLayout.layoutSize().height(); KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); - singleLine = exactOutputWidth + inputWidth < maxWidth - 2; + bool singleLine = exactOutputWidth + inputWidth < maxWidth - 2; if (singleLine && !allExpressionsInline) { KDCoordinate exactOutputBaseline = exactLayout.baseline(); result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline) + singleMargin; @@ -193,7 +192,7 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height(); KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); - singleLine = approximateOutputWidth + inputWidth < maxWidth; + bool singleLine = approximateOutputWidth + inputWidth < maxWidth; if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) { if (singleLine && !allExpressionsInline) { KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); From 5a68b44aa1ddc96eb55ac4bb64147fb03bd8abe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 15 May 2020 12:25:43 +0200 Subject: [PATCH 64/85] [apps/calc] Merge SingleLine and allExpressionsInline behaviours --- apps/calculation/calculation.cpp | 37 +++++++++++--------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index a16f9d1b0..bf0e55518 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -159,19 +159,17 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre if (displayOutput(context) == DisplayOutput::ExactOnly) { KDCoordinate exactOutputHeight = exactLayout.layoutSize().height(); KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); - bool singleLine = exactOutputWidth + inputWidth < maxWidth - 2; - if (singleLine && !allExpressionsInline) { + + bool singleLine = allExpressionsInline || ((exactOutputWidth + inputWidth) < maxWidth - 2); //TODO LEA 2 + + if (singleLine) { KDCoordinate exactOutputBaseline = exactLayout.baseline(); result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline) + singleMargin; } else { - if (allExpressionsInline) { - KDCoordinate exactOutputBaseline = exactLayout.baseline(); - result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline); - } else { - result = inputHeight + exactOutputHeight + doubleMargin; - } + result = inputHeight + exactOutputHeight + doubleMargin; } } else { + // Create the approximate output layout bool couldNotCreateApproximateLayout = false; Layout approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout); if (couldNotCreateApproximateLayout) { @@ -192,18 +190,13 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height(); KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); - bool singleLine = approximateOutputWidth + inputWidth < maxWidth; if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) { - if (singleLine && !allExpressionsInline) { + bool singleLine = allExpressionsInline || ((approximateOutputWidth + inputWidth) < maxWidth); // TODO LEA 2 + if (singleLine) { KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline) + singleMargin; } else { - if (allExpressionsInline) { - KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline); - } else { - result = inputHeight + approximateOutputHeight + doubleMargin; - } + result = inputHeight + approximateOutputHeight + doubleMargin; } } else { assert(displayOutput(context) == DisplayOutput::ExactAndApproximate || (displayOutput(context) == DisplayOutput::ExactAndApproximateToggle && expanded)); @@ -211,17 +204,13 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre KDCoordinate exactOutputBaseline = exactLayout.baseline(); KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); - singleLine = exactOutputWidth + approximateOutputWidth + inputWidth < maxWidth - 30; // the 30 represents the = sign (example: sin(30)) KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - if (singleLine && !allExpressionsInline) { + bool singleLine = allExpressionsInline || ((inputWidth + exactOutputWidth + approximateOutputWidth) < (maxWidth - 30)); // the 30 represents the = sign (example: sin(30)) TODO LEA + if (singleLine) { result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)) + singleMargin; } else { - if (allExpressionsInline) { - result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)); - } else { - KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) + std::max(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline); - result = inputHeight + outputHeight + doubleMargin; - } + KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) + std::max(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline); + result = inputHeight + outputHeight + doubleMargin; } } } From 7ccaf3fc507bd1d308d639b2e5d5272cc391581b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Fri, 15 May 2020 14:16:10 +0200 Subject: [PATCH 65/85] [apps/calculation] Rename allExpressionsInline to forceSingleLine --- apps/calculation/calculation.cpp | 8 ++++---- apps/calculation/calculation.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index bf0e55518..b5da53efd 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -124,7 +124,7 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul } } -KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpressionsInline) { +KDCoordinate Calculation::height(Context * context, bool expanded, bool forceSingleLine) { KDCoordinate result = expanded ? m_expandedHeight : m_height; if (result >= 0) { // Height already computed @@ -160,7 +160,7 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre KDCoordinate exactOutputHeight = exactLayout.layoutSize().height(); KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); - bool singleLine = allExpressionsInline || ((exactOutputWidth + inputWidth) < maxWidth - 2); //TODO LEA 2 + bool singleLine = forceSingleLine || ((exactOutputWidth + inputWidth) < maxWidth - 2); //TODO LEA 2 if (singleLine) { KDCoordinate exactOutputBaseline = exactLayout.baseline(); @@ -191,7 +191,7 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height(); KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) { - bool singleLine = allExpressionsInline || ((approximateOutputWidth + inputWidth) < maxWidth); // TODO LEA 2 + bool singleLine = forceSingleLine || ((approximateOutputWidth + inputWidth) < maxWidth); // TODO LEA 2 if (singleLine) { KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline) + singleMargin; @@ -205,7 +205,7 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool allExpre KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - bool singleLine = allExpressionsInline || ((inputWidth + exactOutputWidth + approximateOutputWidth) < (maxWidth - 30)); // the 30 represents the = sign (example: sin(30)) TODO LEA + bool singleLine = forceSingleLine || ((inputWidth + exactOutputWidth + approximateOutputWidth) < (maxWidth - 30)); // the 30 represents the = sign (example: sin(30)) TODO LEA if (singleLine) { result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)) + singleMargin; } else { diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index 203d56568..9746768a6 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -84,7 +84,7 @@ public: Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout); // Memoization of height - KDCoordinate height(Poincare::Context * context, bool expanded = false, bool allExpressionsInline = false); + KDCoordinate height(Poincare::Context * context, bool expanded = false, bool forceSingleLine = false); // Displayed output DisplayOutput displayOutput(Poincare::Context * context); From 525290427a209c1b207779d49c66141f1ff8be78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 18 May 2020 11:42:59 +0200 Subject: [PATCH 66/85] [apps/calculation] Calculation::height takes verticalMargin argument --- .../illustrated_list_controller.cpp | 2 +- apps/calculation/calculation.cpp | 29 +++++++++++-------- apps/calculation/calculation.h | 2 +- apps/calculation/history_controller.cpp | 2 +- apps/calculation/history_view_cell.h | 2 ++ escher/include/escher/metric.h | 4 +-- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/apps/calculation/additional_outputs/illustrated_list_controller.cpp b/apps/calculation/additional_outputs/illustrated_list_controller.cpp index 80ea4f52a..432bf49c1 100644 --- a/apps/calculation/additional_outputs/illustrated_list_controller.cpp +++ b/apps/calculation/additional_outputs/illustrated_list_controller.cpp @@ -78,7 +78,7 @@ KDCoordinate IllustratedListController::rowHeight(int j) { return 0; } Shared::ExpiringPointer calculation = m_calculationStore.calculationAtIndex(calculationIndex); - return calculation->height(App::app()->localContext(), true, true) + 2 * Metric::CommonSmallMargin + Metric::CellSeparatorThickness; + return calculation->height(App::app()->localContext(), Metric::CommonSmallMargin, true, true) + Metric::CellSeparatorThickness; } int IllustratedListController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index b5da53efd..22c84be74 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -124,7 +124,7 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul } } -KDCoordinate Calculation::height(Context * context, bool expanded, bool forceSingleLine) { +KDCoordinate Calculation::height(Context * context, float verticalMargin, bool expanded, bool forceSingleLine) { KDCoordinate result = expanded ? m_expandedHeight : m_height; if (result >= 0) { // Height already computed @@ -135,8 +135,6 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool forceSin Layout inputLayout = createInputLayout(); KDCoordinate inputHeight = inputLayout.layoutSize().height(); KDCoordinate inputWidth = inputLayout.layoutSize().width(); - float singleMargin = 2 * Metric::CommonSmallMargin; - float doubleMargin = 4 * Metric::CommonSmallMargin; KDCoordinate inputBaseline = inputLayout.baseline(); // Get exact output height if needed @@ -159,14 +157,13 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool forceSin if (displayOutput(context) == DisplayOutput::ExactOnly) { KDCoordinate exactOutputHeight = exactLayout.layoutSize().height(); KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); - bool singleLine = forceSingleLine || ((exactOutputWidth + inputWidth) < maxWidth - 2); //TODO LEA 2 - if (singleLine) { KDCoordinate exactOutputBaseline = exactLayout.baseline(); - result = std::max(inputBaseline, exactOutputBaseline) + std::max(inputHeight - inputBaseline, exactOutputHeight-exactOutputBaseline) + singleMargin; + result = std::max(inputBaseline, exactOutputBaseline) // Above the baseline + + std::max(inputHeight - inputBaseline, exactOutputHeight - exactOutputBaseline); // Below the baseline } else { - result = inputHeight + exactOutputHeight + doubleMargin; + result = inputHeight + verticalMargin + exactOutputHeight; } } else { // Create the approximate output layout @@ -194,9 +191,10 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool forceSin bool singleLine = forceSingleLine || ((approximateOutputWidth + inputWidth) < maxWidth); // TODO LEA 2 if (singleLine) { KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - result = std::max(inputBaseline, approximateOutputBaseline) + std::max(inputHeight - inputBaseline, approximateOutputHeight-approximateOutputBaseline) + singleMargin; + result = std::max(inputBaseline, approximateOutputBaseline) // Above the baseline + + std::max(inputHeight - inputBaseline, approximateOutputHeight - approximateOutputBaseline); // Below the baseline } else { - result = inputHeight + approximateOutputHeight + doubleMargin; + result = inputHeight + verticalMargin + approximateOutputHeight; } } else { assert(displayOutput(context) == DisplayOutput::ExactAndApproximate || (displayOutput(context) == DisplayOutput::ExactAndApproximateToggle && expanded)); @@ -207,14 +205,21 @@ KDCoordinate Calculation::height(Context * context, bool expanded, bool forceSin KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); bool singleLine = forceSingleLine || ((inputWidth + exactOutputWidth + approximateOutputWidth) < (maxWidth - 30)); // the 30 represents the = sign (example: sin(30)) TODO LEA if (singleLine) { - result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) + std::max(inputHeight - inputBaseline, std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline)) + singleMargin; + result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) // Above the baseline + + std::max(inputHeight - inputBaseline, // Beloxw the baseline + std::max(exactOutputHeight - exactOutputBaseline, + approximateOutputHeight - approximateOutputBaseline)); } else { - KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) + std::max(exactOutputHeight-exactOutputBaseline, approximateOutputHeight-approximateOutputBaseline); - result = inputHeight + outputHeight + doubleMargin; + KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) // Above the baseline + + std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight - approximateOutputBaseline); // Below the baseline + result = inputHeight + verticalMargin + outputHeight; } } } + // Add the top and bottom margins + result += 2 * verticalMargin; + /* For all display outputs except ExactAndApproximateToggle, the selected * height and the usual height are identical. We update both heights in * theses cases. */ diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index 9746768a6..767fc1e67 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -84,7 +84,7 @@ public: Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout); // Memoization of height - KDCoordinate height(Poincare::Context * context, bool expanded = false, bool forceSingleLine = false); + KDCoordinate height(Poincare::Context * context, float verticalMargin, bool expanded, bool forceSingleLine); // Displayed output DisplayOutput displayOutput(Poincare::Context * context); diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 292a6199e..7d763ec22 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -208,7 +208,7 @@ KDCoordinate HistoryController::rowHeight(int j) { return 0; } Shared::ExpiringPointer calculation = calculationAtIndex(j); - return calculation->height(App::app()->localContext(), j == selectedRow() && selectedSubviewType() == SubviewType::Output); + return calculation->height(App::app()->localContext(), HistoryViewCell::k_verticalMargin, j == selectedRow() && selectedSubviewType() == SubviewType::Output, false); } int HistoryController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index 7d6c12b45..e4e4d8da8 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -31,6 +31,7 @@ private: class HistoryViewCell : public ::EvenOddCell, public Responder { public: + constexpr static KDCoordinate k_verticalMargin = Metric::CommonLargeMargin; //TODO LEA same as k_horizontalMargin? HistoryViewCell(Responder * parentResponder = nullptr); void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType = HistoryViewCellDataSource::SubviewType::None); void setEven(bool even) override; @@ -52,6 +53,7 @@ public: Shared::ScrollableTwoExpressionsView * outputView(); Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; } private: + constexpr static KDCoordinate k_horizontalMargin = Metric::CommonSmallMargin; constexpr static KDCoordinate k_resultWidth = 80; void reloadScroll(); void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType); diff --git a/escher/include/escher/metric.h b/escher/include/escher/metric.h index 0ac573c96..f514cb68c 100644 --- a/escher/include/escher/metric.h +++ b/escher/include/escher/metric.h @@ -10,8 +10,8 @@ public: constexpr static KDCoordinate CommonRightMargin = 20; constexpr static KDCoordinate CommonTopMargin = 15; constexpr static KDCoordinate CommonBottomMargin = 15; - constexpr static KDCoordinate CommonLargeMargin = 12; - constexpr static KDCoordinate CommonSmallMargin = 12; + constexpr static KDCoordinate CommonLargeMargin = 10; + constexpr static KDCoordinate CommonSmallMargin = 5; constexpr static KDCoordinate TitleBarExternHorizontalMargin = 5; constexpr static KDCoordinate TitleBarHeight = 18; constexpr static KDCoordinate ParameterCellHeight = 35; From 30c243e2e4a05cc017d2e4b2aa4afb78c9b2922b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 18 May 2020 11:45:54 +0200 Subject: [PATCH 67/85] [apps/scrollable_multiple_expressions_view] Factorize baseline() --- .../scrollable_multiple_expressions_view.cpp | 96 +++++++++++++------ .../scrollable_multiple_expressions_view.h | 11 +-- 2 files changed, 71 insertions(+), 36 deletions(-) diff --git a/apps/shared/scrollable_multiple_expressions_view.cpp b/apps/shared/scrollable_multiple_expressions_view.cpp index c86630143..044fad871 100644 --- a/apps/shared/scrollable_multiple_expressions_view.cpp +++ b/apps/shared/scrollable_multiple_expressions_view.cpp @@ -55,26 +55,36 @@ void AbstractScrollableMultipleExpressionsView::ContentCell::reloadTextColor() { } KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOptimalDisplay() const { - KDSize leftSize = KDSizeZero; - KDCoordinate leftViewBaseline = 0; KDCoordinate width = 0; + + // Compute baselines + KDCoordinate leftBaseline = 0; + KDCoordinate centerBaseline = 0; + KDCoordinate rightBaseline = 0; + KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline); + + KDSize leftSize = KDSizeZero; if (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) { leftSize = leftExpressionView()->minimalSizeForOptimalDisplay(); - leftViewBaseline = leftExpressionView()->layout().baseline(); width += leftSize.width() + Metric::CommonLargeMargin; } - KDSize rightExpressionSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); - width += rightExpressionSize.width(); - Layout l = m_rightExpressionView.layout(); - KDCoordinate rightBaseline = l.isUninitialized() ? 0 : l.baseline(); - KDSize centeredExpressionSize = KDSizeZero; - KDCoordinate centeredBaseline = 0; + + KDSize centerSize = KDSizeZero; if (displayCenter()) { - centeredBaseline = m_centeredExpressionView.layout().baseline(); - centeredExpressionSize = m_centeredExpressionView.minimalSizeForOptimalDisplay(); - width += centeredExpressionSize.width() + 2*Metric::CommonLargeMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); + centerSize = m_centeredExpressionView.minimalSizeForOptimalDisplay(); + width += centerSize.width() + 2 * Metric::CommonLargeMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); } - KDCoordinate height = std::max(std::max(centeredBaseline, rightBaseline), leftViewBaseline) + std::max(std::max(centeredExpressionSize.height()-centeredBaseline, rightExpressionSize.height()-rightBaseline), leftSize.height()-leftViewBaseline); + + KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); + width += rightSize.width(); + + KDCoordinate height = viewBaseline + + std::max( + std::max( + centerSize.height() - centerBaseline, + rightSize.height() - rightBaseline), + leftSize.height() - leftBaseline); + return KDSize(width, height); } @@ -111,6 +121,32 @@ int AbstractScrollableMultipleExpressionsView::ContentCell::numberOfSubviews() c return nbOfSubviews; } +KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::baseline(KDCoordinate * leftBaseline, KDCoordinate * centerBaseline, KDCoordinate * rightBaseline) const { + // Left view + KDCoordinate leftViewBaseline = (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) ? + leftExpressionView()->layout().baseline() : + 0; + if (leftBaseline != nullptr) { + *leftBaseline = leftViewBaseline; + } + + // Center view + KDCoordinate centerViewBaseline = displayCenter() ? m_centeredExpressionView.layout().baseline() : 0; + if (centerBaseline != nullptr) { + *centerBaseline = centerViewBaseline; + } + + // Right view + KDCoordinate rightViewBaseline = m_rightExpressionView.layout().isUninitialized() ? + 0 : + m_rightExpressionView.layout().baseline(); + if (rightBaseline != nullptr) { + *rightBaseline = rightViewBaseline; + } + + return std::max(std::max(leftViewBaseline, centerViewBaseline), rightViewBaseline); +} + View * AbstractScrollableMultipleExpressionsView::ContentCell::subviewAtIndex(int index) { bool leftIsVisible = leftExpressionView() != nullptr; if (leftIsVisible && index == 0) { @@ -123,33 +159,33 @@ View * AbstractScrollableMultipleExpressionsView::ContentCell::subviewAtIndex(in void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool force) { // Subviews sizes KDSize leftSize = leftExpressionView() ? leftExpressionView()->minimalSizeForOptimalDisplay() : KDSizeZero; - KDCoordinate leftViewBaseline = leftExpressionView() && !leftExpressionView()->layout().isUninitialized() ? leftExpressionView()->layout().baseline() : 0; - KDSize centeredExpressionSize = KDSizeZero; - KDCoordinate centeredBaseline = 0; - if (displayCenter()) { - centeredBaseline = m_centeredExpressionView.layout().baseline(); - centeredExpressionSize = m_centeredExpressionView.minimalSizeForOptimalDisplay(); - } - KDSize rightExpressionSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); - KDCoordinate rightBaseline = m_rightExpressionView.layout().isUninitialized() ? 0 : m_rightExpressionView.layout().baseline(); - // Compute baseline - KDCoordinate baseline = std::max(std::max(leftViewBaseline, rightBaseline), centeredBaseline); + KDSize centerSize = displayCenter() ? m_centeredExpressionView.minimalSizeForOptimalDisplay() : KDSizeZero; + KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); + + // Compute baselines + KDCoordinate leftBaseline = 0; + KDCoordinate centerBaseline = 0; + KDCoordinate rightBaseline = 0; + KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline); + // Layout left view KDCoordinate currentWidth = 0; if (leftExpressionView()) { - leftExpressionView()->setFrame(KDRect(currentWidth, baseline-leftViewBaseline, leftSize), force); + leftExpressionView()->setFrame(KDRect(currentWidth, viewBaseline - leftBaseline, leftSize), force); currentWidth += leftSize.width() + Metric::CommonLargeMargin; } - // Layout centered expression + + // Layout center expression if (displayCenter()) { KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); - m_centeredExpressionView.setFrame(KDRect(currentWidth, baseline-centeredBaseline, centeredExpressionSize), force); - currentWidth += Metric::CommonLargeMargin+centeredExpressionSize.width(); - m_approximateSign.setFrame(KDRect(currentWidth, baseline-approximateSignSize.height()/2, approximateSignSize), force); + m_centeredExpressionView.setFrame(KDRect(currentWidth, viewBaseline - centerBaseline, centerSize), force); + currentWidth += Metric::CommonLargeMargin + centerSize.width(); + m_approximateSign.setFrame(KDRect(currentWidth, viewBaseline - approximateSignSize.height()/2, approximateSignSize), force); currentWidth += Metric::CommonLargeMargin + approximateSignSize.width(); } + // Layout right expression - m_rightExpressionView.setFrame(KDRect(currentWidth, baseline-rightBaseline, rightExpressionSize), force); + m_rightExpressionView.setFrame(KDRect(currentWidth, viewBaseline - rightBaseline, rightSize), force); } AbstractScrollableMultipleExpressionsView::AbstractScrollableMultipleExpressionsView(Responder * parentResponder, View * contentCell) : diff --git a/apps/shared/scrollable_multiple_expressions_view.h b/apps/shared/scrollable_multiple_expressions_view.h index 2a561ac41..d95d209fc 100644 --- a/apps/shared/scrollable_multiple_expressions_view.h +++ b/apps/shared/scrollable_multiple_expressions_view.h @@ -28,9 +28,8 @@ public: void setDisplayCenter(bool display); void reloadScroll(); bool handleEvent(Ion::Events::Event event) override; - Poincare::Layout layout() const { - return constContentCell()->layout(); - } + Poincare::Layout layout() const { return constContentCell()->layout(); } + KDCoordinate baseline() const { return constContentCell()->baseline(); } protected: class ContentCell : public ::EvenOddCell { public: @@ -59,7 +58,7 @@ protected: void layoutSubviews(bool force = false) override; int numberOfSubviews() const override; virtual Poincare::Layout layout() const override; - + KDCoordinate baseline(KDCoordinate * leftBaseline = nullptr, KDCoordinate * centerBaseline = nullptr, KDCoordinate * rightBaseline = nullptr) const; private: View * subviewAtIndex(int index) override; ExpressionView m_rightExpressionView; @@ -68,8 +67,8 @@ protected: SubviewPosition m_selectedSubviewPosition; bool m_displayCenter; }; - virtual ContentCell * contentCell() = 0; - virtual const ContentCell * constContentCell() const = 0; + virtual ContentCell * contentCell() = 0; + virtual const ContentCell * constContentCell() const = 0; }; class ScrollableTwoExpressionsView : public AbstractScrollableMultipleExpressionsView { From 1c2801907f06c496c3718ab790de43e89bb5aa1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 18 May 2020 12:03:41 +0200 Subject: [PATCH 68/85] [apps/calculation] Factorize and clean code --- apps/calculation/history_view_cell.cpp | 32 ++++++++++++++++++-------- apps/calculation/history_view_cell.h | 2 +- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 0d3c84da3..43251acc8 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -179,18 +179,32 @@ void HistoryViewCell::layoutSubviews(bool force) { } KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay(); KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); - int singleLine = outputSize.width() + inputSize.width() < Ion::Display::Width - (Metric::CommonSmallMargin * 2) - Metric::EllipsisCellWidth; - int inputHeight = (singleLine && inputSize.height() < outputSize.height()) ? (outputSize.height() - inputSize.height()) : 0; + bool singleLine = (inputSize.width() + k_margin + outputSize.width()) < bounds().width() - Metric::EllipsisCellWidth; // k_margin the separation between the input and output. inputSize and outputSize already handle their left and right margins TODO LEA factorize singleLine() + + KDCoordinate inputY = k_margin; + KDCoordinate outputY = k_margin; + if (singleLine) { + KDCoordinate inputBaseline = m_inputView.layout().baseline(); + KDCoordinate outputBaseline = m_scrollableOutputView.baseline(); + KDCoordinate baselineDifference = outputBaseline - inputBaseline; + if (baselineDifference > 0) { + inputY += baselineDifference; + } else { + outputY += -baselineDifference; + } + } else { + outputY += inputSize.height(); + } + m_inputView.setFrame(KDRect( - 0, - inputHeight, - std::min(maxFrameWidth, inputSize.width()), - inputSize.height()), - force); - int outputHeight = singleLine ? std::max(0, inputSize.height() - outputSize.height()) / 2 + std::max(0, (inputSize.height() - outputSize.height()) / 2) + 1 : inputSize.height(); + 0, + inputY, + std::min(maxFrameWidth, inputSize.width()), + inputSize.height()), + force); m_scrollableOutputView.setFrame(KDRect( std::max(0, maxFrameWidth - outputSize.width()), - outputHeight, + outputY, std::min(maxFrameWidth, outputSize.width()), outputSize.height()), force); diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index e4e4d8da8..0df307b5e 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -53,7 +53,7 @@ public: Shared::ScrollableTwoExpressionsView * outputView(); Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; } private: - constexpr static KDCoordinate k_horizontalMargin = Metric::CommonSmallMargin; + constexpr static KDCoordinate k_margin = Metric::CommonSmallMargin; constexpr static KDCoordinate k_resultWidth = 80; void reloadScroll(); void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType); From 78cac6ddaeef199ba1ec261c7a98b37761d1d885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 19 May 2020 10:45:14 +0200 Subject: [PATCH 69/85] [apps/calculation] Calculation::height has CanBeSingleLine argument --- .../additional_outputs/illustrated_list_controller.cpp | 2 +- apps/calculation/calculation.cpp | 8 ++++---- apps/calculation/calculation.h | 4 ++-- apps/calculation/history_controller.cpp | 2 +- apps/calculation/history_view_cell.cpp | 8 +++++++- apps/calculation/history_view_cell.h | 1 + 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/calculation/additional_outputs/illustrated_list_controller.cpp b/apps/calculation/additional_outputs/illustrated_list_controller.cpp index 432bf49c1..e8b3fbb8d 100644 --- a/apps/calculation/additional_outputs/illustrated_list_controller.cpp +++ b/apps/calculation/additional_outputs/illustrated_list_controller.cpp @@ -78,7 +78,7 @@ KDCoordinate IllustratedListController::rowHeight(int j) { return 0; } Shared::ExpiringPointer calculation = m_calculationStore.calculationAtIndex(calculationIndex); - return calculation->height(App::app()->localContext(), Metric::CommonSmallMargin, true, true) + Metric::CellSeparatorThickness; + return calculation->height(App::app()->localContext(), Metric::CommonSmallMargin, true, true) + Metric::CellSeparatorThickness; //TODO LEA } int IllustratedListController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 22c84be74..0ec02edb3 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -124,7 +124,7 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul } } -KDCoordinate Calculation::height(Context * context, float verticalMargin, bool expanded, bool forceSingleLine) { +KDCoordinate Calculation::height(Context * context, float verticalMargin, bool expanded, bool forceSingleLine, CanBeSingleLineFunction canBeSingleLine) { KDCoordinate result = expanded ? m_expandedHeight : m_height; if (result >= 0) { // Height already computed @@ -157,7 +157,7 @@ KDCoordinate Calculation::height(Context * context, float verticalMargin, bool e if (displayOutput(context) == DisplayOutput::ExactOnly) { KDCoordinate exactOutputHeight = exactLayout.layoutSize().height(); KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); - bool singleLine = forceSingleLine || ((exactOutputWidth + inputWidth) < maxWidth - 2); //TODO LEA 2 + bool singleLine = forceSingleLine || canBeSingleLine(inputWidth, exactOutputWidth); if (singleLine) { KDCoordinate exactOutputBaseline = exactLayout.baseline(); result = std::max(inputBaseline, exactOutputBaseline) // Above the baseline @@ -188,7 +188,7 @@ KDCoordinate Calculation::height(Context * context, float verticalMargin, bool e KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height(); KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) { - bool singleLine = forceSingleLine || ((approximateOutputWidth + inputWidth) < maxWidth); // TODO LEA 2 + bool singleLine = forceSingleLine || canBeSingleLine(inputWidth, approximateOutputWidth); if (singleLine) { KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); result = std::max(inputBaseline, approximateOutputBaseline) // Above the baseline @@ -203,7 +203,7 @@ KDCoordinate Calculation::height(Context * context, float verticalMargin, bool e KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - bool singleLine = forceSingleLine || ((inputWidth + exactOutputWidth + approximateOutputWidth) < (maxWidth - 30)); // the 30 represents the = sign (example: sin(30)) TODO LEA + bool singleLine = forceSingleLine || canBeSingleLine(inputWidth, exactOutputWidth + /*TODO LEA equal sign " = "*/ + approximateOutputWidth); if (singleLine) { result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) // Above the baseline + std::max(inputHeight - inputBaseline, // Beloxw the baseline diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index 767fc1e67..ec7dc3895 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -84,7 +84,8 @@ public: Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout); // Memoization of height - KDCoordinate height(Poincare::Context * context, float verticalMargin, bool expanded, bool forceSingleLine); + typedef bool (*CanBeSingleLineFunction)(KDCoordinate inputWidth, KDCoordinate outputWidth); + KDCoordinate height(Poincare::Context * context, float verticalMargin, bool expanded, bool forceSingleLine, CanBeSingleLineFunction canbeSingleLine = [](KDCoordinate inputWidth, KDCoordinate outputWidth) { assert(false); return true; }); // Displayed output DisplayOutput displayOutput(Poincare::Context * context); @@ -95,7 +96,6 @@ public: // Additional Information AdditionalInformationType additionalInformationType(Poincare::Context * context); private: - static constexpr int maxWidth = Ion::Display::Width - (Metric::CommonSmallMargin * 2) - Metric::EllipsisCellWidth - 48; // 48 is the difference history_view_cell's width and calculation's static constexpr int k_numberOfExpressions = 4; static constexpr KDCoordinate k_heightComputationFailureHeight = 50; static constexpr const char * k_maximalIntegerWithAdditionalInformation = "10000000000000000"; diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 7d763ec22..18669503f 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -208,7 +208,7 @@ KDCoordinate HistoryController::rowHeight(int j) { return 0; } Shared::ExpiringPointer calculation = calculationAtIndex(j); - return calculation->height(App::app()->localContext(), HistoryViewCell::k_verticalMargin, j == selectedRow() && selectedSubviewType() == SubviewType::Output, false); + return calculation->height(App::app()->localContext(), HistoryViewCell::k_verticalMargin, j == selectedRow() && selectedSubviewType() == SubviewType::Output, false, &HistoryViewCell::CanBeSingleLine); } int HistoryController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 43251acc8..8ea49b677 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -169,6 +169,11 @@ View * HistoryViewCell::subviewAtIndex(int index) { return views[index]; } +bool HistoryViewCell::CanBeSingleLine(KDCoordinate inputWidth, KDCoordinate outputWidth) { + // k_margin is the separation between the input and output. + return (inputWidth + k_margin + outputWidth) < (Ion::Display::Width - Metric::EllipsisCellWidth - 2 * k_margin); +} + void HistoryViewCell::layoutSubviews(bool force) { KDCoordinate maxFrameWidth = bounds().width(); if (displayedEllipsis()) { @@ -179,7 +184,8 @@ void HistoryViewCell::layoutSubviews(bool force) { } KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay(); KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); - bool singleLine = (inputSize.width() + k_margin + outputSize.width()) < bounds().width() - Metric::EllipsisCellWidth; // k_margin the separation between the input and output. inputSize and outputSize already handle their left and right margins TODO LEA factorize singleLine() + + bool singleLine = CanBeSingleLine(inputSize.width(), outputSize.width()); KDCoordinate inputY = k_margin; KDCoordinate outputY = k_margin; diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index 0df307b5e..a47b43a4d 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -33,6 +33,7 @@ class HistoryViewCell : public ::EvenOddCell, public Responder { public: constexpr static KDCoordinate k_verticalMargin = Metric::CommonLargeMargin; //TODO LEA same as k_horizontalMargin? HistoryViewCell(Responder * parentResponder = nullptr); + static bool CanBeSingleLine(KDCoordinate inputWidth, KDCoordinate outputWidth); void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType = HistoryViewCellDataSource::SubviewType::None); void setEven(bool even) override; void setHighlighted(bool highlight) override; From 11c39b6206da8ff2c079fc59c8acb3691738966a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 19 May 2020 14:17:26 +0200 Subject: [PATCH 70/85] [apps/calculation] Calculation::height has two types of margin arguments One for the margin between layouts and on top / at the bottom of the cell, the other for the margin surrounding each layout. --- .../illustrated_list_controller.cpp | 2 +- .../scrollable_three_expressions_cell.h | 3 ++- apps/calculation/calculation.cpp | 22 +++++++++++-------- apps/calculation/calculation.h | 2 +- apps/calculation/history_controller.cpp | 8 ++++++- apps/calculation/history_view_cell.cpp | 4 ++-- apps/calculation/history_view_cell.h | 4 ++-- .../scrollable_multiple_expressions_view.cpp | 20 ++++++++++------- .../scrollable_multiple_expressions_view.h | 10 +++++++++ 9 files changed, 50 insertions(+), 25 deletions(-) diff --git a/apps/calculation/additional_outputs/illustrated_list_controller.cpp b/apps/calculation/additional_outputs/illustrated_list_controller.cpp index e8b3fbb8d..ba792c161 100644 --- a/apps/calculation/additional_outputs/illustrated_list_controller.cpp +++ b/apps/calculation/additional_outputs/illustrated_list_controller.cpp @@ -78,7 +78,7 @@ KDCoordinate IllustratedListController::rowHeight(int j) { return 0; } Shared::ExpiringPointer calculation = m_calculationStore.calculationAtIndex(calculationIndex); - return calculation->height(App::app()->localContext(), Metric::CommonSmallMargin, true, true) + Metric::CellSeparatorThickness; //TODO LEA + return calculation->height(App::app()->localContext(), Metric::CommonSmallMargin, ScrollableThreeExpressionsView::k_margin, true, true) + Metric::CellSeparatorThickness; } int IllustratedListController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h index 42c18becb..a7836d7f7 100644 --- a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h +++ b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h @@ -10,8 +10,9 @@ namespace Calculation { class ScrollableThreeExpressionsView : public Shared::AbstractScrollableMultipleExpressionsView { public: + static constexpr KDCoordinate k_margin = Metric::CommonSmallMargin; ScrollableThreeExpressionsView(Responder * parentResponder) : Shared::AbstractScrollableMultipleExpressionsView(parentResponder, &m_contentCell), m_contentCell() { - setMargins(Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin, Metric::CommonSmallMargin); // Left Right margins are already added by TableCell + setMargins(k_margin, k_margin, k_margin, k_margin); // Left Right margins are already added by TableCell setBackgroundColor(KDColorWhite); } void resetMemoization(); diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 0ec02edb3..3e8d69107 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -1,5 +1,6 @@ #include "calculation.h" #include "../shared/poincare_helpers.h" +#include "../shared/scrollable_multiple_expressions_view.h" #include "../global_preferences.h" #include "../exam_mode_configuration.h" #include @@ -124,7 +125,7 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul } } -KDCoordinate Calculation::height(Context * context, float verticalMargin, bool expanded, bool forceSingleLine, CanBeSingleLineFunction canBeSingleLine) { +KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginBetweenLayouts, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, CanBeSingleLineFunction canBeSingleLine) { KDCoordinate result = expanded ? m_expandedHeight : m_height; if (result >= 0) { // Height already computed @@ -161,9 +162,10 @@ KDCoordinate Calculation::height(Context * context, float verticalMargin, bool e if (singleLine) { KDCoordinate exactOutputBaseline = exactLayout.baseline(); result = std::max(inputBaseline, exactOutputBaseline) // Above the baseline - + std::max(inputHeight - inputBaseline, exactOutputHeight - exactOutputBaseline); // Below the baseline + + std::max(inputHeight - inputBaseline, exactOutputHeight - exactOutputBaseline) // Below the baseline + + 2 * verticalMarginAroundLayouts; } else { - result = inputHeight + verticalMargin + exactOutputHeight; + result = inputHeight + verticalMarginBetweenLayouts + exactOutputHeight + 4 * verticalMarginAroundLayouts; } } else { // Create the approximate output layout @@ -192,9 +194,10 @@ KDCoordinate Calculation::height(Context * context, float verticalMargin, bool e if (singleLine) { KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); result = std::max(inputBaseline, approximateOutputBaseline) // Above the baseline - + std::max(inputHeight - inputBaseline, approximateOutputHeight - approximateOutputBaseline); // Below the baseline + + std::max(inputHeight - inputBaseline, approximateOutputHeight - approximateOutputBaseline) // Below the baseline + + 2 * verticalMarginAroundLayouts; } else { - result = inputHeight + verticalMargin + approximateOutputHeight; + result = inputHeight + verticalMarginBetweenLayouts + approximateOutputHeight + 4 * verticalMarginAroundLayouts; } } else { assert(displayOutput(context) == DisplayOutput::ExactAndApproximate || (displayOutput(context) == DisplayOutput::ExactAndApproximateToggle && expanded)); @@ -203,22 +206,23 @@ KDCoordinate Calculation::height(Context * context, float verticalMargin, bool e KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - bool singleLine = forceSingleLine || canBeSingleLine(inputWidth, exactOutputWidth + /*TODO LEA equal sign " = "*/ + approximateOutputWidth); + bool singleLine = forceSingleLine || canBeSingleLine(inputWidth, exactOutputWidth + AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize() + approximateOutputWidth); if (singleLine) { result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) // Above the baseline + std::max(inputHeight - inputBaseline, // Beloxw the baseline std::max(exactOutputHeight - exactOutputBaseline, - approximateOutputHeight - approximateOutputBaseline)); + approximateOutputHeight - approximateOutputBaseline)) + + 2 * verticalMarginAroundLayouts; } else { KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) // Above the baseline + std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight - approximateOutputBaseline); // Below the baseline - result = inputHeight + verticalMargin + outputHeight; + result = inputHeight + verticalMarginBetweenLayouts + outputHeight + 4 * verticalMarginAroundLayouts; } } } // Add the top and bottom margins - result += 2 * verticalMargin; + result += 2 * verticalMarginBetweenLayouts; /* For all display outputs except ExactAndApproximateToggle, the selected * height and the usual height are identical. We update both heights in diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index ec7dc3895..a1685131c 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -85,7 +85,7 @@ public: // Memoization of height typedef bool (*CanBeSingleLineFunction)(KDCoordinate inputWidth, KDCoordinate outputWidth); - KDCoordinate height(Poincare::Context * context, float verticalMargin, bool expanded, bool forceSingleLine, CanBeSingleLineFunction canbeSingleLine = [](KDCoordinate inputWidth, KDCoordinate outputWidth) { assert(false); return true; }); + KDCoordinate height(Poincare::Context * context, KDCoordinate verticalMarginBetweenLayouts, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, CanBeSingleLineFunction canbeSingleLine = [](KDCoordinate inputWidth, KDCoordinate outputWidth) { assert(false); return true; }); // Displayed output DisplayOutput displayOutput(Poincare::Context * context); diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 18669503f..005847dd8 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -208,7 +208,13 @@ KDCoordinate HistoryController::rowHeight(int j) { return 0; } Shared::ExpiringPointer calculation = calculationAtIndex(j); - return calculation->height(App::app()->localContext(), HistoryViewCell::k_verticalMargin, j == selectedRow() && selectedSubviewType() == SubviewType::Output, false, &HistoryViewCell::CanBeSingleLine); + return calculation->height( + App::app()->localContext(), + HistoryViewCell::k_margin, + HistoryViewCell::k_inputOutputViewsVerticalMargin, + j == selectedRow() && selectedSubviewType() == SubviewType::Output, + false, + &HistoryViewCell::CanBeSingleLine); } int HistoryController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 8ea49b677..8c7c5cdaa 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -42,7 +42,7 @@ HistoryViewCell::HistoryViewCell(Responder * parentResponder) : m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown), m_calculationAdditionInformation(Calculation::AdditionalInformationType::None), m_calculationExpanded(false), - m_inputView(this, Metric::CommonLargeMargin, Metric::CommonSmallMargin), + m_inputView(this, Metric::CommonLargeMargin, k_inputOutputViewsVerticalMargin), m_scrollableOutputView(this) { m_calculationCRC32 = 0; @@ -171,7 +171,7 @@ View * HistoryViewCell::subviewAtIndex(int index) { bool HistoryViewCell::CanBeSingleLine(KDCoordinate inputWidth, KDCoordinate outputWidth) { // k_margin is the separation between the input and output. - return (inputWidth + k_margin + outputWidth) < (Ion::Display::Width - Metric::EllipsisCellWidth - 2 * k_margin); + return (inputWidth + k_margin + outputWidth) < (Ion::Display::Width - Metric::EllipsisCellWidth - 2 * k_margin); //TODO LEA -2 ?? } void HistoryViewCell::layoutSubviews(bool force) { diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index a47b43a4d..d4a4b00e6 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -31,7 +31,8 @@ private: class HistoryViewCell : public ::EvenOddCell, public Responder { public: - constexpr static KDCoordinate k_verticalMargin = Metric::CommonLargeMargin; //TODO LEA same as k_horizontalMargin? + constexpr static KDCoordinate k_margin = Metric::CommonSmallMargin; + constexpr static KDCoordinate k_inputOutputViewsVerticalMargin = k_margin; HistoryViewCell(Responder * parentResponder = nullptr); static bool CanBeSingleLine(KDCoordinate inputWidth, KDCoordinate outputWidth); void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType = HistoryViewCellDataSource::SubviewType::None); @@ -54,7 +55,6 @@ public: Shared::ScrollableTwoExpressionsView * outputView(); Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; } private: - constexpr static KDCoordinate k_margin = Metric::CommonSmallMargin; constexpr static KDCoordinate k_resultWidth = 80; void reloadScroll(); void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType); diff --git a/apps/shared/scrollable_multiple_expressions_view.cpp b/apps/shared/scrollable_multiple_expressions_view.cpp index 044fad871..3a4c1b652 100644 --- a/apps/shared/scrollable_multiple_expressions_view.cpp +++ b/apps/shared/scrollable_multiple_expressions_view.cpp @@ -8,7 +8,7 @@ namespace Shared { AbstractScrollableMultipleExpressionsView::ContentCell::ContentCell() : m_rightExpressionView(), - m_approximateSign(KDFont::LargeFont, I18n::Message::AlmostEqual, 0.5f, 0.5f, Palette::GreyVeryDark), + m_approximateSign(k_font, k_defaultApproximateMessage, 0.5f, 0.5f, Palette::GreyVeryDark), m_centeredExpressionView(), m_selectedSubviewPosition(SubviewPosition::Center), m_displayCenter(true) @@ -66,13 +66,13 @@ KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOpt KDSize leftSize = KDSizeZero; if (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) { leftSize = leftExpressionView()->minimalSizeForOptimalDisplay(); - width += leftSize.width() + Metric::CommonLargeMargin; + width += leftSize.width() + k_horizontalMargin; } KDSize centerSize = KDSizeZero; if (displayCenter()) { centerSize = m_centeredExpressionView.minimalSizeForOptimalDisplay(); - width += centerSize.width() + 2 * Metric::CommonLargeMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); + width += centerSize.width() + 2 * k_horizontalMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); } KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); @@ -156,6 +156,10 @@ View * AbstractScrollableMultipleExpressionsView::ContentCell::subviewAtIndex(in return views[index - leftIsVisible]; } +KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::StandardApproximateViewAndMarginsSize() { + return 2 * k_horizontalMargin + k_font->stringSize(I18n::translate(k_defaultApproximateMessage)).width(); //TODO LEA +} + void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool force) { // Subviews sizes KDSize leftSize = leftExpressionView() ? leftExpressionView()->minimalSizeForOptimalDisplay() : KDSizeZero; @@ -172,16 +176,16 @@ void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool KDCoordinate currentWidth = 0; if (leftExpressionView()) { leftExpressionView()->setFrame(KDRect(currentWidth, viewBaseline - leftBaseline, leftSize), force); - currentWidth += leftSize.width() + Metric::CommonLargeMargin; + currentWidth += leftSize.width() + k_horizontalMargin; } // Layout center expression if (displayCenter()) { KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); m_centeredExpressionView.setFrame(KDRect(currentWidth, viewBaseline - centerBaseline, centerSize), force); - currentWidth += Metric::CommonLargeMargin + centerSize.width(); + currentWidth += k_horizontalMargin + centerSize.width(); m_approximateSign.setFrame(KDRect(currentWidth, viewBaseline - approximateSignSize.height()/2, approximateSignSize), force); - currentWidth += Metric::CommonLargeMargin + approximateSignSize.width(); + currentWidth += k_horizontalMargin + approximateSignSize.width(); } // Layout right expression @@ -243,8 +247,8 @@ bool AbstractScrollableMultipleExpressionsView::handleEvent(Ion::Events::Event e if (contentCell()->displayCenter()) { KDCoordinate centerExpressionWidth = contentCell()->centeredExpressionView()->minimalSizeForOptimalDisplay().width(); KDCoordinate signWidth = contentCell()->approximateSign()->minimalSizeForOptimalDisplay().width(); - centeredExpressionIsVisibleOnTheLeft = leftWidth + Metric::CommonLargeMargin + centerExpressionWidth - contentOffset().x() > 0; - centeredExpressionIsVisibleOnTheRight = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - signWidth - centerExpressionWidth - 2*Metric::CommonLargeMargin - contentOffset().x() < bounds().width(); + centeredExpressionIsVisibleOnTheLeft = leftWidth + ContentCell::k_horizontalMargin + centerExpressionWidth - contentOffset().x() > 0; + centeredExpressionIsVisibleOnTheRight = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - signWidth - centerExpressionWidth - 2 * ContentCell::k_horizontalMargin - contentOffset().x() < bounds().width(); } // Select center if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && centeredExpressionIsVisibleOnTheLeft) || diff --git a/apps/shared/scrollable_multiple_expressions_view.h b/apps/shared/scrollable_multiple_expressions_view.h index d95d209fc..26eb85386 100644 --- a/apps/shared/scrollable_multiple_expressions_view.h +++ b/apps/shared/scrollable_multiple_expressions_view.h @@ -2,6 +2,7 @@ #define SHARED_SCROLLABLE_MULTIPLE_EXPRESSIONS_VIEW_H #include +#include namespace Shared { @@ -12,6 +13,11 @@ public: Center = 1, Right = 2 }; + + static KDCoordinate StandardApproximateViewAndMarginsSize() { + return ContentCell::StandardApproximateViewAndMarginsSize(); + } + AbstractScrollableMultipleExpressionsView(Responder * parentResponder, View * contentCell); ::EvenOddCell * evenOddCell() { return contentCell(); @@ -33,6 +39,8 @@ public: protected: class ContentCell : public ::EvenOddCell { public: + static KDCoordinate StandardApproximateViewAndMarginsSize(); + constexpr static KDCoordinate k_horizontalMargin = Metric::CommonLargeMargin; ContentCell(); KDColor backgroundColor() const override; void setHighlighted(bool highlight) override; @@ -60,6 +68,8 @@ protected: virtual Poincare::Layout layout() const override; KDCoordinate baseline(KDCoordinate * leftBaseline = nullptr, KDCoordinate * centerBaseline = nullptr, KDCoordinate * rightBaseline = nullptr) const; private: + constexpr static const KDFont * k_font = KDFont::LargeFont; + const static I18n::Message k_defaultApproximateMessage = I18n::Message::AlmostEqual; View * subviewAtIndex(int index) override; ExpressionView m_rightExpressionView; MessageTextView m_approximateSign; From 4f50a72abfbc833b1992909970b8fabb74e99d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 19 May 2020 15:22:40 +0200 Subject: [PATCH 71/85] [apps/calculation] Clean Calculation::height --- apps/calculation/calculation.cpp | 138 ++++++++++++++----------------- 1 file changed, 63 insertions(+), 75 deletions(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 3e8d69107..51a710d83 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -138,87 +138,75 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginB KDCoordinate inputWidth = inputLayout.layoutSize().width(); KDCoordinate inputBaseline = inputLayout.baseline(); - // Get exact output height if needed - Poincare::Layout exactLayout; - bool couldNotCreateExactLayout = false; - if (DisplaysExact(displayOutput(context))) { - // Create the exact output layout - exactLayout = createExactOutputLayout(&couldNotCreateExactLayout); - if (couldNotCreateExactLayout) { - if (displayOutput(context) != DisplayOutput::ExactOnly) { - forceDisplayOutput(DisplayOutput::ApproximateOnly); + KDCoordinate outputWidth; + KDCoordinate outputBaseline; + KDCoordinate outputHeightBelowBaseline; + + { + DisplayOutput displayType = displayOutput(context); + bool displaysExactOnly = displayType == DisplayOutput::ExactOnly; + bool displayApproximateOnly = displayType == DisplayOutput::ApproximateOnly + || (!expanded + && (displayType == DisplayOutput::ExactAndApproximateToggle)); + + KDCoordinate exactOutputWidth = 0; + KDCoordinate exactOutputBaseline = 0; + KDCoordinate exactOutputBelowBaseline = 0; + + // Get exact output info if needed + if (displaysExactOnly || !displayApproximateOnly) { + bool couldNotCreateExactLayout = false; + Poincare::Layout exactLayout = createExactOutputLayout(&couldNotCreateExactLayout); + if (couldNotCreateExactLayout) { + if (!displaysExactOnly) { + displayType = DisplayOutput::ApproximateOnly; + forceDisplayOutput(displayType); + displaysExactOnly = false; + displayApproximateOnly = true; + } else { + /* We should only display the exact result, but we cannot create it + * -> raise an exception. */ + ExceptionCheckpoint::Raise(); + } } else { - /* We should only display the exact result, but we cannot create it - * -> raise an exception. */ - ExceptionCheckpoint::Raise(); + KDSize exactSize = exactLayout.layoutSize(); + exactOutputWidth = exactSize.width(); + exactOutputBaseline = exactLayout.baseline(); + exactOutputBelowBaseline = exactSize.height() - exactOutputBaseline; } } + + KDCoordinate approximateOutputWidth = 0; + KDCoordinate approximateOutputBaseline = 0; + KDCoordinate approximateOutputBelowBaseline = 0; + + // Get approximate output info if needed + if (displayApproximateOnly || !displaysExactOnly) { + bool couldNotCreateApproximateLayout = false; + Layout approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout); + if (couldNotCreateApproximateLayout) { + Poincare::ExceptionCheckpoint::Raise(); + } + KDSize approximateOutputSize = approximateLayout.layoutSize(); + approximateOutputWidth = approximateOutputSize.width(); + approximateOutputBaseline = approximateLayout.baseline(); + approximateOutputBelowBaseline = approximateOutputSize.height() - approximateOutputBaseline; + } + + // Compute the output info + outputWidth = exactOutputWidth + + ((exactOutputWidth > 0 && approximateOutputWidth > 0) ? AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize() : 0) + + approximateOutputWidth; + outputBaseline = std::max(exactOutputBaseline, approximateOutputBaseline); + outputHeightBelowBaseline = std::max(exactOutputBelowBaseline, approximateOutputBelowBaseline); } - if (displayOutput(context) == DisplayOutput::ExactOnly) { - KDCoordinate exactOutputHeight = exactLayout.layoutSize().height(); - KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); - bool singleLine = forceSingleLine || canBeSingleLine(inputWidth, exactOutputWidth); - if (singleLine) { - KDCoordinate exactOutputBaseline = exactLayout.baseline(); - result = std::max(inputBaseline, exactOutputBaseline) // Above the baseline - + std::max(inputHeight - inputBaseline, exactOutputHeight - exactOutputBaseline) // Below the baseline - + 2 * verticalMarginAroundLayouts; - } else { - result = inputHeight + verticalMarginBetweenLayouts + exactOutputHeight + 4 * verticalMarginAroundLayouts; - } + if (forceSingleLine || canBeSingleLine(inputWidth, outputWidth)) { + result = std::max(inputBaseline, outputBaseline) // Above the baseline + + std::max(static_cast(inputHeight - inputBaseline), outputHeightBelowBaseline) // Below the baseline + + 2 * verticalMarginAroundLayouts; } else { - // Create the approximate output layout - bool couldNotCreateApproximateLayout = false; - Layout approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout); - if (couldNotCreateApproximateLayout) { - if (displayOutput(context) == DisplayOutput::ApproximateOnly) { - Poincare::ExceptionCheckpoint::Raise(); - } else { - /* Set the display output to ApproximateOnly, make room in the pool by - * erasing the exact layout, and retry to create the approximate layout */ - forceDisplayOutput(DisplayOutput::ApproximateOnly); - exactLayout = Poincare::Layout(); - couldNotCreateApproximateLayout = false; - approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout); - if (couldNotCreateApproximateLayout) { - Poincare::ExceptionCheckpoint::Raise(); - } - } - } - - KDCoordinate approximateOutputHeight = approximateLayout.layoutSize().height(); - KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); - if (displayOutput(context) == DisplayOutput::ApproximateOnly || (!expanded && displayOutput(context) == DisplayOutput::ExactAndApproximateToggle)) { - bool singleLine = forceSingleLine || canBeSingleLine(inputWidth, approximateOutputWidth); - if (singleLine) { - KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - result = std::max(inputBaseline, approximateOutputBaseline) // Above the baseline - + std::max(inputHeight - inputBaseline, approximateOutputHeight - approximateOutputBaseline) // Below the baseline - + 2 * verticalMarginAroundLayouts; - } else { - result = inputHeight + verticalMarginBetweenLayouts + approximateOutputHeight + 4 * verticalMarginAroundLayouts; - } - } else { - assert(displayOutput(context) == DisplayOutput::ExactAndApproximate || (displayOutput(context) == DisplayOutput::ExactAndApproximateToggle && expanded)); - KDCoordinate exactOutputHeight = exactLayout.layoutSize().height(); - KDCoordinate exactOutputBaseline = exactLayout.baseline(); - KDCoordinate exactOutputWidth = exactLayout.layoutSize().width(); - KDCoordinate approximateOutputWidth = approximateLayout.layoutSize().width(); - KDCoordinate approximateOutputBaseline = approximateLayout.baseline(); - bool singleLine = forceSingleLine || canBeSingleLine(inputWidth, exactOutputWidth + AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize() + approximateOutputWidth); - if (singleLine) { - result = std::max(inputBaseline, std::max(exactOutputBaseline, approximateOutputBaseline)) // Above the baseline - + std::max(inputHeight - inputBaseline, // Beloxw the baseline - std::max(exactOutputHeight - exactOutputBaseline, - approximateOutputHeight - approximateOutputBaseline)) - + 2 * verticalMarginAroundLayouts; - } else { - KDCoordinate outputHeight = std::max(exactOutputBaseline, approximateOutputBaseline) // Above the baseline - + std::max(exactOutputHeight - exactOutputBaseline, approximateOutputHeight - approximateOutputBaseline); // Below the baseline - result = inputHeight + verticalMarginBetweenLayouts + outputHeight + 4 * verticalMarginAroundLayouts; - } - } + result = inputHeight + verticalMarginBetweenLayouts + outputBaseline + outputHeightBelowBaseline + 4 * verticalMarginAroundLayouts; } // Add the top and bottom margins From 579485c8c0f1ce10fbc491820b3fab1a336962d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 19 May 2020 16:42:28 +0200 Subject: [PATCH 72/85] [apps/calculation] CanBeSingleLine differs if args are view/layout sizes If the sizes are for layouts, we need to add the margins added by the views. --- apps/calculation/calculation.cpp | 2 +- apps/calculation/calculation.h | 4 ++-- apps/calculation/history_controller.cpp | 2 +- apps/calculation/history_view_cell.cpp | 13 +++++++++---- apps/calculation/history_view_cell.h | 4 +++- .../scrollable_multiple_expressions_view.cpp | 16 ++++++++-------- .../scrollable_multiple_expressions_view.h | 2 +- 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 51a710d83..ca0422fcb 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -125,7 +125,7 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul } } -KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginBetweenLayouts, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, CanBeSingleLineFunction canBeSingleLine) { +KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginBetweenLayouts, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canBeSingleLine) { KDCoordinate result = expanded ? m_expandedHeight : m_height; if (result >= 0) { // Height already computed diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index a1685131c..3c9ef1ffa 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -84,8 +84,8 @@ public: Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout); // Memoization of height - typedef bool (*CanBeSingleLineFunction)(KDCoordinate inputWidth, KDCoordinate outputWidth); - KDCoordinate height(Poincare::Context * context, KDCoordinate verticalMarginBetweenLayouts, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, CanBeSingleLineFunction canbeSingleLine = [](KDCoordinate inputWidth, KDCoordinate outputWidth) { assert(false); return true; }); + typedef bool (*LayoutsCanBeSingleLineFunction)(KDCoordinate inputWidth, KDCoordinate outputWidth); + KDCoordinate height(Poincare::Context * context, KDCoordinate verticalMarginBetweenLayouts, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canbeSingleLine = [](KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth) { assert(false); return true; }); // Displayed output DisplayOutput displayOutput(Poincare::Context * context); diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 005847dd8..b83ff5879 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -214,7 +214,7 @@ KDCoordinate HistoryController::rowHeight(int j) { HistoryViewCell::k_inputOutputViewsVerticalMargin, j == selectedRow() && selectedSubviewType() == SubviewType::Output, false, - &HistoryViewCell::CanBeSingleLine); + &HistoryViewCell::LayoutsCanBeSingleLine); } int HistoryController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 8c7c5cdaa..4d1bddc02 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -42,7 +42,7 @@ HistoryViewCell::HistoryViewCell(Responder * parentResponder) : m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown), m_calculationAdditionInformation(Calculation::AdditionalInformationType::None), m_calculationExpanded(false), - m_inputView(this, Metric::CommonLargeMargin, k_inputOutputViewsVerticalMargin), + m_inputView(this, k_inputOutputViewsHorizontalMargin, k_inputOutputViewsVerticalMargin), m_scrollableOutputView(this) { m_calculationCRC32 = 0; @@ -169,9 +169,14 @@ View * HistoryViewCell::subviewAtIndex(int index) { return views[index]; } -bool HistoryViewCell::CanBeSingleLine(KDCoordinate inputWidth, KDCoordinate outputWidth) { +bool HistoryViewCell::LayoutsCanBeSingleLine(KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth) { // k_margin is the separation between the input and output. - return (inputWidth + k_margin + outputWidth) < (Ion::Display::Width - Metric::EllipsisCellWidth - 2 * k_margin); //TODO LEA -2 ?? + return ViewsCanBeSingleLine(inputLayoutWidth + 2 * k_inputOutputViewsHorizontalMargin, outputLayoutWidth + 2 * k_inputOutputViewsHorizontalMargin); +} + +bool HistoryViewCell::ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth) { + // k_margin is the separation between the input and output. + return (inputViewWidth + k_margin + outputViewWidth) < Ion::Display::Width - Metric::EllipsisCellWidth; } void HistoryViewCell::layoutSubviews(bool force) { @@ -185,7 +190,7 @@ void HistoryViewCell::layoutSubviews(bool force) { KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay(); KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); - bool singleLine = CanBeSingleLine(inputSize.width(), outputSize.width()); + bool singleLine = ViewsCanBeSingleLine(inputSize.width(), outputSize.width()); KDCoordinate inputY = k_margin; KDCoordinate outputY = k_margin; diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index d4a4b00e6..13a5b3cf3 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -33,8 +33,10 @@ class HistoryViewCell : public ::EvenOddCell, public Responder { public: constexpr static KDCoordinate k_margin = Metric::CommonSmallMargin; constexpr static KDCoordinate k_inputOutputViewsVerticalMargin = k_margin; + constexpr static KDCoordinate k_inputOutputViewsHorizontalMargin = Shared::AbstractScrollableMultipleExpressionsView::k_horizontalMargin; HistoryViewCell(Responder * parentResponder = nullptr); - static bool CanBeSingleLine(KDCoordinate inputWidth, KDCoordinate outputWidth); + static bool LayoutsCanBeSingleLine(KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth); + static bool ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth); void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType = HistoryViewCellDataSource::SubviewType::None); void setEven(bool even) override; void setHighlighted(bool highlight) override; diff --git a/apps/shared/scrollable_multiple_expressions_view.cpp b/apps/shared/scrollable_multiple_expressions_view.cpp index 3a4c1b652..0af626941 100644 --- a/apps/shared/scrollable_multiple_expressions_view.cpp +++ b/apps/shared/scrollable_multiple_expressions_view.cpp @@ -66,13 +66,13 @@ KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOpt KDSize leftSize = KDSizeZero; if (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) { leftSize = leftExpressionView()->minimalSizeForOptimalDisplay(); - width += leftSize.width() + k_horizontalMargin; + width += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin; } KDSize centerSize = KDSizeZero; if (displayCenter()) { centerSize = m_centeredExpressionView.minimalSizeForOptimalDisplay(); - width += centerSize.width() + 2 * k_horizontalMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); + width += centerSize.width() + 2 * AbstractScrollableMultipleExpressionsView::k_horizontalMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); } KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); @@ -157,7 +157,7 @@ View * AbstractScrollableMultipleExpressionsView::ContentCell::subviewAtIndex(in } KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::StandardApproximateViewAndMarginsSize() { - return 2 * k_horizontalMargin + k_font->stringSize(I18n::translate(k_defaultApproximateMessage)).width(); //TODO LEA + return 2 * AbstractScrollableMultipleExpressionsView::k_horizontalMargin + k_font->stringSize(I18n::translate(k_defaultApproximateMessage)).width(); } void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool force) { @@ -176,16 +176,16 @@ void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool KDCoordinate currentWidth = 0; if (leftExpressionView()) { leftExpressionView()->setFrame(KDRect(currentWidth, viewBaseline - leftBaseline, leftSize), force); - currentWidth += leftSize.width() + k_horizontalMargin; + currentWidth += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin; } // Layout center expression if (displayCenter()) { KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); m_centeredExpressionView.setFrame(KDRect(currentWidth, viewBaseline - centerBaseline, centerSize), force); - currentWidth += k_horizontalMargin + centerSize.width(); + currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + centerSize.width(); m_approximateSign.setFrame(KDRect(currentWidth, viewBaseline - approximateSignSize.height()/2, approximateSignSize), force); - currentWidth += k_horizontalMargin + approximateSignSize.width(); + currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + approximateSignSize.width(); } // Layout right expression @@ -247,8 +247,8 @@ bool AbstractScrollableMultipleExpressionsView::handleEvent(Ion::Events::Event e if (contentCell()->displayCenter()) { KDCoordinate centerExpressionWidth = contentCell()->centeredExpressionView()->minimalSizeForOptimalDisplay().width(); KDCoordinate signWidth = contentCell()->approximateSign()->minimalSizeForOptimalDisplay().width(); - centeredExpressionIsVisibleOnTheLeft = leftWidth + ContentCell::k_horizontalMargin + centerExpressionWidth - contentOffset().x() > 0; - centeredExpressionIsVisibleOnTheRight = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - signWidth - centerExpressionWidth - 2 * ContentCell::k_horizontalMargin - contentOffset().x() < bounds().width(); + centeredExpressionIsVisibleOnTheLeft = leftWidth + k_horizontalMargin + centerExpressionWidth - contentOffset().x() > 0; + centeredExpressionIsVisibleOnTheRight = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - signWidth - centerExpressionWidth - 2 * k_horizontalMargin - contentOffset().x() < bounds().width(); } // Select center if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && centeredExpressionIsVisibleOnTheLeft) || diff --git a/apps/shared/scrollable_multiple_expressions_view.h b/apps/shared/scrollable_multiple_expressions_view.h index 26eb85386..9e8961d21 100644 --- a/apps/shared/scrollable_multiple_expressions_view.h +++ b/apps/shared/scrollable_multiple_expressions_view.h @@ -8,6 +8,7 @@ namespace Shared { class AbstractScrollableMultipleExpressionsView : public ScrollableView, public ScrollViewDataSource { public: + constexpr static KDCoordinate k_horizontalMargin = Metric::CommonLargeMargin; enum class SubviewPosition : uint8_t { Left = 0, Center = 1, @@ -40,7 +41,6 @@ protected: class ContentCell : public ::EvenOddCell { public: static KDCoordinate StandardApproximateViewAndMarginsSize(); - constexpr static KDCoordinate k_horizontalMargin = Metric::CommonLargeMargin; ContentCell(); KDColor backgroundColor() const override; void setHighlighted(bool highlight) override; From 6948011ae03f6fd5c000cab95b661e2650849d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 19 May 2020 17:52:44 +0200 Subject: [PATCH 73/85] [apps/calculation] Handle navigation in single lined output --- apps/calculation/history_view_cell.cpp | 63 ++++++++++++++++---------- apps/calculation/history_view_cell.h | 11 +++-- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 4d1bddc02..0f1ff3bd7 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -39,17 +39,18 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, HistoryViewCell::HistoryViewCell(Responder * parentResponder) : Responder(parentResponder), + m_calculationCRC32(0), m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown), m_calculationAdditionInformation(Calculation::AdditionalInformationType::None), - m_calculationExpanded(false), m_inputView(this, k_inputOutputViewsHorizontalMargin, k_inputOutputViewsVerticalMargin), - m_scrollableOutputView(this) + m_scrollableOutputView(this), + m_calculationExpanded(false), + m_calculationSingleLine(false) { - m_calculationCRC32 = 0; } Shared::ScrollableTwoExpressionsView * HistoryViewCell::outputView() { - return &m_scrollableOutputView; + return &m_scrollableOutputView; //TODO LEA inline } void HistoryViewCell::setEven(bool even) { @@ -190,11 +191,11 @@ void HistoryViewCell::layoutSubviews(bool force) { KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay(); KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); - bool singleLine = ViewsCanBeSingleLine(inputSize.width(), outputSize.width()); + m_calculationSingleLine = ViewsCanBeSingleLine(inputSize.width(), outputSize.width()); KDCoordinate inputY = k_margin; KDCoordinate outputY = k_margin; - if (singleLine) { + if (m_calculationSingleLine) { KDCoordinate inputBaseline = m_inputView.layout().baseline(); KDCoordinate outputBaseline = m_scrollableOutputView.baseline(); KDCoordinate baselineDifference = outputBaseline - inputBaseline; @@ -312,27 +313,41 @@ void HistoryViewCell::didBecomeFirstResponder() { } bool HistoryViewCell::handleEvent(Ion::Events::Event event) { - assert(m_dataSource); + assert(m_dataSource != nullptr); HistoryViewCellDataSource::SubviewType type = m_dataSource->selectedSubviewType(); - if ((event == Ion::Events::Down && type == HistoryViewCellDataSource::SubviewType::Input) || - (event == Ion::Events::Up && type == HistoryViewCellDataSource::SubviewType::Output) || - (event == Ion::Events::Right && type != HistoryViewCellDataSource::SubviewType::Ellipsis && displayedEllipsis()) || - (event == Ion::Events::Left && type == HistoryViewCellDataSource::SubviewType::Ellipsis)) { - HistoryViewCellDataSource::SubviewType otherSubviewType; - if (event == Ion::Events::Down) { - otherSubviewType = HistoryViewCellDataSource::SubviewType::Output; - } else if (event == Ion::Events::Up) { - otherSubviewType = HistoryViewCellDataSource::SubviewType::Input; - } else if (event == Ion::Events::Right) { - otherSubviewType = HistoryViewCellDataSource::SubviewType::Ellipsis; - } else { - assert(event == Ion::Events::Left); - otherSubviewType = HistoryViewCellDataSource::SubviewType::Output; + assert(type != HistoryViewCellDataSource::SubviewType::None); + HistoryViewCellDataSource::SubviewType otherSubviewType = HistoryViewCellDataSource::SubviewType::None; + if (m_calculationSingleLine) { + static_assert( + static_cast(HistoryViewCellDataSource::SubviewType::None) == 0 + && static_cast(HistoryViewCellDataSource::SubviewType::Input) == 1 + && static_cast(HistoryViewCellDataSource::SubviewType::Output) == 2 + && static_cast(HistoryViewCellDataSource::SubviewType::Ellipsis) == 3, + "The array types is not well-formed anymore"); + HistoryViewCellDataSource::SubviewType types[] = { + HistoryViewCellDataSource::SubviewType::None, + HistoryViewCellDataSource::SubviewType::Input, + HistoryViewCellDataSource::SubviewType::Output, + displayedEllipsis() ? HistoryViewCellDataSource::SubviewType::Ellipsis : HistoryViewCellDataSource::SubviewType::None, + HistoryViewCellDataSource::SubviewType::None, + }; + if (event == Ion::Events::Right || event == Ion::Events::Left) { + otherSubviewType = types[static_cast(type) + (event == Ion::Events::Right ? 1 : -1)]; } - m_dataSource->setSelectedSubviewType(otherSubviewType, true); - return true; + } else if ((event == Ion::Events::Down && type == HistoryViewCellDataSource::SubviewType::Input) + || (event == Ion::Events::Left && type == HistoryViewCellDataSource::SubviewType::Ellipsis)) + { + otherSubviewType = HistoryViewCellDataSource::SubviewType::Output; + } else if (event == Ion::Events::Up && type == HistoryViewCellDataSource::SubviewType::Output) { + otherSubviewType = HistoryViewCellDataSource::SubviewType::Input; + } else if (event == Ion::Events::Right && type != HistoryViewCellDataSource::SubviewType::Ellipsis && displayedEllipsis()) { + otherSubviewType = HistoryViewCellDataSource::SubviewType::Ellipsis; } - return false; + if (otherSubviewType == HistoryViewCellDataSource::SubviewType::None) { + return false; + } + m_dataSource->setSelectedSubviewType(otherSubviewType, true); + return true; } bool HistoryViewCell::displayedEllipsis() const { diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index 13a5b3cf3..e2bda913b 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -12,10 +12,10 @@ class HistoryViewCell; class HistoryViewCellDataSource { public: enum class SubviewType { - None, - Input, - Output, - Ellipsis + None = 0, + Input = 1, + Output = 2, + Ellipsis = 3 }; HistoryViewCellDataSource(); void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1); @@ -64,11 +64,12 @@ private: uint32_t m_calculationCRC32; Calculation::DisplayOutput m_calculationDisplayOutput; Calculation::AdditionalInformationType m_calculationAdditionInformation; - bool m_calculationExpanded; ScrollableExpressionView m_inputView; Shared::ScrollableTwoExpressionsView m_scrollableOutputView; EvenOddCellWithEllipsis m_ellipsis; HistoryViewCellDataSource * m_dataSource; + bool m_calculationExpanded; + bool m_calculationSingleLine; }; } From feee0a673c2e63c655e189a942aa6570905e5a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 20 May 2020 10:28:55 +0200 Subject: [PATCH 74/85] [apps/calculation] HistoryViewCell cleaning --- apps/calculation/history_view_cell.cpp | 20 -------------------- apps/calculation/history_view_cell.h | 12 +++++++----- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 0f1ff3bd7..53b42f0f1 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -11,9 +11,6 @@ namespace Calculation { /* HistoryViewCellDataSource */ -HistoryViewCellDataSource::HistoryViewCellDataSource() : - m_selectedSubviewType(SubviewType::Output) {} - void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedCellX, int previousSelectedCellY) { HistoryViewCell * selectedCell = nullptr; HistoryViewCell * previouslySelectedCell = nullptr; @@ -49,10 +46,6 @@ HistoryViewCell::HistoryViewCell(Responder * parentResponder) : { } -Shared::ScrollableTwoExpressionsView * HistoryViewCell::outputView() { - return &m_scrollableOutputView; //TODO LEA inline -} - void HistoryViewCell::setEven(bool even) { EvenOddCell::setEven(even); m_inputView.setBackgroundColor(backgroundColor()); @@ -138,15 +131,6 @@ void HistoryViewCell::cellDidSelectSubview(HistoryViewCellDataSource::SubviewTyp reloadScroll(); } -KDColor HistoryViewCell::backgroundColor() const { - KDColor background = m_even ? KDColorWhite : Palette::WallScreen; - return background; -} - -int HistoryViewCell::numberOfSubviews() const { - return 2 + displayedEllipsis(); -} - View * HistoryViewCell::subviewAtIndex(int index) { /* The order of the subviews should not matter here as they don't overlap. * However, the order determines the order of redrawing as well. For several @@ -350,8 +334,4 @@ bool HistoryViewCell::handleEvent(Ion::Events::Event event) { return true; } -bool HistoryViewCell::displayedEllipsis() const { - return m_highlighted && m_calculationAdditionInformation != Calculation::AdditionalInformationType::None; -} - } diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index e2bda913b..1a378067e 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -17,7 +17,7 @@ public: Output = 2, Ellipsis = 3 }; - HistoryViewCellDataSource(); + HistoryViewCellDataSource() : m_selectedSubviewType(SubviewType::Output) {} void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1); SubviewType selectedSubviewType() { return m_selectedSubviewType; } private: @@ -46,21 +46,23 @@ public: return this; } Poincare::Layout layout() const override; - KDColor backgroundColor() const override; + KDColor backgroundColor() const override { return m_even ? KDColorWhite : Palette::WallScreen; } void resetMemoization(); void setCalculation(Calculation * calculation, bool expanded); - int numberOfSubviews() const override; + int numberOfSubviews() const override { return 2 + displayedEllipsis(); } View * subviewAtIndex(int index) override; void layoutSubviews(bool force = false) override; void didBecomeFirstResponder() override; bool handleEvent(Ion::Events::Event event) override; - Shared::ScrollableTwoExpressionsView * outputView(); + Shared::ScrollableTwoExpressionsView * outputView() { return &m_scrollableOutputView; } Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; } private: constexpr static KDCoordinate k_resultWidth = 80; void reloadScroll(); void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType); - bool displayedEllipsis() const; + bool displayedEllipsis() const { + return m_highlighted && m_calculationAdditionInformation != Calculation::AdditionalInformationType::None; + } uint32_t m_calculationCRC32; Calculation::DisplayOutput m_calculationDisplayOutput; Calculation::AdditionalInformationType m_calculationAdditionInformation; From 5535145b51734ff665b596f0cbe54f7ed70fc0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 26 May 2020 10:54:14 +0200 Subject: [PATCH 75/85] [apps/calculation] Calculation on 2 lines if does not fit when expanded Scenario: 1+1+1+1+1+1+1+10.5 fits on one line when not expanded, but when the exact result is displayed not -> we thus always display it on two lines --- apps/calculation/calculation.cpp | 39 ++++++++--- apps/calculation/history_view_cell.cpp | 5 +- .../scrollable_multiple_expressions_view.cpp | 68 +++++++++++-------- .../scrollable_multiple_expressions_view.h | 6 +- 4 files changed, 75 insertions(+), 43 deletions(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index ca0422fcb..ee076fbbe 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -126,6 +126,9 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul } KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginBetweenLayouts, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canBeSingleLine) { + /* WARNING: this method must return the same result as + * Calculation::HistoryViewCell::layoutSubviews. */ + KDCoordinate result = expanded ? m_expandedHeight : m_height; if (result >= 0) { // Height already computed @@ -138,12 +141,19 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginB KDCoordinate inputWidth = inputLayout.layoutSize().width(); KDCoordinate inputBaseline = inputLayout.baseline(); + // Get output height KDCoordinate outputWidth; KDCoordinate outputBaseline; KDCoordinate outputHeightBelowBaseline; + + DisplayOutput displayType = displayOutput(context); + /* If the display output is ExactAndApproximateToggle, we want to use the + * expanded width to compute if the calculaton is in single line or not. */ + bool shouldComputeMaxOutputWidthForSingleLine = !forceSingleLine && (!expanded && displayType == DisplayOutput::ExactAndApproximateToggle); + KDCoordinate maxOutputWidth = -1; + { - DisplayOutput displayType = displayOutput(context); bool displaysExactOnly = displayType == DisplayOutput::ExactOnly; bool displayApproximateOnly = displayType == DisplayOutput::ApproximateOnly || (!expanded @@ -154,7 +164,8 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginB KDCoordinate exactOutputBelowBaseline = 0; // Get exact output info if needed - if (displaysExactOnly || !displayApproximateOnly) { + bool displaysExact = displaysExactOnly || !displayApproximateOnly; + if (displaysExact || shouldComputeMaxOutputWidthForSingleLine) { bool couldNotCreateExactLayout = false; Poincare::Layout exactLayout = createExactOutputLayout(&couldNotCreateExactLayout); if (couldNotCreateExactLayout) { @@ -170,9 +181,12 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginB } } else { KDSize exactSize = exactLayout.layoutSize(); - exactOutputWidth = exactSize.width(); - exactOutputBaseline = exactLayout.baseline(); - exactOutputBelowBaseline = exactSize.height() - exactOutputBaseline; + maxOutputWidth = exactSize.width(); + if (displaysExact) { + exactOutputWidth = exactSize.width(); + exactOutputBaseline = exactLayout.baseline(); + exactOutputBelowBaseline = exactSize.height() - exactOutputBaseline; + } } } @@ -181,19 +195,24 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginB KDCoordinate approximateOutputBelowBaseline = 0; // Get approximate output info if needed - if (displayApproximateOnly || !displaysExactOnly) { + bool displaysApproximate = displayApproximateOnly || !displaysExactOnly; + if (displaysApproximate || shouldComputeMaxOutputWidthForSingleLine) { bool couldNotCreateApproximateLayout = false; Layout approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout); if (couldNotCreateApproximateLayout) { Poincare::ExceptionCheckpoint::Raise(); } KDSize approximateOutputSize = approximateLayout.layoutSize(); - approximateOutputWidth = approximateOutputSize.width(); - approximateOutputBaseline = approximateLayout.baseline(); - approximateOutputBelowBaseline = approximateOutputSize.height() - approximateOutputBaseline; + maxOutputWidth += approximateOutputSize.width(); + if (displaysApproximate) { + approximateOutputWidth = approximateOutputSize.width(); + approximateOutputBaseline = approximateLayout.baseline(); + approximateOutputBelowBaseline = approximateOutputSize.height() - approximateOutputBaseline; + } } // Compute the output info + maxOutputWidth += AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize(); outputWidth = exactOutputWidth + ((exactOutputWidth > 0 && approximateOutputWidth > 0) ? AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize() : 0) + approximateOutputWidth; @@ -201,7 +220,7 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginB outputHeightBelowBaseline = std::max(exactOutputBelowBaseline, approximateOutputBelowBaseline); } - if (forceSingleLine || canBeSingleLine(inputWidth, outputWidth)) { + if (forceSingleLine || canBeSingleLine(inputWidth, maxOutputWidth)) { result = std::max(inputBaseline, outputBaseline) // Above the baseline + std::max(static_cast(inputHeight - inputBaseline), outputHeightBelowBaseline) // Below the baseline + 2 * verticalMarginAroundLayouts; diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 53b42f0f1..981a34018 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -155,7 +155,6 @@ View * HistoryViewCell::subviewAtIndex(int index) { } bool HistoryViewCell::LayoutsCanBeSingleLine(KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth) { - // k_margin is the separation between the input and output. return ViewsCanBeSingleLine(inputLayoutWidth + 2 * k_inputOutputViewsHorizontalMargin, outputLayoutWidth + 2 * k_inputOutputViewsHorizontalMargin); } @@ -175,7 +174,9 @@ void HistoryViewCell::layoutSubviews(bool force) { KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay(); KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); - m_calculationSingleLine = ViewsCanBeSingleLine(inputSize.width(), outputSize.width()); + /* To compute if the calculation is on a single line, use the expanded width + * if there is both an exact and an approximate layout. */ + m_calculationSingleLine = ViewsCanBeSingleLine(inputSize.width(), m_scrollableOutputView.minimalSizeForOptimalDisplayFullSize().width()); KDCoordinate inputY = k_margin; KDCoordinate outputY = k_margin; diff --git a/apps/shared/scrollable_multiple_expressions_view.cpp b/apps/shared/scrollable_multiple_expressions_view.cpp index 0af626941..497647f8b 100644 --- a/apps/shared/scrollable_multiple_expressions_view.cpp +++ b/apps/shared/scrollable_multiple_expressions_view.cpp @@ -55,37 +55,11 @@ void AbstractScrollableMultipleExpressionsView::ContentCell::reloadTextColor() { } KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOptimalDisplay() const { - KDCoordinate width = 0; + return privateMinimalSizeForOptimalDisplay(false); +} - // Compute baselines - KDCoordinate leftBaseline = 0; - KDCoordinate centerBaseline = 0; - KDCoordinate rightBaseline = 0; - KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline); - - KDSize leftSize = KDSizeZero; - if (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) { - leftSize = leftExpressionView()->minimalSizeForOptimalDisplay(); - width += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin; - } - - KDSize centerSize = KDSizeZero; - if (displayCenter()) { - centerSize = m_centeredExpressionView.minimalSizeForOptimalDisplay(); - width += centerSize.width() + 2 * AbstractScrollableMultipleExpressionsView::k_horizontalMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); - } - - KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); - width += rightSize.width(); - - KDCoordinate height = viewBaseline - + std::max( - std::max( - centerSize.height() - centerBaseline, - rightSize.height() - rightBaseline), - leftSize.height() - leftBaseline); - - return KDSize(width, height); +KDSize AbstractScrollableMultipleExpressionsView::ContentCell::minimalSizeForOptimalDisplayFullSize() const { + return privateMinimalSizeForOptimalDisplay(true); } void AbstractScrollableMultipleExpressionsView::ContentCell::setSelectedSubviewPosition(AbstractScrollableMultipleExpressionsView::SubviewPosition subviewPosition) { @@ -147,6 +121,40 @@ KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::baseline(KD return std::max(std::max(leftViewBaseline, centerViewBaseline), rightViewBaseline); } +KDSize AbstractScrollableMultipleExpressionsView::ContentCell::privateMinimalSizeForOptimalDisplay(bool forceFullDisplay) const { + KDCoordinate width = 0; + + // Compute baselines + KDCoordinate leftBaseline = 0; + KDCoordinate centerBaseline = 0; + KDCoordinate rightBaseline = 0; + KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline); + + KDSize leftSize = KDSizeZero; + if (leftExpressionView() && !leftExpressionView()->layout().isUninitialized()) { + leftSize = leftExpressionView()->minimalSizeForOptimalDisplay(); + width += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin; + } + + KDSize centerSize = KDSizeZero; + if (displayCenter() || (forceFullDisplay && !m_centeredExpressionView.layout().isUninitialized())) { + centerSize = m_centeredExpressionView.minimalSizeForOptimalDisplay(); + width += centerSize.width() + 2 * AbstractScrollableMultipleExpressionsView::k_horizontalMargin + m_approximateSign.minimalSizeForOptimalDisplay().width(); + } + + KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); + width += rightSize.width(); + + KDCoordinate height = viewBaseline + + std::max( + std::max( + centerSize.height() - centerBaseline, + rightSize.height() - rightBaseline), + leftSize.height() - leftBaseline); + + return KDSize(width, height); +} + View * AbstractScrollableMultipleExpressionsView::ContentCell::subviewAtIndex(int index) { bool leftIsVisible = leftExpressionView() != nullptr; if (leftIsVisible && index == 0) { diff --git a/apps/shared/scrollable_multiple_expressions_view.h b/apps/shared/scrollable_multiple_expressions_view.h index 9e8961d21..5e4574415 100644 --- a/apps/shared/scrollable_multiple_expressions_view.h +++ b/apps/shared/scrollable_multiple_expressions_view.h @@ -47,6 +47,7 @@ protected: void setEven(bool even) override; void reloadTextColor(); KDSize minimalSizeForOptimalDisplay() const override; + KDSize minimalSizeForOptimalDisplayFullSize() const; virtual ExpressionView * leftExpressionView() const { return nullptr; } ExpressionView * rightExpressionView() { return &m_rightExpressionView; @@ -70,6 +71,7 @@ protected: private: constexpr static const KDFont * k_font = KDFont::LargeFont; const static I18n::Message k_defaultApproximateMessage = I18n::Message::AlmostEqual; + KDSize privateMinimalSizeForOptimalDisplay(bool forceFullDisplay) const; View * subviewAtIndex(int index) override; ExpressionView m_rightExpressionView; MessageTextView m_approximateSign; @@ -91,7 +93,9 @@ public: Metric::CommonLargeMargin ); } - + KDSize minimalSizeForOptimalDisplayFullSize() const { + return constContentCell()->minimalSizeForOptimalDisplayFullSize(); + } private: ContentCell * contentCell() override { return &m_contentCell; }; const ContentCell * constContentCell() const override { return &m_contentCell; }; From c4908c2bd923783bb956358cfad6b76d7ec1d55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 26 May 2020 11:03:35 +0200 Subject: [PATCH 76/85] [apps/calculation] Fix bottom calculation margin Scenario: enter a calculation that uses two lines, the margin on the bottom is too big --- apps/calculation/calculation.cpp | 6 +++--- apps/calculation/calculation.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index ee076fbbe..b65327fb4 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -125,7 +125,7 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul } } -KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginBetweenLayouts, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canBeSingleLine) { +KDCoordinate Calculation::height(Context * context, KDCoordinate topBottomMargin, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canBeSingleLine) { /* WARNING: this method must return the same result as * Calculation::HistoryViewCell::layoutSubviews. */ @@ -225,11 +225,11 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate verticalMarginB + std::max(static_cast(inputHeight - inputBaseline), outputHeightBelowBaseline) // Below the baseline + 2 * verticalMarginAroundLayouts; } else { - result = inputHeight + verticalMarginBetweenLayouts + outputBaseline + outputHeightBelowBaseline + 4 * verticalMarginAroundLayouts; + result = inputHeight + outputBaseline + outputHeightBelowBaseline + 4 * verticalMarginAroundLayouts; } // Add the top and bottom margins - result += 2 * verticalMarginBetweenLayouts; + result += 2 * topBottomMargin; /* For all display outputs except ExactAndApproximateToggle, the selected * height and the usual height are identical. We update both heights in diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index 3c9ef1ffa..6f6050e45 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -85,7 +85,7 @@ public: // Memoization of height typedef bool (*LayoutsCanBeSingleLineFunction)(KDCoordinate inputWidth, KDCoordinate outputWidth); - KDCoordinate height(Poincare::Context * context, KDCoordinate verticalMarginBetweenLayouts, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canbeSingleLine = [](KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth) { assert(false); return true; }); + KDCoordinate height(Poincare::Context * context, KDCoordinate topBottomMargin, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canbeSingleLine = [](KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth) { assert(false); return true; }); // Displayed output DisplayOutput displayOutput(Poincare::Context * context); From e2f4b30b84248a9cecd3bf74bf66bed0c1e76756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 26 May 2020 11:52:44 +0200 Subject: [PATCH 77/85] [apps/calculation] Remove unused variable --- apps/calculation/calculation.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index b65327fb4..d490e1412 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -135,23 +135,21 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate topBottomMargin return result; } - // Get input height + // Get input information Layout inputLayout = createInputLayout(); KDCoordinate inputHeight = inputLayout.layoutSize().height(); KDCoordinate inputWidth = inputLayout.layoutSize().width(); KDCoordinate inputBaseline = inputLayout.baseline(); - // Get output height + // Get output information KDCoordinate outputWidth; KDCoordinate outputBaseline; KDCoordinate outputHeightBelowBaseline; - DisplayOutput displayType = displayOutput(context); /* If the display output is ExactAndApproximateToggle, we want to use the * expanded width to compute if the calculaton is in single line or not. */ bool shouldComputeMaxOutputWidthForSingleLine = !forceSingleLine && (!expanded && displayType == DisplayOutput::ExactAndApproximateToggle); - KDCoordinate maxOutputWidth = -1; { bool displaysExactOnly = displayType == DisplayOutput::ExactOnly; @@ -174,6 +172,8 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate topBottomMargin forceDisplayOutput(displayType); displaysExactOnly = false; displayApproximateOnly = true; + shouldComputeMaxOutputWidthForSingleLine = false; + outputWidth = 0; } else { /* We should only display the exact result, but we cannot create it * -> raise an exception. */ @@ -181,7 +181,7 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate topBottomMargin } } else { KDSize exactSize = exactLayout.layoutSize(); - maxOutputWidth = exactSize.width(); + outputWidth = exactSize.width(); if (displaysExact) { exactOutputWidth = exactSize.width(); exactOutputBaseline = exactLayout.baseline(); @@ -203,7 +203,7 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate topBottomMargin Poincare::ExceptionCheckpoint::Raise(); } KDSize approximateOutputSize = approximateLayout.layoutSize(); - maxOutputWidth += approximateOutputSize.width(); + outputWidth += approximateOutputSize.width(); if (displaysApproximate) { approximateOutputWidth = approximateOutputSize.width(); approximateOutputBaseline = approximateLayout.baseline(); @@ -212,15 +212,14 @@ KDCoordinate Calculation::height(Context * context, KDCoordinate topBottomMargin } // Compute the output info - maxOutputWidth += AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize(); - outputWidth = exactOutputWidth - + ((exactOutputWidth > 0 && approximateOutputWidth > 0) ? AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize() : 0) - + approximateOutputWidth; + if (shouldComputeMaxOutputWidthForSingleLine || (exactOutputWidth > 0 && approximateOutputWidth > 0)) { + outputWidth += AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize(); + } outputBaseline = std::max(exactOutputBaseline, approximateOutputBaseline); outputHeightBelowBaseline = std::max(exactOutputBelowBaseline, approximateOutputBelowBaseline); } - if (forceSingleLine || canBeSingleLine(inputWidth, maxOutputWidth)) { + if (forceSingleLine || canBeSingleLine(inputWidth, outputWidth)) { result = std::max(inputBaseline, outputBaseline) // Above the baseline + std::max(static_cast(inputHeight - inputBaseline), outputHeightBelowBaseline) // Below the baseline + 2 * verticalMarginAroundLayouts; From 1d3a949b6f4dd49bdf11e0754b28c9132f2823d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 26 May 2020 11:53:57 +0200 Subject: [PATCH 78/85] [apps/shared] Add escape case --- .../scrollable_multiple_expressions_view.cpp | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/apps/shared/scrollable_multiple_expressions_view.cpp b/apps/shared/scrollable_multiple_expressions_view.cpp index 497647f8b..789b0e42a 100644 --- a/apps/shared/scrollable_multiple_expressions_view.cpp +++ b/apps/shared/scrollable_multiple_expressions_view.cpp @@ -242,38 +242,40 @@ void AbstractScrollableMultipleExpressionsView::setDisplayCenter(bool display) { } bool AbstractScrollableMultipleExpressionsView::handleEvent(Ion::Events::Event event) { - bool leftIsVisible = false; - KDCoordinate leftWidth = 0; - if (contentCell()->leftExpressionView()) { - leftWidth = contentCell()->leftExpressionView()->minimalSizeForOptimalDisplay().width(); - leftIsVisible = leftWidth - contentOffset().x() > 0; - } - KDCoordinate rightExpressionWidth = contentCell()->rightExpressionView()->minimalSizeForOptimalDisplay().width(); - bool rightExpressionIsVisible = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - contentOffset().x() < bounds().width(); - bool centeredExpressionIsVisibleOnTheLeft = false; - bool centeredExpressionIsVisibleOnTheRight = false; - if (contentCell()->displayCenter()) { - KDCoordinate centerExpressionWidth = contentCell()->centeredExpressionView()->minimalSizeForOptimalDisplay().width(); - KDCoordinate signWidth = contentCell()->approximateSign()->minimalSizeForOptimalDisplay().width(); - centeredExpressionIsVisibleOnTheLeft = leftWidth + k_horizontalMargin + centerExpressionWidth - contentOffset().x() > 0; - centeredExpressionIsVisibleOnTheRight = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - signWidth - centerExpressionWidth - 2 * k_horizontalMargin - contentOffset().x() < bounds().width(); - } - // Select center - if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && centeredExpressionIsVisibleOnTheLeft) || - (event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && centeredExpressionIsVisibleOnTheRight)) { - setSelectedSubviewPosition(SubviewPosition::Center); - return true; - } - // Select left - if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && leftIsVisible) || - (event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Center && leftIsVisible)) { - setSelectedSubviewPosition(SubviewPosition::Left); - return true; - } - if ((event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Center && rightExpressionIsVisible) || - (event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && rightExpressionIsVisible)) { - setSelectedSubviewPosition(SubviewPosition::Right); - return true; + if (event == Ion::Events::Left || event == Ion::Events::Right ) { + bool leftIsVisible = false; + KDCoordinate leftWidth = 0; + if (contentCell()->leftExpressionView()) { + leftWidth = contentCell()->leftExpressionView()->minimalSizeForOptimalDisplay().width(); + leftIsVisible = leftWidth - contentOffset().x() > 0; + } + KDCoordinate rightExpressionWidth = contentCell()->rightExpressionView()->minimalSizeForOptimalDisplay().width(); + bool rightExpressionIsVisible = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - contentOffset().x() < bounds().width(); + bool centeredExpressionIsVisibleOnTheLeft = false; + bool centeredExpressionIsVisibleOnTheRight = false; + if (contentCell()->displayCenter()) { + KDCoordinate centerExpressionWidth = contentCell()->centeredExpressionView()->minimalSizeForOptimalDisplay().width(); + KDCoordinate signWidth = contentCell()->approximateSign()->minimalSizeForOptimalDisplay().width(); + centeredExpressionIsVisibleOnTheLeft = leftWidth + k_horizontalMargin + centerExpressionWidth - contentOffset().x() > 0; + centeredExpressionIsVisibleOnTheRight = minimalSizeForOptimalDisplay().width() - rightExpressionWidth - signWidth - centerExpressionWidth - 2 * k_horizontalMargin - contentOffset().x() < bounds().width(); + } + // Select center + if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && centeredExpressionIsVisibleOnTheLeft) || + (event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && centeredExpressionIsVisibleOnTheRight)) { + setSelectedSubviewPosition(SubviewPosition::Center); + return true; + } + // Select left + if ((event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Right && leftIsVisible) || + (event == Ion::Events::Left && selectedSubviewPosition() == SubviewPosition::Center && leftIsVisible)) { + setSelectedSubviewPosition(SubviewPosition::Left); + return true; + } + if ((event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Center && rightExpressionIsVisible) || + (event == Ion::Events::Right && selectedSubviewPosition() == SubviewPosition::Left && rightExpressionIsVisible)) { + setSelectedSubviewPosition(SubviewPosition::Right); + return true; + } } return ScrollableView::handleEvent(event); } From 0379327b06c825b477bc7f379bd2b1a72cf7a40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 26 May 2020 11:54:19 +0200 Subject: [PATCH 79/85] [apps/calculation] Geographic navigation in the history --- apps/calculation/history_controller.cpp | 6 ++---- apps/calculation/history_view_cell.h | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index b83ff5879..3fca09d90 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -166,12 +166,10 @@ void HistoryController::tableViewDidChangeSelection(SelectableTableView * t, int } if (previousSelectedCellY == -1) { setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY); - } else if (selectedRow() < previousSelectedCellY) { - setSelectedSubviewType(SubviewType::Output, false, previousSelectedCellX, previousSelectedCellY); - } else if (selectedRow() > previousSelectedCellY) { - setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY); } else if (selectedRow() == -1) { setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY); + } else { + setSelectedSubviewType(selectedSubviewType(), false, previousSelectedCellX, previousSelectedCellY); } HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell()); if (selectedCell == nullptr) { diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index 1a378067e..664f9e6ee 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -19,7 +19,7 @@ public: }; HistoryViewCellDataSource() : m_selectedSubviewType(SubviewType::Output) {} void setSelectedSubviewType(SubviewType subviewType, bool sameCell, int previousSelectedX = -1, int previousSelectedY = -1); - SubviewType selectedSubviewType() { return m_selectedSubviewType; } + SubviewType selectedSubviewType() const { return m_selectedSubviewType; } private: /* This method should belong to a delegate instead of a data source but as * both the data source and the delegate will be the same controller, we From a56a73b0ba1c425b815591e6fe681d3275927343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Tue, 26 May 2020 17:23:06 +0200 Subject: [PATCH 80/85] [apps/calculation] Fix calculation height computation It was done in two places before, which created inconsistencies --- .../illustrated_list_controller.cpp | 8 +- .../scrollable_three_expressions_cell.cpp | 11 ++ .../scrollable_three_expressions_cell.h | 9 +- apps/calculation/calculation.cpp | 121 +----------------- apps/calculation/calculation.h | 4 +- apps/calculation/history_controller.cpp | 14 +- apps/calculation/history_view_cell.cpp | 62 ++++++--- apps/calculation/history_view_cell.h | 5 +- .../scrollable_multiple_expressions_view.cpp | 79 ++++++++---- .../scrollable_multiple_expressions_view.h | 1 + escher/src/scroll_view.cpp | 3 + kandinsky/src/rect.cpp | 2 +- 12 files changed, 141 insertions(+), 178 deletions(-) diff --git a/apps/calculation/additional_outputs/illustrated_list_controller.cpp b/apps/calculation/additional_outputs/illustrated_list_controller.cpp index ba792c161..3fd481ee6 100644 --- a/apps/calculation/additional_outputs/illustrated_list_controller.cpp +++ b/apps/calculation/additional_outputs/illustrated_list_controller.cpp @@ -78,7 +78,13 @@ KDCoordinate IllustratedListController::rowHeight(int j) { return 0; } Shared::ExpiringPointer calculation = m_calculationStore.calculationAtIndex(calculationIndex); - return calculation->height(App::app()->localContext(), Metric::CommonSmallMargin, ScrollableThreeExpressionsView::k_margin, true, true) + Metric::CellSeparatorThickness; + constexpr bool expanded = true; + KDCoordinate result = calculation->memoizedHeight(expanded); + if (result < 0) { + result = ScrollableThreeExpressionsCell::Height(calculation.pointer()); + calculation->setMemoizedHeight(expanded, result); + } + return result + Metric::CellSeparatorThickness; } int IllustratedListController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp index d90fc628d..3c77fb2ed 100644 --- a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp +++ b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp @@ -65,6 +65,17 @@ void ScrollableThreeExpressionsView::setCalculation(Calculation * calculation) { layoutSubviews(); } +KDCoordinate ScrollableThreeExpressionsCell::Height(Calculation * calculation) { + ScrollableThreeExpressionsCell cell; + cell.setCalculation(calculation); + KDRect leftFrame = KDRectZero; + KDRect centerFrame = KDRectZero; + KDRect approximateSignFrame = KDRectZero; + KDRect rightFrame = KDRectZero; + cell.subviewFrames(&leftFrame, ¢erFrame, &approximateSignFrame, &rightFrame); + return leftFrame.unionedWith(centerFrame).unionedWith(rightFrame).height(); +} + void ScrollableThreeExpressionsCell::didBecomeFirstResponder() { reinitSelection(); Container::activeApp()->setFirstResponder(&m_view); diff --git a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h index a7836d7f7..e8daa7f26 100644 --- a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h +++ b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.h @@ -17,6 +17,9 @@ public: } void resetMemoization(); void setCalculation(Calculation * calculation); + void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) { + return m_contentCell.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame); + } private: class ContentCell : public Shared::AbstractScrollableMultipleExpressionsView::ContentCell { public: @@ -29,12 +32,13 @@ private: }; ContentCell * contentCell() override { return &m_contentCell; }; - const ContentCell * constContentCell() const override { return &m_contentCell; }; + const ContentCell * constContentCell() const override { return &m_contentCell; }; ContentCell m_contentCell; }; class ScrollableThreeExpressionsCell : public TableCell, public Responder { public: + static KDCoordinate Height(Calculation * calculation); ScrollableThreeExpressionsCell() : Responder(nullptr), m_view(this) {} @@ -59,6 +63,9 @@ public: void setSelectedSubviewPosition(ScrollableThreeExpressionsView::SubviewPosition subviewPosition) { m_view.setSelectedSubviewPosition(subviewPosition); } void reinitSelection(); + void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) { + return m_view.subviewFrames(leftFrame, centerFrame, approximateSignFrame, rightFrame); + } private: // Remove label margin added by TableCell because they're already handled by ScrollableThreeExpressionsView KDCoordinate labelMargin() const override { return 0; } diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index d490e1412..fe3f09f2f 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -125,125 +125,12 @@ Layout Calculation::createApproximateOutputLayout(Context * context, bool * coul } } -KDCoordinate Calculation::height(Context * context, KDCoordinate topBottomMargin, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canBeSingleLine) { - /* WARNING: this method must return the same result as - * Calculation::HistoryViewCell::layoutSubviews. */ - - KDCoordinate result = expanded ? m_expandedHeight : m_height; - if (result >= 0) { - // Height already computed - return result; - } - - // Get input information - Layout inputLayout = createInputLayout(); - KDCoordinate inputHeight = inputLayout.layoutSize().height(); - KDCoordinate inputWidth = inputLayout.layoutSize().width(); - KDCoordinate inputBaseline = inputLayout.baseline(); - - // Get output information - KDCoordinate outputWidth; - KDCoordinate outputBaseline; - KDCoordinate outputHeightBelowBaseline; - - DisplayOutput displayType = displayOutput(context); - /* If the display output is ExactAndApproximateToggle, we want to use the - * expanded width to compute if the calculaton is in single line or not. */ - bool shouldComputeMaxOutputWidthForSingleLine = !forceSingleLine && (!expanded && displayType == DisplayOutput::ExactAndApproximateToggle); - - { - bool displaysExactOnly = displayType == DisplayOutput::ExactOnly; - bool displayApproximateOnly = displayType == DisplayOutput::ApproximateOnly - || (!expanded - && (displayType == DisplayOutput::ExactAndApproximateToggle)); - - KDCoordinate exactOutputWidth = 0; - KDCoordinate exactOutputBaseline = 0; - KDCoordinate exactOutputBelowBaseline = 0; - - // Get exact output info if needed - bool displaysExact = displaysExactOnly || !displayApproximateOnly; - if (displaysExact || shouldComputeMaxOutputWidthForSingleLine) { - bool couldNotCreateExactLayout = false; - Poincare::Layout exactLayout = createExactOutputLayout(&couldNotCreateExactLayout); - if (couldNotCreateExactLayout) { - if (!displaysExactOnly) { - displayType = DisplayOutput::ApproximateOnly; - forceDisplayOutput(displayType); - displaysExactOnly = false; - displayApproximateOnly = true; - shouldComputeMaxOutputWidthForSingleLine = false; - outputWidth = 0; - } else { - /* We should only display the exact result, but we cannot create it - * -> raise an exception. */ - ExceptionCheckpoint::Raise(); - } - } else { - KDSize exactSize = exactLayout.layoutSize(); - outputWidth = exactSize.width(); - if (displaysExact) { - exactOutputWidth = exactSize.width(); - exactOutputBaseline = exactLayout.baseline(); - exactOutputBelowBaseline = exactSize.height() - exactOutputBaseline; - } - } - } - - KDCoordinate approximateOutputWidth = 0; - KDCoordinate approximateOutputBaseline = 0; - KDCoordinate approximateOutputBelowBaseline = 0; - - // Get approximate output info if needed - bool displaysApproximate = displayApproximateOnly || !displaysExactOnly; - if (displaysApproximate || shouldComputeMaxOutputWidthForSingleLine) { - bool couldNotCreateApproximateLayout = false; - Layout approximateLayout = createApproximateOutputLayout(context, &couldNotCreateApproximateLayout); - if (couldNotCreateApproximateLayout) { - Poincare::ExceptionCheckpoint::Raise(); - } - KDSize approximateOutputSize = approximateLayout.layoutSize(); - outputWidth += approximateOutputSize.width(); - if (displaysApproximate) { - approximateOutputWidth = approximateOutputSize.width(); - approximateOutputBaseline = approximateLayout.baseline(); - approximateOutputBelowBaseline = approximateOutputSize.height() - approximateOutputBaseline; - } - } - - // Compute the output info - if (shouldComputeMaxOutputWidthForSingleLine || (exactOutputWidth > 0 && approximateOutputWidth > 0)) { - outputWidth += AbstractScrollableMultipleExpressionsView::StandardApproximateViewAndMarginsSize(); - } - outputBaseline = std::max(exactOutputBaseline, approximateOutputBaseline); - outputHeightBelowBaseline = std::max(exactOutputBelowBaseline, approximateOutputBelowBaseline); - } - - if (forceSingleLine || canBeSingleLine(inputWidth, outputWidth)) { - result = std::max(inputBaseline, outputBaseline) // Above the baseline - + std::max(static_cast(inputHeight - inputBaseline), outputHeightBelowBaseline) // Below the baseline - + 2 * verticalMarginAroundLayouts; +void Calculation::setMemoizedHeight(bool expanded, KDCoordinate height) { + if (expanded) { + m_expandedHeight = height; } else { - result = inputHeight + outputBaseline + outputHeightBelowBaseline + 4 * verticalMarginAroundLayouts; + m_height = height; } - - // Add the top and bottom margins - result += 2 * topBottomMargin; - - /* For all display outputs except ExactAndApproximateToggle, the selected - * height and the usual height are identical. We update both heights in - * theses cases. */ - if (displayOutput(context) != DisplayOutput::ExactAndApproximateToggle) { - m_height = result; - m_expandedHeight = result; - } else { - if (expanded) { - m_expandedHeight = result; - } else { - m_height = result; - } - } - return result; } Calculation::DisplayOutput Calculation::displayOutput(Context * context) { diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index 6f6050e45..dc1646657 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -84,8 +84,8 @@ public: Poincare::Layout createApproximateOutputLayout(Poincare::Context * context, bool * couldNotCreateApproximateLayout); // Memoization of height - typedef bool (*LayoutsCanBeSingleLineFunction)(KDCoordinate inputWidth, KDCoordinate outputWidth); - KDCoordinate height(Poincare::Context * context, KDCoordinate topBottomMargin, KDCoordinate verticalMarginAroundLayouts, bool expanded, bool forceSingleLine, LayoutsCanBeSingleLineFunction canbeSingleLine = [](KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth) { assert(false); return true; }); + KDCoordinate memoizedHeight(bool expanded) { return expanded ? m_expandedHeight : m_height; } + void setMemoizedHeight(bool expanded, KDCoordinate height); // Displayed output DisplayOutput displayOutput(Poincare::Context * context); diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 3fca09d90..5fca0aa98 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -206,13 +206,13 @@ KDCoordinate HistoryController::rowHeight(int j) { return 0; } Shared::ExpiringPointer calculation = calculationAtIndex(j); - return calculation->height( - App::app()->localContext(), - HistoryViewCell::k_margin, - HistoryViewCell::k_inputOutputViewsVerticalMargin, - j == selectedRow() && selectedSubviewType() == SubviewType::Output, - false, - &HistoryViewCell::LayoutsCanBeSingleLine); + bool expanded = j == selectedRow() && selectedSubviewType() == SubviewType::Output; + KDCoordinate result = calculation->memoizedHeight(expanded); + if (result < 0) { + result = HistoryViewCell::Height(calculation.pointer(), expanded); + calculation->setMemoizedHeight(expanded, result); + } + return result; } int HistoryController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 981a34018..239529aff 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -34,12 +34,22 @@ void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, /* HistoryViewCell */ +KDCoordinate HistoryViewCell::Height(Calculation * calculation, bool expanded) { + HistoryViewCell cell(nullptr); + cell.setCalculation(calculation, expanded); + KDRect ellipsisFrame = KDRectZero; + KDRect inputFrame = KDRectZero; + KDRect outputFrame = KDRectZero; + cell.computeSubviewFrames(Ion::Display::Width, KDCOORDINATE_MAX, &ellipsisFrame, &inputFrame, &outputFrame); + return k_margin + inputFrame.unionedWith(outputFrame).height() + k_margin; +} + HistoryViewCell::HistoryViewCell(Responder * parentResponder) : Responder(parentResponder), m_calculationCRC32(0), m_calculationDisplayOutput(Calculation::DisplayOutput::Unknown), m_calculationAdditionInformation(Calculation::AdditionalInformationType::None), - m_inputView(this, k_inputOutputViewsHorizontalMargin, k_inputOutputViewsVerticalMargin), + m_inputView(this, k_inputViewHorizontalMargin, k_inputOutputViewsVerticalMargin), m_scrollableOutputView(this), m_calculationExpanded(false), m_calculationSingleLine(false) @@ -154,23 +164,37 @@ View * HistoryViewCell::subviewAtIndex(int index) { return views[index]; } -bool HistoryViewCell::LayoutsCanBeSingleLine(KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth) { - return ViewsCanBeSingleLine(inputLayoutWidth + 2 * k_inputOutputViewsHorizontalMargin, outputLayoutWidth + 2 * k_inputOutputViewsHorizontalMargin); -} - bool HistoryViewCell::ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth) { // k_margin is the separation between the input and output. return (inputViewWidth + k_margin + outputViewWidth) < Ion::Display::Width - Metric::EllipsisCellWidth; } void HistoryViewCell::layoutSubviews(bool force) { - KDCoordinate maxFrameWidth = bounds().width(); - if (displayedEllipsis()) { - m_ellipsis.setFrame(KDRect(maxFrameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, bounds().height()), force); - maxFrameWidth -= Metric::EllipsisCellWidth; - } else { - m_ellipsis.setFrame(KDRectZero, force); // Required to mark previous rect as dirty + KDRect frameBounds = bounds(); + if (bounds().width() <= 0 || bounds().height() <= 0) { + // TODO Make this behaviour in a non-virtual layoutSublviews, and all layout subviews should become privateLayoutSubviews + return; } + KDRect ellipsisFrame = KDRectZero; + KDRect inputFrame = KDRectZero; + KDRect outputFrame = KDRectZero; + computeSubviewFrames(frameBounds.width(), frameBounds.height(), &ellipsisFrame, &inputFrame, &outputFrame); + + m_ellipsis.setFrame(ellipsisFrame, force); // Required even if ellipsisFrame is KDRectZero, to mark previous rect as dirty + m_inputView.setFrame(inputFrame,force); + m_scrollableOutputView.setFrame(outputFrame, force); +} + +void HistoryViewCell::computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame) { + assert(ellipsisFrame != nullptr && inputFrame != nullptr && outputFrame != nullptr); + + if (displayedEllipsis()) { + *ellipsisFrame = KDRect(frameWidth - Metric::EllipsisCellWidth, 0, Metric::EllipsisCellWidth, frameHeight); + frameWidth -= Metric::EllipsisCellWidth; + } else { + *ellipsisFrame = KDRectZero; + } + KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay(); KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); @@ -193,18 +217,16 @@ void HistoryViewCell::layoutSubviews(bool force) { outputY += inputSize.height(); } - m_inputView.setFrame(KDRect( + *inputFrame = KDRect( 0, inputY, - std::min(maxFrameWidth, inputSize.width()), - inputSize.height()), - force); - m_scrollableOutputView.setFrame(KDRect( - std::max(0, maxFrameWidth - outputSize.width()), + std::min(frameWidth, inputSize.width()), + inputSize.height()); + *outputFrame = KDRect( + std::max(0, frameWidth - outputSize.width()), outputY, - std::min(maxFrameWidth, outputSize.width()), - outputSize.height()), - force); + std::min(frameWidth, outputSize.width()), + outputSize.height()); } void HistoryViewCell::resetMemoization() { diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index 664f9e6ee..2cca12b62 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -33,9 +33,9 @@ class HistoryViewCell : public ::EvenOddCell, public Responder { public: constexpr static KDCoordinate k_margin = Metric::CommonSmallMargin; constexpr static KDCoordinate k_inputOutputViewsVerticalMargin = k_margin; - constexpr static KDCoordinate k_inputOutputViewsHorizontalMargin = Shared::AbstractScrollableMultipleExpressionsView::k_horizontalMargin; + constexpr static KDCoordinate k_inputViewHorizontalMargin = Shared::AbstractScrollableMultipleExpressionsView::k_horizontalMargin; + static KDCoordinate Height(Calculation * calculation, bool expanded); HistoryViewCell(Responder * parentResponder = nullptr); - static bool LayoutsCanBeSingleLine(KDCoordinate inputLayoutWidth, KDCoordinate outputLayoutWidth); static bool ViewsCanBeSingleLine(KDCoordinate inputViewWidth, KDCoordinate outputViewWidth); void cellDidSelectSubview(HistoryViewCellDataSource::SubviewType type, HistoryViewCellDataSource::SubviewType previousType = HistoryViewCellDataSource::SubviewType::None); void setEven(bool even) override; @@ -58,6 +58,7 @@ public: Calculation::AdditionalInformationType additionalInformationType() const { return m_calculationAdditionInformation; } private: constexpr static KDCoordinate k_resultWidth = 80; + void computeSubviewFrames(KDCoordinate frameWidth, KDCoordinate frameHeight, KDRect * ellipsisFrame, KDRect * inputFrame, KDRect * outputFrame); void reloadScroll(); void reloadOutputSelection(HistoryViewCellDataSource::SubviewType previousType); bool displayedEllipsis() const { diff --git a/apps/shared/scrollable_multiple_expressions_view.cpp b/apps/shared/scrollable_multiple_expressions_view.cpp index 789b0e42a..ea57f98a4 100644 --- a/apps/shared/scrollable_multiple_expressions_view.cpp +++ b/apps/shared/scrollable_multiple_expressions_view.cpp @@ -121,6 +121,41 @@ KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::baseline(KD return std::max(std::max(leftViewBaseline, centerViewBaseline), rightViewBaseline); } +void AbstractScrollableMultipleExpressionsView::ContentCell::subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame) { + // Subviews sizes + KDSize leftSize = leftExpressionView() ? leftExpressionView()->minimalSizeForOptimalDisplay() : KDSizeZero; + KDSize centerSize = displayCenter() ? m_centeredExpressionView.minimalSizeForOptimalDisplay() : KDSizeZero; + KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); + + // Compute baselines + KDCoordinate leftBaseline = 0; + KDCoordinate centerBaseline = 0; + KDCoordinate rightBaseline = 0; + KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline); + + // Layout left view + KDCoordinate currentWidth = 0; + if (leftExpressionView()) { + assert(leftFrame != nullptr); + *leftFrame = KDRect(currentWidth, viewBaseline - leftBaseline, leftSize); + currentWidth += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin; + } + + // Layout center expression + if (displayCenter()) { + assert(centerFrame != nullptr && approximateSignFrame != nullptr); + KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); + *centerFrame = KDRect(currentWidth, viewBaseline - centerBaseline, centerSize); + currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + centerSize.width(); + *approximateSignFrame = KDRect(currentWidth, viewBaseline - approximateSignSize.height()/2, approximateSignSize); + currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + approximateSignSize.width(); + } + + // Layout right expression + assert(rightFrame != nullptr); + *rightFrame = KDRect(currentWidth, viewBaseline - rightBaseline, rightSize); +} + KDSize AbstractScrollableMultipleExpressionsView::ContentCell::privateMinimalSizeForOptimalDisplay(bool forceFullDisplay) const { KDCoordinate width = 0; @@ -169,35 +204,25 @@ KDCoordinate AbstractScrollableMultipleExpressionsView::ContentCell::StandardApp } void AbstractScrollableMultipleExpressionsView::ContentCell::layoutSubviews(bool force) { - // Subviews sizes - KDSize leftSize = leftExpressionView() ? leftExpressionView()->minimalSizeForOptimalDisplay() : KDSizeZero; - KDSize centerSize = displayCenter() ? m_centeredExpressionView.minimalSizeForOptimalDisplay() : KDSizeZero; - KDSize rightSize = m_rightExpressionView.minimalSizeForOptimalDisplay(); - - // Compute baselines - KDCoordinate leftBaseline = 0; - KDCoordinate centerBaseline = 0; - KDCoordinate rightBaseline = 0; - KDCoordinate viewBaseline = baseline(&leftBaseline, ¢erBaseline, &rightBaseline); - - // Layout left view - KDCoordinate currentWidth = 0; - if (leftExpressionView()) { - leftExpressionView()->setFrame(KDRect(currentWidth, viewBaseline - leftBaseline, leftSize), force); - currentWidth += leftSize.width() + AbstractScrollableMultipleExpressionsView::k_horizontalMargin; + if (bounds().width() <= 0 || bounds().height() <= 0) { + // TODO Make this behaviour in a non-virtual layoutSublviews, and all layout subviews should become privateLayoutSubviews + return; } - - // Layout center expression - if (displayCenter()) { - KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); - m_centeredExpressionView.setFrame(KDRect(currentWidth, viewBaseline - centerBaseline, centerSize), force); - currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + centerSize.width(); - m_approximateSign.setFrame(KDRect(currentWidth, viewBaseline - approximateSignSize.height()/2, approximateSignSize), force); - currentWidth += AbstractScrollableMultipleExpressionsView::k_horizontalMargin + approximateSignSize.width(); + KDRect leftFrame = KDRectZero; + KDRect centerFrame = KDRectZero; + KDRect approximateSignFrame = KDRectZero; + KDRect rightFrame = KDRectZero; + subviewFrames(&leftFrame, ¢erFrame, &approximateSignFrame, &rightFrame); + if (leftExpressionView() != nullptr) { + leftExpressionView()->setFrame(leftFrame, force); + } + if (centeredExpressionView() != nullptr) { + centeredExpressionView()->setFrame(centerFrame, force); + } + m_approximateSign.setFrame(approximateSignFrame, force); + if (rightExpressionView() != nullptr) { + rightExpressionView()->setFrame(rightFrame, force); } - - // Layout right expression - m_rightExpressionView.setFrame(KDRect(currentWidth, viewBaseline - rightBaseline, rightSize), force); } AbstractScrollableMultipleExpressionsView::AbstractScrollableMultipleExpressionsView(Responder * parentResponder, View * contentCell) : diff --git a/apps/shared/scrollable_multiple_expressions_view.h b/apps/shared/scrollable_multiple_expressions_view.h index 5e4574415..89dc76b16 100644 --- a/apps/shared/scrollable_multiple_expressions_view.h +++ b/apps/shared/scrollable_multiple_expressions_view.h @@ -68,6 +68,7 @@ protected: int numberOfSubviews() const override; virtual Poincare::Layout layout() const override; KDCoordinate baseline(KDCoordinate * leftBaseline = nullptr, KDCoordinate * centerBaseline = nullptr, KDCoordinate * rightBaseline = nullptr) const; + void subviewFrames(KDRect * leftFrame, KDRect * centerFrame, KDRect * approximateSignFrame, KDRect * rightFrame); private: constexpr static const KDFont * k_font = KDFont::LargeFont; const static I18n::Message k_defaultApproximateMessage = I18n::Message::AlmostEqual; diff --git a/escher/src/scroll_view.cpp b/escher/src/scroll_view.cpp index 35b049198..294ec5350 100644 --- a/escher/src/scroll_view.cpp +++ b/escher/src/scroll_view.cpp @@ -117,6 +117,9 @@ KDRect ScrollView::visibleContentRect() { } void ScrollView::layoutSubviews(bool force) { + if (bounds().isEmpty()) { + return; + } KDRect r1 = KDRectZero; KDRect r2 = KDRectZero; KDRect innerFrame = decorator()->layoutIndicators(minimalSizeForOptimalDisplay(), contentOffset(), bounds(), &r1, &r2, force); diff --git a/kandinsky/src/rect.cpp b/kandinsky/src/rect.cpp index f99a09076..4b4be21c4 100644 --- a/kandinsky/src/rect.cpp +++ b/kandinsky/src/rect.cpp @@ -174,5 +174,5 @@ KDRect KDRect::movedTo(KDPoint p) const { } bool KDRect::isEmpty() const { - return (width() == 0 || height() == 0); + return (width() == 0 || height() == 0); //TODO <= 0 } From b034f8e054f89695bb0ecd2f84c01ed66b7a8792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Wed, 27 May 2020 11:52:47 +0200 Subject: [PATCH 81/85] [apps/calculation] Fix history navigation When calculations span on two lines, better selection --- apps/calculation/history_controller.cpp | 8 +++++++- apps/calculation/history_view_cell.h | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 5fca0aa98..251cc3a1f 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -169,8 +169,14 @@ void HistoryController::tableViewDidChangeSelection(SelectableTableView * t, int } else if (selectedRow() == -1) { setSelectedSubviewType(SubviewType::Input, false, previousSelectedCellX, previousSelectedCellY); } else { - setSelectedSubviewType(selectedSubviewType(), false, previousSelectedCellX, previousSelectedCellY); + HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell()); + SubviewType nextSelectedSubviewType = selectedSubviewType(); + if (!selectedCell->displaysSingleLine()) { + nextSelectedSubviewType = previousSelectedCellY < selectedRow() ? SubviewType::Input : SubviewType::Output; + } + setSelectedSubviewType(nextSelectedSubviewType, false, previousSelectedCellX, previousSelectedCellY); } + // The selectedCell may change during setSelectedSubviewType HistoryViewCell * selectedCell = (HistoryViewCell *)(t->selectedCell()); if (selectedCell == nullptr) { return; diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index 2cca12b62..d50256fc8 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -42,6 +42,9 @@ public: void setHighlighted(bool highlight) override; void reloadSubviewHighlight(); void setDataSource(HistoryViewCellDataSource * dataSource) { m_dataSource = dataSource; } + bool displaysSingleLine() const { + return m_calculationSingleLine; + } Responder * responder() override { return this; } From 899e0d4a6b1eb1655d1af9dfa9399764eb676422 Mon Sep 17 00:00:00 2001 From: Joachim Le Fournis <43498612+RedGl0w@users.noreply.github.com> Date: Thu, 28 May 2020 20:04:13 +0200 Subject: [PATCH 82/85] [apps/shared] Fixed a small mistake in parameter_text_field_delegate.cpp In this commit, there was a small oversight in https://github.com/numworks/epsilon/commit/7a4ee746b2a49a090cf920cfe53935ccdea7d607, which caused https://github.com/numworks/epsilon/issues/1570 --- apps/shared/parameter_text_field_delegate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/shared/parameter_text_field_delegate.cpp b/apps/shared/parameter_text_field_delegate.cpp index 990a0e692..c07a46a18 100644 --- a/apps/shared/parameter_text_field_delegate.cpp +++ b/apps/shared/parameter_text_field_delegate.cpp @@ -5,6 +5,7 @@ namespace Shared { bool ParameterTextFieldDelegate::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { if (event == Ion::Events::Backspace && !textField->isEditing()) { + textField->reinitDraftTextBuffer(); textField->setEditing(true); return true; } From 884d9890cf204594cf06526f611e2786dd5a3ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 2 Jun 2020 10:36:58 +0200 Subject: [PATCH 83/85] [.github/workflow] ci-workflow: update setup-emscripten to v2 --- .github/workflows/ci-workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 0526fcb14..e5c02961c 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -68,7 +68,7 @@ jobs: web: runs-on: ubuntu-latest steps: - - uses: numworks/setup-emscripten@v1 + - uses: numworks/setup-emscripten@v2 with: sdk: latest-fastcomp - uses: actions/checkout@v2 From 93d687abc261d3c30a0a8e02d295465551ed2bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 1 Jun 2020 13:42:08 +0200 Subject: [PATCH 84/85] [apps/calculation] Fix height computation in additional results --- .../additional_outputs/scrollable_three_expressions_cell.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp index 3c77fb2ed..29fe03a9a 100644 --- a/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp +++ b/apps/calculation/additional_outputs/scrollable_three_expressions_cell.cpp @@ -73,7 +73,8 @@ KDCoordinate ScrollableThreeExpressionsCell::Height(Calculation * calculation) { KDRect approximateSignFrame = KDRectZero; KDRect rightFrame = KDRectZero; cell.subviewFrames(&leftFrame, ¢erFrame, &approximateSignFrame, &rightFrame); - return leftFrame.unionedWith(centerFrame).unionedWith(rightFrame).height(); + KDRect unionedFrame = leftFrame.unionedWith(centerFrame).unionedWith(rightFrame); + return unionedFrame.height() + 2 * ScrollableThreeExpressionsView::k_margin; } void ScrollableThreeExpressionsCell::didBecomeFirstResponder() { From 043c564a25f11503a26c2259f8e303ee21aba281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 16 Jun 2020 17:45:22 +0200 Subject: [PATCH 85/85] [.github/workflow] ci-workflow: fix emscripten version to 1.39.16 (later versions removed EMTERPRETER) --- .github/workflows/ci-workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index e5c02961c..9c74c0ca0 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -70,7 +70,7 @@ jobs: steps: - uses: numworks/setup-emscripten@v2 with: - sdk: latest-fastcomp + sdk: 1.39.16-fastcomp - uses: actions/checkout@v2 - run: make -j2 PLATFORM=simulator TARGET=web - run: make -j2 PLATFORM=simulator TARGET=web epsilon.official.zip