Merge branch 'omega-hotfix' into fixedCompactCutoffBug2

This commit is contained in:
David
2020-03-18 11:59:10 -05:00
committed by GitHub
151 changed files with 1579 additions and 1268 deletions

View File

@@ -6,6 +6,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
submodules: true
- run: make -j2 PLATFORM=simulator TARGET=android
- uses: actions/upload-artifact@master
with:
@@ -17,6 +19,8 @@ jobs:
- run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
- uses: numworks/setup-arm-toolchain@v1
- uses: actions/checkout@v1
with:
submodules: true
- run: make -j2 MODEL=n0100 epsilon.dfu
- run: make -j2 MODEL=n0100 epsilon.onboarding.dfu
- run: make -j2 MODEL=n0100 epsilon.onboarding.update.dfu
@@ -34,6 +38,8 @@ jobs:
- run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
- uses: numworks/setup-arm-toolchain@v1
- uses: actions/checkout@v1
with:
submodules: true
- run: make -j2 epsilon.dfu
- run: make -j2 epsilon.onboarding.dfu
- run: make -j2 epsilon.onboarding.update.dfu
@@ -47,19 +53,6 @@ jobs:
name: epsilon-device-n0110.dfu
path: output/release/device/n0110/epsilon.dfu
- run: make -j2 test.elf
build-simulator-windows:
runs-on: windows-latest
steps:
- uses: numworks/setup-msys2@v1
- uses: actions/checkout@v1
- run: msys2do pacman -S --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-freetype mingw-w64-x86_64-pkg-config make mingw-w64-x86_64-python3 mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-libpng
- run: msys2do make -j2 PLATFORM=simulator
- uses: actions/upload-artifact@master
with:
name: epsilon-simulator-windows.exe
path: output/release/simulator/windows/epsilon.exe
- run: msys2do make -j2 PLATFORM=simulator test.headless.exe
- run: output\release\simulator\windows\test.headless.exe
build-simulator-web:
runs-on: ubuntu-latest
steps:
@@ -67,44 +60,24 @@ jobs:
with:
sdk: latest-fastcomp
- uses: actions/checkout@v1
with:
submodules: true
- run: make -j2 PLATFORM=simulator TARGET=web
- uses: actions/upload-artifact@master
with:
name: epsilon-simulator-web.zip
path: output/release/simulator/web/simulator.zip
- run: make -j2 PLATFORM=simulator TARGET=web test.headless.js
- run: node output/release/simulator/web/test.headless.js
build-simulator-linux:
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config
- uses: actions/checkout@v1
with:
submodules: true
- run: make -j2 PLATFORM=simulator
- uses: actions/upload-artifact@master
with:
name: epsilon-simulator-linux.bin
path: output/release/simulator/linux/epsilon.bin
- run: make -j2 PLATFORM=simulator test.headless.bin
- run: output/release/simulator/linux/test.headless.bin
build-simulator-macos:
runs-on: macOS-latest
steps:
- run: brew install numworks/tap/epsilon-sdk
- uses: actions/checkout@v1
- run: make -j2 PLATFORM=simulator
- uses: actions/upload-artifact@master
with:
name: epsilon-simulator-macos.zip
path: output/release/simulator/macos/app/Payload
- run: make -j2 PLATFORM=simulator ARCH=x86_64 test.headless.bin
- run: output/release/simulator/macos/x86_64/test.headless.bin
build-simulator-ios:
runs-on: macOS-latest
steps:
- run: brew install numworks/tap/epsilon-sdk
- uses: actions/checkout@v1
- run: make -j2 PLATFORM=simulator TARGET=ios EPSILON_TELEMETRY=0
- uses: actions/upload-artifact@master
with:
name: epsilon-simulator-ios.ipa
path: output/release/simulator/ios/app/epsilon.ipa

4
.gitignore vendored
View File

@@ -1,5 +1,7 @@
/output/
/build/artifacts/
build/device/**/*.pyc
epsilon.elf
.vscode
.DS_Store
.DS_Store
.gradle

View File

@@ -126,8 +126,7 @@ include build/struct_layout/Makefile
include build/scenario/Makefile
include quiz/Makefile # Quiz needs to be included at the end
all_src = $(apps_all_src) $(escher_src) $(ion_all_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(epsilon_src) $(runner_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(ion_target_device_bench_src) $(tests_src)
all_src = $(apps_all_src) $(escher_src) $(ion_all_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(runner_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(ion_target_device_bench_src) $(tests_src)
# Make palette.h a dep for every source-file.
# This ensures that the theming engine works correctly.
$(call object_for,$(all_app_src)): $(BUILD_DIR)/escher/palette.h

View File

@@ -94,7 +94,7 @@ $(BUILD_DIR)/apps/i18n.h: $(BUILD_DIR)/apps/i18n.cpp
$(eval $(call depends_on_image,apps/title_bar_view.cpp,apps/exam_icon.png))
all_app_src = $(app_src) $(epsilon_src) $(apps_launch_on_boarding_src) $(apps_launch_default_src) $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) $(apps_official) $(apps_non_official) $(tests_src)
all_app_src = $(app_src)(apps_launch_on_boarding_src) $(apps_launch_default_src) $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) $(apps_official) $(apps_non_official) $(tests_src)
$(call object_for,$(all_app_src)): $(BUILD_DIR)/apps/i18n.h
$(call object_for,$(all_app_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h

View File

@@ -6,7 +6,7 @@ using namespace Poincare;
namespace Calculation {
ComplexGraphView::ComplexGraphView(ComplexModel * complexModel) :
CurveView(complexModel),
LabeledCurveView(complexModel),
m_complex(complexModel)
{
}

View File

@@ -1,24 +1,19 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_GRAPH_CELL_H
#define CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_GRAPH_CELL_H
#include "../../shared/curve_view.h"
#include "../../shared/labeled_curve_view.h"
#include "complex_model.h"
#include "illustration_cell.h"
namespace Calculation {
class ComplexGraphView : public Shared::CurveView {
class ComplexGraphView : public Shared::LabeledCurveView {
public:
ComplexGraphView(ComplexModel * complexModel);
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
char * label(Axis axis, int index) const override {
return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]);
}
// '-' + significant digits + ".E-" + 2 digits (the represented dot is a float, so it is bounded by 1E38 and 1E-38
size_t labelMaxGlyphLengthSize() const override { return 1 + Poincare::Preferences::VeryShortNumberOfSignificantDigits + 3 + 2; }
char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
ComplexModel * m_complex;
};

View File

@@ -12,7 +12,6 @@ public:
TrigonometryGraphView(TrigonometryModel * model);
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
char * label(Axis axis, int index) const override { return nullptr; }
TrigonometryModel * m_model;
};

View File

@@ -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 = "Primfaktor"
MixedFraction = "Gemischte Fraktion"
EuclideanDivision = "Euklidische Division"

View File

@@ -186,7 +186,7 @@ void HistoryViewCell::layoutSubviews(bool force) {
force);
KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay();
int singleLine = outputSize.width() + inputSize.width() < bounds().width() - 6;
int outputHeight = (singleLine) ? (maxCoordinate(0, inputSize.height() - outputSize.height()) / 2) + maxCoordinate(0, (inputSize.height() - outputSize.height()) / 2) : inputSize.height();
int outputHeight = (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) ? (maxCoordinate(0, inputSize.height() - outputSize.height()) / 2) + maxCoordinate(0, (inputSize.height() - outputSize.height()) / 2) : inputSize.height();
m_scrollableOutputView.setFrame(KDRect(
maxCoordinate(0, maxFrameWidth - outputSize.width()),
outputHeight,

View File

@@ -49,8 +49,10 @@ bool App::Snapshot::lockOnConsole() const {
}
void App::Snapshot::setOpt(const char * name, const char * value) {
if (strcmp(name, "script") == 0) {
if (strcmp(name, "wipe") == 0) {
m_scriptStore.deleteAllScripts();
}
if (strcmp(name, "script") == 0) {
char * separator = const_cast<char *>(UTF8Helper::CodePointSearch(value, ':'));
if (*separator == 0) {
return;

View File

@@ -12,104 +12,104 @@ PythonSingleQuote = "Einfaches Anführungszeichen"
PythonAbs = "Absolute/r Wert/Größe"
PythonAcos = "Arkuskosinus"
PythonAcosh = "Hyperbelkosinus"
PythonAppend = "Add x to the end of the list"
PythonAppend = "Hängt x an das Ende der Liste"
PythonAsin = "Arkussinus"
PythonAsinh = "Hyperbelsinus"
PythonAtan = "Arkustangens"
PythonAtan2 = "Gib atan(y/x)"
PythonAtanh = "Hyperbeltangens"
PythonBin = "Ganzzahl nach binär konvertieren"
PythonBin = "Ganzzahl nach binär"
PythonCeil = "Aufrundung"
PythonChoice = "Zufallszahl aus der Liste"
PythonClear = "Empty the list"
PythonClear = "Leere die Liste"
PythonCmathFunction = "cmath-Modul-Funktionspräfix"
PythonColor = "Definiere eine RGB-Farbe"
PythonColor = "Definiert eine RGB-Farbe"
PythonComplex = "a+ib zurückgeben"
PythonCopySign = "Return x with the sign of y"
PythonCopySign = "x mit dem Vorzeichen von y"
PythonCos = "Kosinus"
PythonCosh = "Hyperbolic cosine"
PythonCount = "Count the occurrences of x"
PythonDegrees = "Convert x from radians to degrees"
PythonDivMod = "Quotient and remainder"
PythonDrawString = "Display a text from pixel (x,y)"
PythonErf = "Error function"
PythonCount = "Zählt wie oft x vorkommt"
PythonDegrees = "x von Radian zu Grad umwandeln"
PythonDivMod = "Quotient und Rest"
PythonDrawString = "Schreibt Text bei (x,y)"
PythonErf = "Fehlerfunktion"
PythonErfc = "Complementary error function"
PythonEval = "Return the evaluated expression"
PythonExp = "Exponential function"
PythonExpm1 = "Compute exp(x)-1"
PythonFabs = "Absolute value"
PythonFillRect = "Fill a rectangle at pixel (x,y)"
PythonFloat = "Convert x to a float"
PythonExp = "Exponentialfunktion"
PythonExpm1 = "Berechne exp(x)-1"
PythonFabs = "Absoluter Wert"
PythonFillRect = "Malt ein Rechteck bei Pixel (x,y)"
PythonFloat = "Wandelt x zu float um"
PythonFloor = "Floor"
PythonFmod = "a modulo b"
PythonFrExp = "Mantissa and exponent of x"
PythonGamma = "Gamma function"
PythonGetPixel = "Return pixel (x,y) color"
PythonGetrandbits = "Integer with k random bits"
PythonHex = "Convert integer to hexadecimal"
PythonImportCmath = "Import cmath module"
PythonImportIon = "Import ion module"
PythonImportKandinsky = "Import kandinsky module"
PythonImportRandom = "Import random module"
PythonImportMath = "Import math module"
PythonImportTime = "Import time module"
PythonImportTurtle = "Import turtle module"
PythonIndex = "Index of the first x occurrence"
PythonInput = "Prompt a value"
PythonInsert = "Insert x at index i in the list"
PythonInt = "Convert x to an integer"
PythonFrExp = "Rest und Exponent von x"
PythonGamma = "Gammafunktion"
PythonGetPixel = "Farbe von Pixel (x,y)"
PythonGetrandbits = "Ganzzahl mit k zufälligen Bits"
PythonHex = "Ganzzahl zu Hexadecimal"
PythonImportCmath = "cmath Modul importieren"
PythonImportIon = "ion Modul importieren"
PythonImportKandinsky = "kandinsky Modul importieren"
PythonImportRandom = "random Modul importieren"
PythonImportMath = "math Modul importieren"
PythonImportTime = "time Modul importieren"
PythonImportTurtle = "turtle Modul importieren"
PythonIndex = "Index, bei dem x zuerst vorkommt"
PythonInput = "Eingabeaufforderung"
PythonInsert = "x bei index i in der Liste einsetzen"
PythonInt = "x zu Ganzzahl"
PythonIonFunction = "ion module function prefix"
PythonIsFinite = "Check if x is finite"
PythonIsInfinite = "Check if x is infinity"
PythonIsNaN = "Check if x is a NaN"
PythonIsKeyDown = "Return True if the k key is down"
PythonIsFinite = "Prüft ob x endlich ist"
PythonIsInfinite = "Prüft ob x unendlich ist"
PythonIsNaN = "Prüft ob x NaN ist"
PythonIsKeyDown = "true wenn k gedrückt ist"
PythonKandinskyFunction = "kandinsky module function prefix"
PythonKeyLeft = "LEFT ARROW key"
PythonKeyUp = "UP ARROW key"
PythonKeyDown = "DOWN ARROW key"
PythonKeyRight = "RIGHT ARROW key"
PythonKeyOk = "OK key"
PythonKeyBack = "BACK key"
PythonKeyHome = "HOME key"
PythonKeyOnOff = "ON/OFF key"
PythonKeyShift = "SHIFT key"
PythonKeyAlpha = "ALPHA key"
PythonKeyXnt = "X,N,T key"
PythonKeyVar = "VAR key"
PythonKeyToolbox = "TOOLBOX key"
PythonKeyBackspace = "BACKSPACE key"
PythonKeyExp = "EXPONENTIAL key"
PythonKeyLn = "NATURAL LOGARITHM key"
PythonKeyLog = "DECIMAL LOGARITHM key"
PythonKeyImaginary = "IMAGINARY I key"
PythonKeyComma = "COMMA key"
PythonKeyPower = "POWER key"
PythonKeySine = "SINE key"
PythonKeyCosine = "COSINE key"
PythonKeyTangent = "TANGENT key"
PythonKeyPi = "PI key"
PythonKeySqrt = "SQUARE ROOT key"
PythonKeySquare = "SQUARE key"
PythonKeySeven = "7 key"
PythonKeyEight = "8 key"
PythonKeyNine = "9 key"
PythonKeyLeft = "Linke Pfeiltaste"
PythonKeyUp = "Pfeiltaste nach oben"
PythonKeyDown = "Pfeiltaste nach unten"
PythonKeyRight = "Rechte Pfeiltaste"
PythonKeyOk = "OK Taste"
PythonKeyBack = "ZURÜCK Taste"
PythonKeyHome = "HOME Taste"
PythonKeyOnOff = "AN/AUS Taste"
PythonKeyShift = "SHIFT Taste"
PythonKeyAlpha = "ALPHA Taste"
PythonKeyXnt = "X,N,T Taste"
PythonKeyVar = "VAR Taste"
PythonKeyToolbox = "WERKZEUGBOX Taste"
PythonKeyBackspace = "LÖSCHEN Taste"
PythonKeyExp = "EXPONENTIAL Taste"
PythonKeyLn = "NATURAL LOGARITHM Taste"
PythonKeyLog = "DECIMAL LOGARITHM Taste"
PythonKeyImaginary = "IMAGINÄRES I Taste"
PythonKeyComma = "KOMMA Taste"
PythonKeyPower = "HOCH Taste"
PythonKeySine = "SINUS Taste"
PythonKeyCosine = "COSINUS Taste"
PythonKeyTangent = "TANGENZ Taste"
PythonKeyPi = "PI Taste"
PythonKeySqrt = "WURZEL Taste"
PythonKeySquare = "QUADRAT Taste"
PythonKeySeven = "7 Taste"
PythonKeyEight = "8 Taste"
PythonKeyNine = "9 Taste"
PythonKeyLeftParenthesis = "LEFT PARENTHESIS key"
PythonKeyRightParenthesis = "RIGHT PARENTHESIS key"
PythonKeyFour = "4 key"
PythonKeyFive = "5 key"
PythonKeySix = "6 key"
PythonKeyMultiplication = "MULTIPLICATION key"
PythonKeyDivision = "DIVISION key"
PythonKeyOne = "1 key"
PythonKeyTwo = "2 key"
PythonKeyThree = "3 key"
PythonKeyPlus = "PLUS key"
PythonKeyMinus = "MINUS key"
PythonKeyZero = "0 key"
PythonKeyDot = "DOT key"
PythonKeyEe = "10 POWER X key"
PythonKeyAns = "ANS key"
PythonKeyExe = "EXE key"
PythonKeyFour = "4 Taste"
PythonKeyFive = "5 Taste"
PythonKeySix = "6 Taste"
PythonKeyMultiplication = "MULTIPLIKATIONSTASTE"
PythonKeyDivision = "DIVISIONSTASTE"
PythonKeyOne = "1 Taste"
PythonKeyTwo = "2 Taste"
PythonKeyThree = "3 Taste"
PythonKeyPlus = "PLUS Taste"
PythonKeyMinus = "MINUS Taste"
PythonKeyZero = "0 Taste"
PythonKeyDot = "PUNKT Taste"
PythonKeyEe = "10 HOCH X Taste"
PythonKeyAns = "ANS Taste"
PythonKeyExe = "EXE Taste"
PythonLdexp = "Return x*(2**i), inverse of frexp"
PythonLength = "Length of an object"
PythonLgamma = "Log-gamma function"
@@ -140,27 +140,27 @@ PythonReverse = "Reverse the elements of the list"
PythonRound = "Round to n digits"
PythonSeed = "Initialize random number generator"
PythonSetPixel = "Color pixel (x,y)"
PythonSin = "Sine"
PythonSin = "Sinus"
PythonSinh = "Hyperbolic sine"
PythonSleep = "Suspend the execution for t seconds"
PythonSort = "Sort the list"
PythonSqrt = "Square root"
PythonSqrt = "Wurzel"
PythonSum = "Sum the items of a list"
PythonTan = "Tangent"
PythonTan = "Tangens"
PythonTanh = "Hyperbolic tangent"
PythonTimeFunction = "time module function prefix"
PythonTrunc = "x truncated to an integer"
PythonTurtleBackward = "Move backward by x pixels"
PythonTurtleBlack = "Black color"
PythonTurtleBlue = "Blue color"
PythonTurtleBrown = "Brown color"
PythonTurtleBlack = "Schwarze Farbe"
PythonTurtleBlue = "Blaue Farbe"
PythonTurtleBrown = "Braune Farbe"
PythonTurtleCircle = "Circle of radius r pixels"
PythonTurtleColor = "Set the pen color"
PythonTurtleColor = "Stiftfarbe setzen"
PythonTurtleForward = "Move forward by x pixels"
PythonTurtleFunction = "turtle module function prefix"
PythonTurtleGoto = "Move to (x,y) coordinates"
PythonTurtleGreen = "Green color"
PythonTurtleGrey = "Grey color"
PythonTurtleGreen = "Grüne Farbe"
PythonTurtleGrey = "Graue Farbe"
PythonTurtleHeading = "Return the current heading"
PythonTurtleHideturtle = "Hide the turtle"
PythonTurtleIsdown = "Return True if the pen is down"
@@ -169,19 +169,19 @@ PythonTurtleOrange = "Orange color"
PythonTurtlePendown = "Pull the pen down"
PythonTurtlePensize = "Set the line thickness to x pixels"
PythonTurtlePenup = "Pull the pen up"
PythonTurtlePink = "Pink color"
PythonTurtlePink = "Pinke Farbe"
PythonTurtlePosition = "Return the current (x,y) location"
PythonTurtlePurple = "Purple color"
PythonTurtleRed = "Red color"
PythonTurtleRed = "Rote Farbe"
PythonTurtleReset = "Reset the drawing"
PythonTurtleRight = "Turn right by a degrees"
PythonTurtleSetheading = "Set the orientation to a degrees"
PythonTurtleSetposition = "Positionne la tortue"
PythonTurtleShowturtle = "Show the turtle"
PythonTurtleSpeed = "Drawing speed between 0 and 10"
PythonTurtleWhite = "White color"
PythonTurtleYellow = "Yellow color"
PythonUniform = "Floating point number in [a,b]"
PythonTurtleSetposition = "Position des turtles"
PythonTurtleShowturtle = "Die turtle anzeigen"
PythonTurtleSpeed = "Zeichengeschwindigkeit zwischen 0 und 10"
PythonTurtleWhite = "Weiße Farbe"
PythonTurtleYellow = "Gelbe Farbe"
PythonUniform = "Fließkommazahl in [a,b]"
PythonTimeFromImport = "Import time module"
PythonTimeImport = "Import time module"
PythonTimePrefix = "time module function prefix"

View File

@@ -400,6 +400,9 @@ void ConsoleController::resetSandbox() {
}
void ConsoleController::refreshPrintOutput() {
if (sandboxIsDisplayed()) {
return;
}
m_selectableTableView.reloadData();
m_selectableTableView.selectCellAtLocation(0, m_consoleStore.numberOfLines());
if (m_preventEdition) {
@@ -431,11 +434,26 @@ void ConsoleController::printText(const char * text, size_t length) {
flushOutputAccumulationBufferToStore();
micropython_port_vm_hook_refresh_print();
}
#if __EMSCRIPTEN__
/* If we called micropython_port_interrupt_if_needed here, we would need to
* put in the WHITELIST all the methods that call
* ConsoleController::printText, which means all the MicroPython methods that
* call print... This is a lot of work + might reduce the performance as
* emterpreted code is slower.
*
* We thus do not allow print interruption on the web simulator. It would be
* better to allow it, but the biggest problem was on the device anyways
* -> It is much quicker to interrupt Python on the web simulator than on the
* device.
*
* TODO: Allow print interrpution on emscripten -> maybe by using WASM=1 ? */
#else
/* micropython_port_vm_hook_loop is not enough to detect user interruptions,
* because it calls micropython_port_interrupt_if_needed every 20000
* operations, and a print operation is quite long. We thus explicitely call
* micropython_port_interrupt_if_needed here. */
micropython_port_interrupt_if_needed();
#endif
}
void ConsoleController::autoImportScript(Script script, bool force) {

View File

@@ -7,6 +7,8 @@
namespace Code {
static inline int minInt(int x, int y) { return x < y ? x : y; }
ConsoleEditCell::ConsoleEditCell(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * delegate) :
HighlightCell(),
Responder(parentResponder),
@@ -65,9 +67,12 @@ void ConsoleEditCell::clearAndReduceSize() {
const char * ConsoleEditCell::shiftCurrentTextAndClear() {
size_t previousBufferSize = m_textField.draftTextBufferSize();
m_textField.setDraftTextBufferSize(previousBufferSize + 1);
char * textFieldBuffer = m_textField.draftTextBuffer();
char * textFieldBuffer = const_cast<char *>(m_textField.text());
char * newTextPosition = textFieldBuffer + 1;
strlcpy(newTextPosition, textFieldBuffer, previousBufferSize);
assert(previousBufferSize > 0);
size_t copyLength = minInt(previousBufferSize - 1, strlen(textFieldBuffer));
memmove(newTextPosition, textFieldBuffer, copyLength);
newTextPosition[copyLength] = 0;
textFieldBuffer[0] = 0;
return newTextPosition;
}

View File

@@ -58,7 +58,7 @@ App::App(Snapshot * snapshot) :
m_listFooter(&m_listHeader, &m_listController, &m_listController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey),
m_listHeader(&m_listStackViewController, &m_listFooter, &m_listController),
m_listStackViewController(&m_tabViewController, &m_listHeader),
m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader),
m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->previousModelsVersions(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader),
m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController),
m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController),
m_graphStackViewController(&m_tabViewController, &m_graphHeader),

View File

@@ -11,8 +11,8 @@ static inline float maxFloat(float x, float y) { return x > y ? x : y; }
static inline double minDouble(double x, double y) { return x < y ? x : y; }
static inline double maxDouble(double x, double y) { return x > y ? x : y; }
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, rangeVersion, angleUnitVersion),
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, previousModelsVersions, rangeVersion, angleUnitVersion),
m_bannerView(this, inputEventHandlerDelegate, this),
m_view(curveViewRange, m_cursor, &m_bannerView, &m_cursorView),
m_graphRange(curveViewRange),

View File

@@ -15,7 +15,7 @@ namespace Graph {
class GraphController : public Shared::FunctionGraphController, public GraphControllerHelper {
public:
GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header);
GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header);
I18n::Message emptyMessage() override;
void viewWillAppear() override;
bool displayDerivativeInBanner() const { return m_displayDerivativeInBanner; }

View File

@@ -1,4 +1,4 @@
Apps = "Anwendungen"
AppsCapital = "OMEGA"
ForbidenAppInExamMode1 = "This application is"
ForbidenAppInExamMode2 = "forbidden in exam mode"
ForbidenAppInExamMode1 = "Diese Anwendung ist im"
ForbidenAppInExamMode2 = "Prüfungsmodus verboten"

View File

@@ -25,9 +25,12 @@ double Distribution::rightIntegralFromAbscissa(double x) const {
}
double Distribution::finiteIntegralBetweenAbscissas(double a, double b) const {
if (b <= a) {
if (b < a) {
return 0.0;
}
if (a == b) {
return evaluateAtDiscreteAbscissa(a);
}
if (isContinuous()) {
return cumulativeDistributiveFunctionAtAbscissa(b) - cumulativeDistributiveFunctionAtAbscissa(a);
}
@@ -100,6 +103,7 @@ double Distribution::rightIntegralInverseForProbability(double * probability) {
}
double Distribution::evaluateAtDiscreteAbscissa(int k) const {
assert(isContinuous()); // Discrete distributions override this method
return 0.0;
}

View File

@@ -19,7 +19,13 @@ void assert_cumulative_distributive_function_direct_and_inverse_is(Probability::
quiz_assert(!std::isnan(r));
quiz_assert(!std::isinf(r));
quiz_assert(std::fabs(r-x) < FLT_EPSILON || std::fabs(r-x)/x < FLT_EPSILON);
}
void assert_finite_integral_between_abscissas_is(Probability::Distribution * distribution, double a, double b, double result) {
double r = distribution->finiteIntegralBetweenAbscissas(a, b);
quiz_assert(!std::isnan(r));
quiz_assert(!std::isinf(r));
quiz_assert(std::fabs(r-result) < FLT_EPSILON || std::fabs(r-result)/result < FLT_EPSILON);
}
//TODO other distributions
@@ -37,6 +43,13 @@ QUIZ_CASE(binomial_distribution) {
distribution.setParameterAtIndex(0.1, 1);
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 0.0, 0.166771816996665822596668249389040283858776092529296875);
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.0, 0.4817852491014791294077213024138472974300384521484375);
// B(21, 0.2)
distribution.setParameterAtIndex(21.0, 0);
distribution.setParameterAtIndex(0.2, 1);
assert_finite_integral_between_abscissas_is(&distribution, 4.0, 4.0, 0.21563235015849934848);
assert_finite_integral_between_abscissas_is(&distribution, 5.0, 4.0, 0.0);
assert_finite_integral_between_abscissas_is(&distribution, 4.0, 5.0, 0.398919847793223794688);
}
QUIZ_CASE(chi_squared_distribution) {
@@ -58,6 +71,12 @@ QUIZ_CASE(chi_squared_distribution) {
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.3, 0.047059684573231390369851823152202996425330638885498046875);
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 2.9874567, 0.250530060451470470983537097708904184401035308837890625);
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 4.987, 0.53051693435084168459781039928202517330646514892578125);
// Chi Squared distribution with 6 degrees of freedom
distribution.setParameterAtIndex(6.0, 0);
assert_finite_integral_between_abscissas_is(&distribution, 4.0, 4.0, 0.0);
assert_finite_integral_between_abscissas_is(&distribution, 5.0, 4.0, 0.0);
assert_finite_integral_between_abscissas_is(&distribution, 4.0, 5.0, 0.1328633002997339414718);
}
QUIZ_CASE(student_distribution) {
@@ -79,6 +98,12 @@ QUIZ_CASE(student_distribution) {
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, -4.987, 0.00167496657737900025118837898929768925881944596767425537109375);
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.3, 0.876837383157582639370275501278229057788848876953125);
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 2.9874567, 0.98612148076325445433809591122553683817386627197265625);
// Student distribution with 6 degrees of freedom
distribution.setParameterAtIndex(6.0, 0);
assert_finite_integral_between_abscissas_is(&distribution, 4.0, 4.0, 0.0);
assert_finite_integral_between_abscissas_is(&distribution, 5.0, 4.0, 0.0);
assert_finite_integral_between_abscissas_is(&distribution, 4.0, 5.0, 0.002333318101494775250);
}
QUIZ_CASE(geometric_distribution) {
@@ -92,6 +117,12 @@ QUIZ_CASE(geometric_distribution) {
distribution.setParameterAtIndex(0.2, 0);
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 7.0, 0.8322278399999998299563230830244719982147216796875);
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 3.0, 0.5904);
// Geometric distribution with probability of success 0.4
distribution.setParameterAtIndex(0.4, 0);
assert_finite_integral_between_abscissas_is(&distribution, 1.0, 1.0, 0.24);
assert_finite_integral_between_abscissas_is(&distribution, 2.0, 1.0, 0.0);
assert_finite_integral_between_abscissas_is(&distribution, 1.0, 2.0, 0.384);
}
QUIZ_CASE(fisher_distribution) {
@@ -115,4 +146,10 @@ QUIZ_CASE(fisher_distribution) {
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.4, 0.94560850441205857);
assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.425, 0.95425004959692871775);
// Fisher distribution with d1 = 4 and d2 = 2
distribution.setParameterAtIndex(4.0, 0);
distribution.setParameterAtIndex(2.0, 1);
assert_finite_integral_between_abscissas_is(&distribution, 1.0, 1.0, 0.0);
assert_finite_integral_between_abscissas_is(&distribution, 2.0, 1.0, 0.0);
assert_finite_integral_between_abscissas_is(&distribution, 1.0, 2.0, 0.19555555555555555);
}

View File

@@ -38,7 +38,7 @@ App * App::Snapshot::unpack(Container * container) {
}
void App::Snapshot::reset() {
m_store.deleteAllPairs();
m_store.reset();
m_modelVersion = 0;
m_rangeVersion = 0;
setActiveTab(0);
@@ -59,7 +59,7 @@ App::App(Snapshot * snapshot, Poincare::Context * parentContext) :
m_calculationController(&m_calculationAlternateEmptyViewController, &m_calculationHeader, snapshot->store()),
m_calculationAlternateEmptyViewController(&m_calculationHeader, &m_calculationController, &m_calculationController),
m_calculationHeader(&m_tabViewController, &m_calculationAlternateEmptyViewController, &m_calculationController),
m_graphController(&m_graphAlternateEmptyViewController, this, &m_graphHeader, snapshot->store(), snapshot->cursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->graphSelectedDotIndex(), snapshot->selectedSeriesIndex()),
m_graphController(&m_graphAlternateEmptyViewController, this, &m_graphHeader, snapshot->store(), snapshot->cursor(), snapshot->modelVersion(), snapshot->previousModelsVersions(), snapshot->rangeVersion(), snapshot->graphSelectedDotIndex(), snapshot->selectedSeriesIndex()),
m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController),
m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController),
m_graphStackViewController(&m_tabViewController, &m_graphHeader),

View File

@@ -31,6 +31,7 @@ public:
int * graphSelectedDotIndex() { return &m_graphSelectedDotIndex; }
int * selectedSeriesIndex() { return &m_selectedSeriesIndex; }
uint32_t * modelVersion() { return &m_modelVersion; }
uint32_t * previousModelsVersions() { return m_store.seriesChecksum(); }
uint32_t * rangeVersion() { return &m_rangeVersion; }
private:
void tidy() override;

View File

@@ -14,8 +14,8 @@ static inline int maxInt(int x, int y) { return x > y ? x : y; }
namespace Regression {
GraphController::GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex) :
InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, store, &m_view, cursor, modelVersion, rangeVersion),
GraphController::GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex) :
InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, store, &m_view, cursor, modelVersion, previousModelsVersions, rangeVersion),
m_crossCursorView(),
m_roundCursorView(),
m_bannerView(this, inputEventHandlerDelegate, this),
@@ -84,18 +84,7 @@ void GraphController::viewWillAppear() {
/* Since *m_selectedDotIndex is altered by initCursorParameters(),
* the following must absolutely come at the end. */
if (*m_selectedDotIndex >= 0) {
setRoundCrossCursorView(false);
} else {
setRoundCrossCursorView(true);
m_roundCursorView.setColor(Palette::DataColor[*m_selectedSeriesIndex]);
}
}
void GraphController::selectRegressionCurve() {
*m_selectedDotIndex = -1;
setRoundCrossCursorView(true);
m_roundCursorView.setColor(Palette::DataColor[*m_selectedSeriesIndex]);
setRoundCrossCursorView();
}
// Private
@@ -324,19 +313,22 @@ bool GraphController::moveCursorVertically(int direction) {
assert(!validDot || !validRegression);
/* The model should be up to date before setting the cursor view. */
if (validRegression) {
// Select the regression
*m_selectedSeriesIndex = closestRegressionSeries;
selectRegressionCurve();
*m_selectedDotIndex = -1;
setRoundCrossCursorView();
m_cursor->moveTo(x, x, yValue(*m_selectedSeriesIndex, x, context));
return true;
}
if (validDot) {
// Select the dot
setRoundCrossCursorView(false);
*m_selectedSeriesIndex = closestDotSeries;
*m_selectedDotIndex = dotSelected;
setRoundCrossCursorView();
if (dotSelected == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) {
// Select the mean dot
double x = m_store->meanOfColumn(*m_selectedSeriesIndex, 0);
@@ -359,6 +351,11 @@ uint32_t GraphController::modelVersion() {
return m_store->storeChecksum();
}
uint32_t GraphController::modelVersionAtIndex(size_t i) {
assert(i < numberOfMemoizedVersions());
return *(m_store->seriesChecksum() + i);
}
uint32_t GraphController::rangeVersion() {
return m_store->rangeChecksum();
}
@@ -404,8 +401,16 @@ InteractiveCurveViewRangeDelegate::Range GraphController::computeYRange(Interact
return range;
}
void GraphController::setRoundCrossCursorView(bool round) {
void GraphController::setRoundCrossCursorView() {
/* At this point, the model (selected series and dot indices) should be up
* to date. */
bool round = *m_selectedDotIndex < 0;
if (round) {
// Set the color although the cursor view stays round
m_roundCursorView.setColor(Palette::DataColor[*m_selectedSeriesIndex]);
}
CursorView * nextCursorView = round ? static_cast<Shared::CursorView *>(&m_roundCursorView) : static_cast<Shared::CursorView *>(&m_crossCursorView);
// Escape if the cursor view stays the same
if (m_view.cursorView() == nextCursorView) {
return;
}

View File

@@ -16,12 +16,12 @@ namespace Regression {
class GraphController : public Shared::InteractiveCurveViewController {
public:
GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, Shared::CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex);
GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, Shared::CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex);
ViewController * initialisationParameterController() override;
bool isEmpty() const override;
I18n::Message emptyMessage() override;
void viewWillAppear() override;
void selectRegressionCurve();
void selectRegressionCurve() { *m_selectedDotIndex = -1; }
int selectedSeriesIndex() const { return *m_selectedSeriesIndex; }
// moveCursorHorizontally and Vertically are public to be used in tests
@@ -43,7 +43,9 @@ private:
// InteractiveCurveViewController
void initCursorParameters() override;
uint32_t modelVersion() override;
uint32_t modelVersionAtIndex(size_t i) override;
uint32_t rangeVersion() override;
size_t numberOfMemoizedVersions() const override { return Store::k_numberOfSeries; }
int selectedCurveIndex() const override { return *m_selectedSeriesIndex; }
bool closestCurveIndexIsSuitable(int newIndex, int currentIndex) const override;
Poincare::Coordinate2D<double> xyValues(int curveIndex, double x, Poincare::Context * context) const override;
@@ -55,7 +57,7 @@ private:
// InteractiveCurveViewRangeDelegate
Shared::InteractiveCurveViewRangeDelegate::Range computeYRange(Shared::InteractiveCurveViewRange * interactiveCurveViewRange) override;
void setRoundCrossCursorView(bool round);
void setRoundCrossCursorView();
Shared::CursorView m_crossCursorView;
Shared::RoundCursorView m_roundCursorView;
BannerView m_bannerView;

View File

@@ -9,10 +9,8 @@ using namespace Shared;
namespace Regression {
GraphView::GraphView(Store * store, CurveViewCursor * cursor, BannerView * bannerView, Shared::CursorView * cursorView) :
CurveView(store, cursor, bannerView, cursorView),
m_store(store),
m_xLabels{},
m_yLabels{}
LabeledCurveView(store, cursor, bannerView, cursorView),
m_store(store)
{
}
@@ -41,13 +39,4 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const {
}
}
char * GraphView::label(Axis axis, int index) const {
if (axis == Axis::Vertical) {
assert(index < k_maxNumberOfXLabels);
return (char *)m_yLabels[index];
}
assert(index < k_maxNumberOfYLabels);
return (char *)m_xLabels[index];
}
}

View File

@@ -3,19 +3,16 @@
#include "store.h"
#include "../constant.h"
#include "../shared/curve_view.h"
#include "../shared/labeled_curve_view.h"
namespace Regression {
class GraphView : public Shared::CurveView {
class GraphView : public Shared::LabeledCurveView {
public:
GraphView(Store * store, Shared::CurveViewCursor * cursor, Shared::BannerView * bannerView, Shared::CursorView * cursorView);
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
char * label(Axis axis, int index) const override;
Store * m_store;
char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
};
}

View File

@@ -19,13 +19,14 @@ static_assert(Store::k_numberOfSeries == 3, "Number of series changed, Regressio
Store::Store() :
InteractiveCurveViewRange(),
DoublePairStore(),
m_seriesChecksum{0, 0, 0},
m_angleUnit(Poincare::Preferences::AngleUnit::Degree)
{
for (int i = 0; i < k_numberOfSeries; i++) {
m_regressionTypes[i] = Model::Type::Linear;
m_regressionChanged[i] = false;
}
resetMemoization();
}
void Store::reset() {
deleteAllPairs();
resetMemoization();
}
void Store::tidy() {
@@ -188,6 +189,13 @@ double Store::doubleCastedNumberOfPairsOfSeries(int series) const {
return DoublePairStore::numberOfPairsOfSeries(series);
}
void Store::resetMemoization() {
assert(((int)Model::Type::Linear) == 0);
memset(m_seriesChecksum, 0, sizeof(m_seriesChecksum));
memset(m_regressionTypes, 0, sizeof(m_regressionTypes));
memset(m_regressionChanged, 0, sizeof(m_regressionChanged));
}
float Store::maxValueOfColumn(int series, int i) const {
float maxColumn = -FLT_MAX;
for (int k = 0; k < numberOfPairsOfSeries(series); k++) {

View File

@@ -22,6 +22,7 @@ class Store : public Shared::InteractiveCurveViewRange, public Shared::DoublePai
public:
Store();
void reset();
// Clean pool
void tidy();
@@ -35,6 +36,7 @@ public:
assert((int)m_regressionTypes[series] >= 0 && (int)m_regressionTypes[series] < Model::k_numberOfModels);
return regressionModel((int)m_regressionTypes[series]);
}
uint32_t * seriesChecksum() { return m_seriesChecksum; }
// Dots
/* Return the closest dot to abscissa x above the regression curve if
@@ -70,6 +72,7 @@ public:
double squaredCorrelationCoefficient(int series) const;
private:
constexpr static float k_displayHorizontalMarginRatio = 0.05f;
void resetMemoization();
float maxValueOfColumn(int series, int i) const; //TODO LEA why float ?
float minValueOfColumn(int series, int i) const; //TODO LEA why float ?
Model * regressionModel(int index);

View File

@@ -55,7 +55,7 @@ App::App(Snapshot * snapshot) :
m_listFooter(&m_listHeader, &m_listController, &m_listController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey),
m_listHeader(nullptr, &m_listFooter, &m_listController),
m_listStackViewController(&m_tabViewController, &m_listHeader),
m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->functionStore(), snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader),
m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->functionStore(), snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->previousModelsVersions(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader),
m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController),
m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController),
m_graphStackViewController(&m_tabViewController, &m_graphHeader),

View File

@@ -11,8 +11,8 @@ namespace Sequence {
static inline int minInt(int x, int y) { return (x < y ? x : y); }
static inline int maxInt(int x, int y) { return (x > y ? x : y); }
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, graphRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, rangeVersion, angleUnitVersion),
GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) :
FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, graphRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, previousModelsVersions, rangeVersion, angleUnitVersion),
m_bannerView(this, inputEventHandlerDelegate, this),
m_view(sequenceStore, graphRange, m_cursor, &m_bannerView, &m_cursorView),
m_graphRange(graphRange),

View File

@@ -14,7 +14,7 @@ namespace Sequence {
class GraphController final : public Shared::FunctionGraphController {
public:
GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header);
GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header);
I18n::Message emptyMessage() override;
void viewWillAppear() override;
TermSumController * termSumController() { return &m_termSumController; }

View File

@@ -28,7 +28,6 @@ void Sequence::tidy() {
m_firstInitialCondition.tidyName();
m_secondInitialCondition.tidy();
m_secondInitialCondition.tidyName();
m_nameLayout = Layout();
}
Sequence::Type Sequence::type() const {
@@ -81,13 +80,10 @@ void Sequence::setInitialRank(int rank) {
}
Poincare::Layout Sequence::nameLayout() {
if (m_nameLayout.isUninitialized()) {
m_nameLayout = HorizontalLayout::Builder(
CodePointLayout::Builder(fullName()[0], KDFont::SmallFont),
VerticalOffsetLayout::Builder(CodePointLayout::Builder(symbol(), KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Subscript)
);
}
return m_nameLayout;
return HorizontalLayout::Builder(
CodePointLayout::Builder(fullName()[0], KDFont::SmallFont),
VerticalOffsetLayout::Builder(CodePointLayout::Builder(symbol(), KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Subscript)
);
}
bool Sequence::isDefined() {

View File

@@ -24,8 +24,9 @@ public:
DoubleRecurrence = 2
};
Sequence(Ion::Storage::Record record = Record()) :
Function(record),
m_nameLayout() {}
Function(record)
{
}
I18n::Message parameterMessageName() const override;
CodePoint symbol() const override { return 'n'; }
void tidy() override;
@@ -111,7 +112,7 @@ private:
public:
SequenceModel() : Shared::ExpressionModel(), m_name() {}
void tidyName() { m_name = Poincare::Layout(); }
virtual Poincare::Layout name(Sequence * sequence);
Poincare::Layout name(Sequence * sequence);
protected:
virtual void buildName(Sequence * sequence) = 0;
Poincare::Layout m_name;
@@ -153,7 +154,6 @@ private:
DefinitionModel m_definition;
FirstInitialConditionModel m_firstInitialCondition;
SecondInitialConditionModel m_secondInitialCondition;
Poincare::Layout m_nameLayout;
};
}

View File

@@ -7,7 +7,7 @@ EditionLinear = "Linear "
Edition2D = "Natürlich "
ComplexFormat = "Komplex"
ExamMode = "Testmodus"
ExamModeActive = "Wieder starten Testmodus"
ExamModeActive = "Testmodus neustarten"
ToDeactivateExamMode1 = "Um den Testmodus auszuschalten,"
ToDeactivateExamMode2 = "schließen Sie den Rechner an einen"
ToDeactivateExamMode3 = "Computer oder eine Steckdose an."
@@ -36,28 +36,15 @@ SoftwareVersion = "Epsilon version"
CustomSoftwareVersion = "Omega version"
Username = "Name"
MicroPythonVersion = "µPythonversion"
ResultDisplay = "Resultaatweergave"
DefaultResult = "Standaard "
ResultDisplay = "Ergebniswiedergabe"
DefaultResult = "Standard "
CompactResult = "Compact "
FontSizes = "Python Schriftgröße"
LargeFont = "Große "
SmallFont = "Kleine "
LargeFont = "Groß "
SmallFont = "Klein "
SerialNumber = "Seriennummer"
UpdatePopUp = "Erinnerung: Update"
BetaPopUp = "Beta pop-up"
LEDColor = "LEDs farbe"
ExamModeMode = "Modus"
ExamModeModeStandard = "Standard "
ExamModeModeNoSym = "Ohne symbolisch "
ExamModeModeNoSymNoText = "No sym no text "
ExamModeModeDutch = "Niederländisch "
ColorRed = "Rot "
ColorWhite = "Weiss "
ColorBlue = "Blau "
ColorGreen = "Grün "
ColorYellow = "Gelb "
ColorPurple = "Lila "
ColorOrange = "Orange "
Contributors = "Beiträger"
Accessibility = "Barrierefreiheit"
AccessibilityInvertColors = "Farbumkehrung"
@@ -66,13 +53,13 @@ AccessibilityGamma = "Gammakorrektur"
AccessibilityGammaRed = "Rotes Gamma"
AccessibilityGammaGreen = "Grünes Gamma"
AccessibilityGammaBlue = "Blaues Gamma"
MathOptions = "Mathe-optionen"
MathOptions = "Berechnungseinstellungen"
SymbolMultiplication = "Multiplikation"
SymbolMultiplicationCross = "Kreuz "
SymbolMultiplicationMiddleDot = "Mittelpunkt "
SymbolMultiplicationStar = "Stern "
SymbolMultiplicationAutoSymbol = "automatisch "
PythonFont = "Python schriftart"
PythonFont = "Python Schriftart"
Large = "Groß "
Small = "Klein "
MemUse = "Speicher"

View File

@@ -45,19 +45,6 @@ SmallFont = "Small "
SerialNumber = "Serial number"
UpdatePopUp = "Update pop-up"
BetaPopUp = "Beta pop-up"
LEDColor = "LED color"
ExamModeMode = "Mode"
ExamModeModeStandard = "Standard "
ExamModeModeNoSym = "No sym "
ExamModeModeNoSymNoText = "No sym no text "
ExamModeModeDutch = "Dutch "
ColorRed = "Red "
ColorWhite = "White "
ColorBlue = "Blue "
ColorGreen = "Green "
ColorYellow = "Yellow "
ColorPurple = "Purple "
ColorOrange = "Orange "
Contributors = "Contributors"
Accessibility = "Accessibility"
AccessibilityInvertColors = "Invert colors"

View File

@@ -45,19 +45,6 @@ SmallFont = "Pequeño "
SerialNumber = "Número serie"
UpdatePopUp = "Pop-up de actualización"
BetaPopUp = "Beta pop-up"
LEDColor = "Color del LED"
ExamModeMode = "Modo"
ExamModeModeStandard = "Estándar "
ExamModeModeNoSym = "Sin simbólico "
ExamModeModeNoSymNoText = "sin simbolismo sin texto "
ExamModeModeDutch = "Holandés "
ColorRed = "Rojo "
ColorWhite = "Blanco "
ColorBlue = "Azul "
ColorGreen = "Verde "
ColorYellow = "Amarillo "
ColorPurple = "Púrpura "
ColorOrange = "Naranja "
Contributors = "Contribuyentes"
Accessibility = "Accesibilidad"
AccessibilityInvertColors = "Colores invertidos"

View File

@@ -45,19 +45,6 @@ SmallFont = "Petit "
SerialNumber = "Numéro série"
UpdatePopUp = "Rappel mise à jour"
BetaPopUp = "Rappel version bêta"
LEDColor = "Couleur LED"
ExamModeMode = "Mode"
ExamModeModeStandard = "Standard "
ExamModeModeNoSym = "Sans symbolique "
ExamModeModeNoSymNoText = "Sans symbolique ni texte "
ExamModeModeDutch = "Néerlandais "
ColorRed = "Rouge "
ColorWhite = "Blanc "
ColorBlue = "Bleu "
ColorGreen = "Vert "
ColorYellow = "Jaune "
ColorPurple = "Mauve "
ColorOrange = "Orange "
Contributors = "Contributeurs"
Accessibility = "Accessibilité"
AccessibilityInvertColors = "Inverser couleurs"

View File

@@ -45,19 +45,6 @@ SmallFont = "Kicsi "
SerialNumber = "Sorozatszám"
UpdatePopUp = "Elöugró ablak frissítése"
BetaPopUp = "Béta pop-up"
LEDColor = "LED szín"
ExamModeMode = "Üzemmód"
ExamModeModeStandard = "Normál"
ExamModeModeNoSym = "Nincs sym"
ExamModeModeNoSymNoText = "Nincs szimbolikus, nincs szöveg "
ExamModeModeDutch = "Holland "
ColorRed = "Piros "
ColorWhite = "Fehér "
ColorBlue = "Kék "
ColorGreen = "Zöld "
ColorYellow = "Sárga "
ColorPurple = "Lila "
ColorOrange = "Narancssárga "
Contributors = "Közremüködök"
Accessibility = "Hozzáférhetöség"
AccessibilityInvertColors = "Invertált színek"

View File

@@ -45,19 +45,6 @@ SmallFont = "Pequeno "
SerialNumber = "Número serie"
UpdatePopUp = "Alertas de atualização"
BetaPopUp = "Beta pop-up"
LEDColor = "Cor LED"
ExamModeMode = "Modo"
ExamModeModeStandard = "Padrão "
ExamModeModeNoSym = "Sem simbólico "
ExamModeModeNoSymNoText = "Sem simbólico sem texto "
ExamModeModeDutch = "Holandês "
ColorRed = "Vermelho "
ColorWhite = "Branco "
ColorBlue = "Azul "
ColorGreen = "Verde "
ColorYellow = "Amarelo "
ColorPurple = "Roxo "
ColorOrange = "Caranja "
Contributors = "Contribuidores"
Accessibility = "Acessibilidade"
AccessibilityInvertColors = "Cores invertidas"

View File

@@ -82,3 +82,16 @@ XStart = "X Startwert"
Zoom = "Zoom"
Developers = "Entwickler"
BetaTesters = "Beta-Tester"
LEDColor = "LED Farbe"
ExamModeMode = "Modus"
ExamModeModeStandard = "Standard "
ExamModeModeNoSym = "Ohne Symbole "
ExamModeModeNoSymNoText = "Ohne Symbole & Text "
ExamModeModeDutch = "Niederländisch "
ColorRed = "Rot "
ColorWhite = "Weiss "
ColorBlue = "Blau "
ColorGreen = "Grün "
ColorYellow = "Gelb "
ColorPurple = "Lila "
ColorOrange = "Orange "

View File

@@ -82,3 +82,16 @@ XStart = "X start"
Zoom = "Zoom"
Developers = "Developers"
BetaTesters = "Beta testers"
LEDColor = "LED color"
ExamModeMode = "Mode"
ExamModeModeStandard = "Standard "
ExamModeModeNoSym = "No sym "
ExamModeModeNoSymNoText = "No sym no text "
ExamModeModeDutch = "Dutch "
ColorRed = "Red "
ColorWhite = "White "
ColorBlue = "Blue "
ColorGreen = "Green "
ColorYellow = "Yellow "
ColorPurple = "Purple "
ColorOrange = "Orange "

View File

@@ -82,3 +82,16 @@ XStart = "X inicio"
Zoom = "Zoom"
Developers = "Desarrolladores"
BetaTesters = "Probadores beta"
LEDColor = "Color del LED"
ExamModeMode = "Modo"
ExamModeModeStandard = "Estándar "
ExamModeModeNoSym = "Sin simbólico "
ExamModeModeNoSymNoText = "sin simbolismo sin texto "
ExamModeModeDutch = "Holandés "
ColorRed = "Rojo "
ColorWhite = "Blanco "
ColorBlue = "Azul "
ColorGreen = "Verde "
ColorYellow = "Amarillo "
ColorPurple = "Púrpura "
ColorOrange = "Naranja "

View File

@@ -82,3 +82,16 @@ XStart = "X début"
Zoom = "Zoom"
Developers = "Développeurs"
BetaTesters = "Beta testeurs"
LEDColor = "Couleur LED"
ExamModeMode = "Mode"
ExamModeModeStandard = "Standard "
ExamModeModeNoSym = "Sans symbolique "
ExamModeModeNoSymNoText = "Sans symbolique ni texte "
ExamModeModeDutch = "Néerlandais "
ColorRed = "Rouge "
ColorWhite = "Blanc "
ColorBlue = "Bleu "
ColorGreen = "Vert "
ColorYellow = "Jaune "
ColorPurple = "Mauve "
ColorOrange = "Orange "

View File

@@ -82,3 +82,16 @@ XStart = "X kezdete"
Zoom = "Zoom"
Developers = "Fejlesztök"
BetaTesters = "Béta tesztelök"
LEDColor = "LED szín"
ExamModeMode = "Üzemmód"
ExamModeModeStandard = "Normál"
ExamModeModeNoSym = "Nincs sym"
ExamModeModeNoSymNoText = "Nincs szimbolikus, nincs szöveg "
ExamModeModeDutch = "Holland "
ColorRed = "Piros "
ColorWhite = "Fehér "
ColorBlue = "Kék "
ColorGreen = "Zöld "
ColorYellow = "Sárga "
ColorPurple = "Lila "
ColorOrange = "Narancssárga "

View File

@@ -82,3 +82,16 @@ XStart = "X inicio"
Zoom = "Zoom"
Developers = "Desenvolvedores"
BetaTesters = "Testadores beta"
LEDColor = "Cor LED"
ExamModeMode = "Modo"
ExamModeModeStandard = "Padrão "
ExamModeModeNoSym = "Sem simbólico "
ExamModeModeNoSymNoText = "Sem simbólico sem texto "
ExamModeModeDutch = "Holandês "
ColorRed = "Vermelho "
ColorWhite = "Branco "
ColorBlue = "Azul "
ColorGreen = "Verde "
ColorYellow = "Amarelo "
ColorPurple = "Roxo "
ColorOrange = "Caranja "

View File

@@ -45,6 +45,7 @@ app_shared_src = $(addprefix apps/shared/,\
interactive_curve_view_controller.cpp \
interval.cpp \
interval_parameter_controller.cpp \
labeled_curve_view.cpp \
language_controller.cpp \
layout_field_delegate.cpp \
list_parameter_controller.cpp \

View File

@@ -1,11 +1,12 @@
#ifndef SHARED_CURSOR_VIEW_H
#define SHARED_CURSOR_VIEW_H
#include <escher.h>
#include <escher/transparent_view.h>
#include <escher/palette.h>
namespace Shared {
class CursorView : public View {
class CursorView : public TransparentView {
public:
virtual void setCursorFrame(KDRect frame, bool force) { View::setFrame(frame, force); }
void drawRect(KDContext * ctx, KDRect rect) const override;

View File

@@ -106,11 +106,11 @@ void CurveView::setOkView(View * okView) {
* m_frame.height() - 1 yMin()
*/
const float CurveView::pixelWidth() const {
float CurveView::pixelWidth() const {
return (m_curveViewRange->xMax() - m_curveViewRange->xMin()) / (m_frame.width() - 1);
}
const float CurveView::pixelHeight() const {
float CurveView::pixelHeight() const {
return (m_curveViewRange->yMax() - m_curveViewRange->yMin()) / (m_frame.height() - 1);
}

View File

@@ -38,8 +38,8 @@ public:
void setBannerView(View * bannerView);
void setOkView(View * okView);
void setForceOkDisplay(bool force) { m_forceOkDisplay = force; }
const float pixelWidth() const;
const float pixelHeight() const;
float pixelWidth() const;
float pixelHeight() const;
protected:
CurveViewRange * curveViewRange() const { return m_curveViewRange; }
void setCurveViewRange(CurveViewRange * curveViewRange);
@@ -100,7 +100,7 @@ private:
float min(Axis axis) const;
float max(Axis axis) const;
float gridUnit(Axis axis) const;
virtual char * label(Axis axis, int index) const = 0;
virtual char * label(Axis axis, int index) const { return nullptr; }
virtual size_t labelMaxGlyphLengthSize() const { return k_labelBufferMaxGlyphLength; }
int numberOfLabels(Axis axis) const;
/* Recursively join two dots (dichotomy). The method stops when the

View File

@@ -17,8 +17,8 @@ public:
virtual float xMax() const = 0;
virtual float yMin() const = 0;
virtual float yMax() const = 0;
const float xCenter() const { return (xMin() + xMax()) / 2; }
const float yCenter() const { return (yMin() + yMax()) / 2; }
float xCenter() const { return (xMin() + xMax()) / 2; }
float yCenter() const { return (yMin() + yMax()) / 2; }
virtual float xGridUnit() const {
return computeGridUnit(k_minNumberOfXGridUnits, k_maxNumberOfXGridUnits, xMax() - xMin());
}

View File

@@ -11,11 +11,14 @@ FunctionApp::Snapshot::Snapshot() :
m_rangeVersion(0),
m_angleUnitVersion(Preferences::AngleUnit::Radian)
{
assert(m_previousModelsVersions[0] == 0);
}
void FunctionApp::Snapshot::reset() {
m_indexFunctionSelectedByCursor = 0;
m_modelVersion = 0;
assert(sizeof(m_previousModelsVersions) == sizeof(uint32_t) * FunctionGraphController::sNumberOfMemoizedModelVersions);
memset(m_previousModelsVersions, 0, sizeof(m_previousModelsVersions));
m_rangeVersion = 0;
setActiveTab(0);
}

View File

@@ -2,6 +2,7 @@
#define SHARED_FUNCTION_APP_H
#include "expression_field_delegate_app.h"
#include "function_graph_controller.h"
#include "function_store.h"
#include "curve_view_cursor.h"
#include "values_controller.h"
@@ -15,6 +16,7 @@ public:
Snapshot();
CurveViewCursor * cursor() { return &m_cursor; }
uint32_t * modelVersion() { return &m_modelVersion; }
uint32_t * previousModelsVersions() { return m_previousModelsVersions; }
uint32_t * rangeVersion() { return &m_rangeVersion; }
Poincare::Preferences::AngleUnit * angleUnitVersion() { return &m_angleUnitVersion; }
virtual FunctionStore * functionStore() = 0;
@@ -26,6 +28,7 @@ public:
private:
int m_indexFunctionSelectedByCursor;
uint32_t m_modelVersion;
uint32_t m_previousModelsVersions[FunctionGraphController::sNumberOfMemoizedModelVersions];
uint32_t m_rangeVersion;
Poincare::Preferences::AngleUnit m_angleUnitVersion;
};

View File

@@ -15,8 +15,8 @@ static inline float maxFloat(float x, float y) { return x > y ? x : y; }
static inline double minDouble(double x, double y) { return x < y ? x : y; }
static inline double maxDouble(double x, double y) { return x > y ? x : y; }
FunctionGraphController::FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion) :
InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, interactiveRange, curveView, cursor, modelVersion, rangeVersion),
FunctionGraphController::FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion) :
InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, interactiveRange, curveView, cursor, modelVersion, previousModelsVersions, rangeVersion),
m_initialisationParameterController(this, interactiveRange),
m_angleUnitVersion(angleUnitVersion),
m_indexFunctionSelectedByCursor(indexFunctionSelectedByCursor)
@@ -183,6 +183,10 @@ uint32_t FunctionGraphController::modelVersion() {
return functionStore()->storeChecksum();
}
uint32_t FunctionGraphController::modelVersionAtIndex(size_t i) {
return functionStore()->storeChecksumAtIndex(i);
}
uint32_t FunctionGraphController::rangeVersion() {
return interactiveCurveViewRange()->rangeChecksum();
}

View File

@@ -13,7 +13,8 @@ namespace Shared {
class FunctionGraphController : public InteractiveCurveViewController, public FunctionBannerDelegate {
public:
FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion);
static constexpr size_t sNumberOfMemoizedModelVersions = 5;
FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion);
bool isEmpty() const override;
ViewController * initialisationParameterController() override;
void didBecomeFirstResponder() override;
@@ -49,7 +50,9 @@ private:
// InteractiveCurveViewController
bool moveCursorVertically(int direction) override;
uint32_t modelVersion() override;
uint32_t modelVersionAtIndex(size_t i) override;
uint32_t rangeVersion() override;
size_t numberOfMemoizedVersions() const override { return sNumberOfMemoizedModelVersions; }
InitialisationParameterController m_initialisationParameterController;
Poincare::Preferences::AngleUnit * m_angleUnitVersion;

View File

@@ -8,13 +8,11 @@ namespace Shared {
FunctionGraphView::FunctionGraphView(InteractiveCurveViewRange * graphRange,
CurveViewCursor * cursor, BannerView * bannerView, CursorView * cursorView) :
CurveView(graphRange, cursor, bannerView, cursorView),
LabeledCurveView(graphRange, cursor, bannerView, cursorView),
m_selectedRecord(),
m_highlightedStart(NAN),
m_highlightedEnd(NAN),
m_shouldColorHighlighted(false),
m_xLabels{},
m_yLabels{},
m_context(nullptr)
{
}
@@ -67,10 +65,6 @@ void FunctionGraphView::setAreaHighlightColor(bool highlightColor) {
}
}
char * FunctionGraphView::label(Axis axis, int index) const {
return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]);
}
void FunctionGraphView::reloadBetweenBounds(float start, float end) {
if (start == end) {
return;

View File

@@ -2,14 +2,14 @@
#define SHARED_FUNCTION_GRAPH_VIEW_H
#include <escher.h>
#include "curve_view.h"
#include "labeled_curve_view.h"
#include "function.h"
#include "../constant.h"
#include "interactive_curve_view_range.h"
namespace Shared {
class FunctionGraphView : public CurveView {
class FunctionGraphView : public LabeledCurveView {
public:
FunctionGraphView(InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor,
BannerView * bannerView, CursorView * cursorView);
@@ -26,9 +26,6 @@ protected:
float m_highlightedEnd;
bool m_shouldColorHighlighted;
private:
char * label(Axis axis, int index) const override;
char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
Poincare::Context * m_context;
};

View File

@@ -6,4 +6,11 @@ uint32_t FunctionStore::storeChecksum() {
return Ion::Storage::sharedStorage()->checksum();
}
uint32_t FunctionStore::storeChecksumAtIndex(size_t i) {
if (numberOfActiveFunctions() <= i) {
return 0;
}
return activeRecordAtIndex(i).checksum();
}
}

View File

@@ -13,6 +13,7 @@ class FunctionStore : public ExpressionModelStore {
public:
FunctionStore() : ExpressionModelStore() {}
uint32_t storeChecksum();
uint32_t storeChecksumAtIndex(size_t i);
int numberOfActiveFunctions() const {
return numberOfModelsSatisfyingTest(&isFunctionActive, nullptr);
}

View File

@@ -7,10 +7,11 @@ using namespace Poincare;
namespace Shared {
InteractiveCurveViewController::InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion) :
InteractiveCurveViewController::InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion) :
SimpleInteractiveCurveViewController(parentResponder, cursor),
ButtonRowDelegate(header, nullptr),
m_modelVersion(modelVersion),
m_previousModelsVersions(previousModelsVersions),
m_rangeVersion(rangeVersion),
m_rangeParameterController(this, inputEventHandlerDelegate, interactiveRange),
m_zoomParameterController(this, interactiveRange, curveView),
@@ -132,11 +133,43 @@ Responder * InteractiveCurveViewController::defaultController() {
return tabController();
}
bool InteractiveCurveViewController::previousModelsWereAllDeleted() {
bool result = true;
const int modelsCount = numberOfCurves();
const int memoizationCount = numberOfMemoizedVersions();
// Look for a current model that is the same as in the previous version
for (int i = 0; i < modelsCount; i++) {
uint32_t currentVersion = modelVersionAtIndex(i);
for (int j = 0; j < memoizationCount; j++) {
uint32_t * previousVersion = m_previousModelsVersions + j;
if (currentVersion == *previousVersion) {
result = false;
break;
}
}
if (!result) {
break;
}
}
// Update the memoization
for (int i = 0; i < memoizationCount; i++) {
uint32_t * previousVersion = m_previousModelsVersions + i;
uint32_t newVersion = modelVersionAtIndex(i);
if (*previousVersion != newVersion) {
*previousVersion = newVersion;
}
}
return result;
}
void InteractiveCurveViewController::viewWillAppear() {
SimpleInteractiveCurveViewController::viewWillAppear();
uint32_t newModelVersion = modelVersion();
if (*m_modelVersion != newModelVersion) {
if (*m_modelVersion == 0 || numberOfCurves() == 1 || shouldSetDefaultOnModelChange()) {
// Put previousModelsWereAllDeleted first to update the model versions
if (previousModelsWereAllDeleted() || *m_modelVersion == 0 || numberOfCurves() == 1 || shouldSetDefaultOnModelChange()) {
interactiveCurveViewRange()->setDefault();
}
*m_modelVersion = newModelVersion;

View File

@@ -12,7 +12,7 @@ namespace Shared {
class InteractiveCurveViewController : public SimpleInteractiveCurveViewController, public InteractiveCurveViewRangeDelegate, public ButtonRowDelegate, public AlternateEmptyViewDefaultDelegate {
public:
InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion);
InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion);
const char * title() override;
bool handleEvent(Ion::Events::Event event) override;
@@ -28,6 +28,8 @@ public:
Responder * defaultController() override;
bool previousModelsWereAllDeleted();
void viewWillAppear() override;
void viewDidDisappear() override;
void willExitResponderChain(Responder * nextFirstResponder) override;
@@ -39,6 +41,7 @@ protected:
virtual void initCursorParameters() = 0;
virtual bool moveCursorVertically(int direction) = 0;
virtual uint32_t modelVersion() = 0;
virtual uint32_t modelVersionAtIndex(size_t i) = 0;
virtual uint32_t rangeVersion() = 0;
bool isCursorVisible();
@@ -66,7 +69,9 @@ private:
float addMargin(float x, float range, bool isVertical, bool isMin) override;
virtual bool shouldSetDefaultOnModelChange() const { return false; }
virtual size_t numberOfMemoizedVersions() const = 0;
uint32_t * m_modelVersion;
uint32_t * m_previousModelsVersions;
uint32_t * m_rangeVersion;
RangeParameterController m_rangeParameterController;
ZoomParameterController m_zoomParameterController;

View File

@@ -0,0 +1,31 @@
#include "labeled_curve_view.h"
namespace Shared {
char * HorizontallyLabeledCurveView::label(Axis axis, int index) const {
if (axis == Axis::Horizontal) {
assert(index < k_maxNumberOfXLabels);
return m_xLabels[index];
}
return nullptr;
}
char * VerticallyLabeledCurveView::label(Axis axis, int index) const {
if (axis == Axis::Vertical) {
assert(index < k_maxNumberOfYLabels);
return m_yLabels[index];
}
return nullptr;
}
char * LabeledCurveView::label(Axis axis, int index) const {
if (axis == Axis::Horizontal) {
assert(index < k_maxNumberOfXLabels);
return m_xLabels[index];
} else {
assert(index < k_maxNumberOfYLabels);
return m_yLabels[index];
}
}
}

View File

@@ -0,0 +1,37 @@
#ifndef SHARED_LABELED_CURVE_VIEW_H
#define SHARED_LABELED_CURVE_VIEW_H
#include "curve_view.h"
/* This CurveView subclass provides label storage for common use cases */
namespace Shared {
class HorizontallyLabeledCurveView : public CurveView {
public:
using CurveView::CurveView;
private:
char * label(Axis axis, int index) const override;
mutable char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
};
class VerticallyLabeledCurveView : public CurveView {
public:
using CurveView::CurveView;
private:
char * label(Axis axis, int index) const override;
mutable char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
};
class LabeledCurveView : public CurveView {
public:
using CurveView::CurveView;
private:
char * label(Axis axis, int index) const override;
mutable char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
mutable char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize];
};
}
#endif

View File

@@ -49,6 +49,14 @@ void RoundCursorView::setCursorFrame(KDRect f, bool force) {
CursorView::setCursorFrame(f, force);
}
void RoundCursorView::markRectAsDirty(KDRect rect) {
/* The CursorView class inherits from TransparentView, so does
* RoundCursorView. The method markRectAsDirty is thus overriden to avoid
* marking as dirty the background of the RoundCursorView in its superview.
*/
View::markRectAsDirty(rect);
}
#ifdef GRAPH_CURSOR_SPEEDUP
bool RoundCursorView::eraseCursorIfPossible() {
if (!m_underneathPixelBufferLoaded) {

View File

@@ -24,6 +24,7 @@ public:
void resetMemoization() const { m_underneathPixelBufferLoaded = false; }
#endif
private:
void markRectAsDirty(KDRect rect) override;
#ifdef GRAPH_CURSOR_SPEEDUP
bool eraseCursorIfPossible();
#endif

View File

@@ -3,7 +3,6 @@
#include <poincare/empty_layout.h>
#include <poincare/condensed_sum_layout.h>
#include <poincare/layout_helper.h>
#include <poincare/preferences.h>
#include "poincare_helpers.h"
#include <assert.h>
@@ -150,14 +149,13 @@ void SumGraphController::reloadBannerView() {
m_legendView.setEditableZone(m_cursor->x());
result = NAN;
}
m_legendView.setSumSymbol(m_step, m_startSum, endSum, result, functionLayout);
m_legendView.setSumLayout(m_step, m_startSum, endSum, result, functionLayout);
}
/* Legend View */
SumGraphController::LegendView::LegendView(SumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, CodePoint sumSymbol) :
m_sum(0.0f, 0.5f, Palette::PrimaryText, Palette::SubMenuBackground),
m_sumLayout(),
m_legend(k_font, I18n::Message::Default, 0.0f, 0.5f, Palette::PrimaryText, Palette::SubMenuBackground),
m_editableZone(controller, m_textBuffer, k_editableZoneBufferSize, TextField::maxBufferSize(), inputEventHandlerDelegate, controller, k_font, 0.0f, 0.5f, Palette::PrimaryText, Palette::SubMenuBackground),
m_sumSymbol(sumSymbol)
@@ -179,54 +177,41 @@ void SumGraphController::LegendView::setLegendMessage(I18n::Message message, Ste
}
void SumGraphController::LegendView::setEditableZone(double d) {
constexpr int precision = Preferences::MediumNumberOfSignificantDigits;
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision);
char buffer[bufferSize];
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(d, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal);
char buffer[k_valuesBufferSize];
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(d, buffer, k_valuesBufferSize, k_valuesPrecision, Preferences::PrintFloatMode::Decimal);
m_editableZone.setText(buffer);
}
void SumGraphController::LegendView::setSumSymbol(Step step, double start, double end, double result, Layout functionLayout) {
void SumGraphController::LegendView::setSumLayout(Step step, double start, double end, double result, Layout functionLayout) {
assert(step == Step::Result || functionLayout.isUninitialized());
constexpr int sigmaLength = 2;
const CodePoint sigma[sigmaLength] = {' ', m_sumSymbol};
if (step == Step::FirstParameter) {
m_sumLayout = LayoutHelper::CodePointString(sigma, sigmaLength);
} else if (step == Step::SecondParameter) {
constexpr int precision = Preferences::MediumNumberOfSignificantDigits;
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision);
char buffer[bufferSize];
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(start, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal);
m_sumLayout = CondensedSumLayout::Builder(
LayoutHelper::CodePointString(sigma, sigmaLength),
Poincare::Layout sumLayout = LayoutHelper::CodePointString(sigma, sigmaLength);
if (step != Step::FirstParameter) {
char buffer[k_valuesBufferSize];
Layout endLayout;
if (step == Step::SecondParameter) {
endLayout = EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, k_font, false);
} else {
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(end, buffer, k_valuesBufferSize, k_valuesPrecision, Preferences::PrintFloatMode::Decimal);
endLayout = LayoutHelper::String(buffer, strlen(buffer), k_font);
}
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(start, buffer, k_valuesBufferSize, k_valuesPrecision, Preferences::PrintFloatMode::Decimal);
sumLayout = CondensedSumLayout::Builder(
sumLayout,
LayoutHelper::String(buffer, strlen(buffer), k_font),
EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, k_font, false));
} else {
constexpr int precision = Preferences::LargeNumberOfSignificantDigits;
constexpr int sizeForPrecision = PrintFloat::charSizeForFloatsWithPrecision(precision);
constexpr int bufferSize = 2 + sizeForPrecision;
char buffer[bufferSize];
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(start, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal);
Layout start = LayoutHelper::String(buffer, strlen(buffer), k_font);
PoincareHelpers::ConvertFloatToTextWithDisplayMode<double>(end, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal);
Layout end = LayoutHelper::String(buffer, strlen(buffer), k_font);
m_sumLayout = CondensedSumLayout::Builder(
LayoutHelper::CodePointString(sigma, sigmaLength),
start,
end);
strlcpy(buffer, "= ", 3);
PoincareHelpers::ConvertFloatToText<double>(result, buffer+2, bufferSize-2, precision);
m_sumLayout = HorizontalLayout::Builder(
m_sumLayout,
functionLayout,
LayoutHelper::String(buffer, strlen(buffer), k_font));
}
m_sum.setLayout(m_sumLayout);
if (step == Step::Result) {
m_sum.setAlignment(0.5f, 0.5f);
} else {
m_sum.setAlignment(0.0f, 0.5f);
endLayout);
if (step == Step::Result) {
PoincareHelpers::ConvertFloatToText<double>(result, buffer, k_valuesBufferSize, k_valuesPrecision);
sumLayout = HorizontalLayout::Builder(
sumLayout,
functionLayout,
LayoutHelper::String("= ", 2, k_font),
LayoutHelper::String(buffer, strlen(buffer), k_font));
}
}
m_sum.setLayout(sumLayout);
m_sum.setAlignment(0.5f * (step == Step::Result), 0.5f);
layoutSubviews(step, false);
}

View File

@@ -55,9 +55,11 @@ private:
void drawRect(KDContext * ctx, KDRect rect) const override;
void setLegendMessage(I18n::Message message, Step step);
void setEditableZone(double d);
void setSumSymbol(Step step, double start, double end, double result, Poincare::Layout functionLayout);
void setSumLayout(Step step, double start, double end, double result, Poincare::Layout functionLayout);
private:
constexpr static KDCoordinate k_editableZoneBufferSize = Poincare::PrintFloat::k_maxFloatCharSize;
constexpr static size_t k_editableZoneBufferSize = Poincare::PrintFloat::k_maxFloatCharSize;
constexpr static int k_valuesPrecision = Poincare::Preferences::MediumNumberOfSignificantDigits;
constexpr static int k_valuesBufferSize = Poincare::PrintFloat::charSizeForFloatsWithPrecision(k_valuesPrecision);
constexpr static KDCoordinate k_legendHeight = 35;
constexpr static const KDFont * k_font = KDFont::SmallFont;
static KDCoordinate editableZoneWidth() { return 12*k_font->glyphSize().width(); }
@@ -69,7 +71,6 @@ private:
void layoutSubviews(bool force = false) override;
void layoutSubviews(Step step, bool force);
ExpressionView m_sum;
Poincare::Layout m_sumLayout;
MessageTextView m_legend;
TextField m_editableZone;
char m_textBuffer[k_editableZoneBufferSize];

View File

@@ -12,8 +12,4 @@ void BoxAxisView::drawRect(KDContext * ctx, KDRect rect) const {
drawLabelsAndGraduations(ctx, rect, Axis::Horizontal, false, false, true, k_axisMargin);
}
char * BoxAxisView::label(Axis axis, int index) const {
return axis == Axis::Vertical ? nullptr : (char *)m_labels[index];
}
}

View File

@@ -3,24 +3,21 @@
#include "box_range.h"
#include "store.h"
#include "../shared/curve_view.h"
#include "../shared/labeled_curve_view.h"
#include "../constant.h"
#include <poincare/print_float.h>
namespace Statistics {
class BoxAxisView : public Shared::CurveView {
class BoxAxisView : public Shared::HorizontallyLabeledCurveView {
public:
BoxAxisView(Store * store) :
CurveView(&m_boxRange),
m_labels{},
HorizontallyLabeledCurveView(&m_boxRange),
m_boxRange(BoxRange(store))
{}
void drawRect(KDContext * ctx, KDRect rect) const override;
private:
constexpr static KDCoordinate k_axisMargin = 3;
char * label(Axis axis, int index) const override;
char m_labels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
BoxRange m_boxRange;
};

View File

@@ -37,7 +37,6 @@ private:
static constexpr KDCoordinate k_quantileBarWidth = 2;
KDCoordinate boxLowerBoundPixel() const;
KDCoordinate boxUpperBoundPixel() const;
char * label(Axis axis, int index) const override { return nullptr; }
Store * m_store;
BoxRange m_boxRange;
int m_series;

View File

@@ -8,10 +8,9 @@ using namespace Shared;
namespace Statistics {
HistogramView::HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView, KDColor selectedHistogramColor, KDColor notSelectedHistogramColor, KDColor selectedBarColor) :
CurveView(store, nullptr, bannerView, nullptr),
HorizontallyLabeledCurveView(store, nullptr, bannerView, nullptr),
m_controller(controller),
m_store(store),
m_labels{},
m_highlightedBarStart(NAN),
m_highlightedBarEnd(NAN),
m_series(series),
@@ -63,10 +62,6 @@ void HistogramView::setHighlight(float start, float end) {
}
}
char * HistogramView::label(Axis axis, int index) const {
return axis == Axis::Vertical ? nullptr : (char *)m_labels[index];
}
float HistogramView::EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context) {
Store * store = (Store *)model;
float totalSize = ((float *)context)[0];

View File

@@ -5,13 +5,13 @@
#include <poincare/print_float.h>
#include "store.h"
#include "../constant.h"
#include "../shared/curve_view.h"
#include "../shared/labeled_curve_view.h"
namespace Statistics {
class HistogramController;
class HistogramView : public Shared::CurveView {
class HistogramView : public Shared::HorizontallyLabeledCurveView {
public:
HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView, KDColor selectedHistogramColor = Palette::StatisticsSelected, KDColor notSelectedHistogramColor = Palette::StatisticsNotSelected, KDColor selectedBarColor = Palette::StatisticsSelected);
int series() const { return m_series; }
@@ -21,10 +21,8 @@ public:
void setHighlight(float start, float end);
void setDisplayLabels(bool display) { m_displayLabels = display; }
private:
char * label(Axis axis, int index) const override;
HistogramController * m_controller;
Store * m_store;
char m_labels[k_maxNumberOfXLabels][k_labelBufferMaxSize];
static float EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context);
float m_highlightedBarStart;
float m_highlightedBarEnd;

View File

@@ -1,17 +1,17 @@
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 = "Microsekunde"
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 = "Distanz"
UnitDistanceMeterMenu = "Meter"
UnitDistanceMeterKilo = "Kilometer"
UnitDistanceMeter = "Meter"
@@ -19,43 +19,43 @@ UnitDistanceMeterMilli = "Millimeter"
UnitDistanceMeterMicro = "Micrometer"
UnitDistanceMeterNano = "Nanometer"
UnitDistanceMeterPico = "Picometer"
UnitDistanceAstronomicalUnit = "Astronomical unit"
UnitDistanceLightYear = "Light year"
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 = "Microgramm"
UnitMassGramNano = "Nanogramm"
UnitMassTonne = "Tonne"
UnitCurrentMenu = "Electric current"
UnitCurrentMenu = "Elektrischer Strom"
UnitCurrentAmpere = "Ampere"
UnitCurrentAmpereMilli = "Milliampere"
UnitCurrentAmpereMicro = "Microampere"
UnitTemperatureMenu = "Temperature"
UnitTemperatureMenu = "Temperaturen"
UnitTemperatureKelvin = "Kelvin"
UnitAmountMenu = "Amount of substance"
UnitAmountMole = "Mole"
UnitAmountMoleMilli = "Millimole"
UnitAmountMoleMicro = "Micromole"
UnitLuminousIntensityMenu = "Luminous intensity"
UnitAmountMenu = "Substanzmenge"
UnitAmountMole = "Mol"
UnitAmountMoleMilli = "Millimol"
UnitAmountMoleMicro = "Micromol"
UnitLuminousIntensityMenu = "Helligkeit"
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"
UnitPressureBar = "Bar"
UnitPressureAtm = "Atmosphere"
UnitEnergyMenu = "Energy"
UnitEnergyMenu = "Energie"
UnitEnergyJouleMenu = "Joule"
UnitEnergyJouleKilo = "Kilojoule"
UnitEnergyJoule = "Joule"
@@ -65,37 +65,37 @@ 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"
UnitElectricChargeMenu = "Elektrische Ladung"
UnitChargeCoulomb = "Coulomb"
UnitPotentialMenu = "Electric potential"
UnitPotentialMenu = "Elektrisches Potenzial"
UnitPotentialVoltKilo = "Kilovolt"
UnitPotentialVolt = "Volt"
UnitPotentialVoltMilli = "Millivolt"
UnitPotentialVoltMicro = "Microvolt"
UnitCapacitanceMenu = "Electrical capacitance"
UnitCapacitanceMenu = "Elektrische Kapazität"
UnitCapacitanceFarad = "Farad"
UnitCapacitanceFaradMilli = "Millifarad"
UnitCapacitanceFaradMicro = "Microfarad"
UnitResistanceMenu = "Electrical resistance"
UnitResistanceMenu = "Elektrischer Widerstand"
UnitResistanceOhmKilo = "Kiloohm"
UnitResistanceOhm = "Ohm"
UnitConductanceMenu = "Electrical conductance"
UnitConductanceMenu = "Elektrische Leitfähigkeit"
UnitConductanceSiemens = "Siemens"
UnitConductanceSiemensMilli = "Millisiemens"
UnitMagneticFieldMenu = "Magnetic field"
UnitMagneticFieldMenu = "Magnetisches Feld"
UnitMagneticFieldTesla = "Tesla"
InductanceMenu = "Electrical inductance"
InductanceMenu = "Elektrische Induktion"
UnitInductanceHenry = "Henry"
UnitSurfaceMenu = "Area"
UnitSurfaceHectar = "Hectare"
UnitVolumeMenu = "Volume"
UnitSurfaceMenu = "Fläche"
UnitSurfaceHectar = "Hektar"
UnitVolumeMenu = "Volumen"
UnitVolumeLiter = "Liter"
UnitVolumeLiterDeci = "Deciliter"
UnitVolumeLiterCenti = "Centiliter"
@@ -409,9 +409,9 @@ NumberElementUue = "119 - Ununennium (Uue)"
AlphaElementUue = "Uue - Ununennium (119)"
NumberElementUbn = "120 - Unbinilium (Ubn)"
AlphaElementUbn = "Ubn - Unbinilium (120)"
UnitOfMesurement = "Unit of mesurement"
UnitOfMesurement = "Messeinheit"
SpeedOfLightTag = "Lichtgeschwindigkeit (m·s^-1)"
YearLightTag = "Ein Jahr Licht (km)"
YearLightTag = "Lichtjahr (km)"
Thermodynamics = "Thermodynamik"
BoltzmannTag = "Boltzmann Konstante (J·K^-1)"
AvogadroTag = "Avogadro-Konstante (mol^-1)"
@@ -427,4 +427,4 @@ NeutronMassTag = "Masse eines Neutrons (kg)"
Gravitation = "Gravitation"
ElementalChargeTag = "Elementarladung (C)"
GAccelerationTag = "Beschleunigung (m·s^-2)"
GConstantTag = "Konstant (m^3·kg^-1·s^-2)"
GConstantTag = "Konstant (m^3·kg^-1·s^-2)"

View File

@@ -1,11 +1,11 @@
.PHONY: scenario_logger
scenario_logger:
$(Q) make -j8 PLATFORM=simulator clean && make -j8 DEBUG=1 ARCH=x86_64 PLATFORM=simulator epsilon.headless.bin
$(Q) $(MAKE) PLATFORM=simulator clean && $(MAKE) DEBUG=1 ARCH=x86_64 PLATFORM=simulator epsilon.headless.bin
$(Q) cp output/debug/simulator/macos/x86_64/epsilon.headless.bin epsilon_scenario_logger.bin
@echo "Run ./epsilon_scenario_logger.bin --logAfter 0 < scenario.esc to log a scenario"
.PHONY: scenario_creator
scenario_creator:
$(Q) make -j8 PLATFORM=simulator clean && make -j8 DEBUG=1 ESCHER_LOG_EVENTS_BINARY=1 PLATFORM=simulator
$(Q) $(MAKE) PLATFORM=simulator clean && $(MAKE) DEBUG=1 ESCHER_LOG_EVENTS_BINARY=1 PLATFORM=simulator
$(Q) cp -R output/debug/simulator/macos/app/Payload/Epsilon.app epsilon_scenario_creator.app
@echo "Run lldb epsilon_scenario_creator.app then process launch -o scenario.esc to create a scenario"

62
build/targets.all.mak Normal file
View File

@@ -0,0 +1,62 @@
ANDROID_GRADLE_KEYSTORE ?= ~/.gradle/google-play-upload.keystore
ANDROID_GRADLE_PROPERTIES ?= ~/.gradle/gradle.properties
IOS_MOBILE_PROVISION ?= build/artifacts/NumWorks_Graphing_Calculator_Distribution.mobileprovision
EMCC ?= emcc
define source_emsdk
source ~/emsdk/emsdk_env.sh > /dev/null
endef
define file_check
@ if test ! -f $(1); \
then \
echo "Missing file: $(1)"; \
exit 1; \
fi
endef
define command_check
@ if ! command -v $(1) > /dev/null; \
then \
echo "Missing command: $(1), did you forget to source?"; \
exit 1; \
fi
endef
.PHONY: all_official
all_official:
$(call file_check,$(ANDROID_GRADLE_KEYSTORE))
$(call file_check,$(ANDROID_GRADLE_PROPERTIES))
$(call file_check,$(IOS_MOBILE_PROVISION))
$(call command_check,$(EMCC))
$(Q) rm -rf output/all_official
$(Q) mkdir -p output/all_official
$(Q) echo "BUILD_FIRMWARE DEVICE N0110"
$(Q) $(MAKE) clean
$(Q) $(MAKE) epsilon.official.onboarding.dfu
$(Q) cp output/release/device/n0110/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0110.dfu
$(Q) echo "BUILD_FIRMWARE DEVICE N0100"
$(Q) $(MAKE) MODEL=n0100 clean
$(Q) $(MAKE) MODEL=n0100 epsilon.official.onboarding.dfu
$(Q) cp output/release/device/n0100/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0100.dfu
$(Q) echo "BUILD_FIRMWARE SIMULATOR WEB ZIP"
$(Q) $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web clean
$(Q) $(call source_emsdk); $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web output/release/simulator/web/simulator.official.zip
$(Q) cp output/release/simulator/web/simulator.official.zip output/all_official/simulator.web.zip
$(Q) echo "BUILD_FIRMWARE SIMULATOR WEB JS"
$(Q) $(call source_emsdk); $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web epsilon.official.js
$(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.js
$(Q) cp output/release/simulator/web/epsilon.official.js.mem output/all_official/epsilon.js.mem
$(Q) echo "BUILD_FIRMWARE SIMULATOR WEB PYTHON JS"
$(Q) $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web clean
$(Q) $(call source_emsdk); $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web EPSILON_GETOPT=1 EPSILON_APPS=code epsilon.official.js
$(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.python.js
$(Q) cp output/release/simulator/web/epsilon.official.js.mem output/all_official/epsilon.python.js.mem
$(Q) echo "BUILD_FIRMWARE SIMULATOR ANDROID"
$(Q) $(MAKE) PLATFORM=simulator TARGET=android clean
$(Q) $(MAKE) PLATFORM=simulator TARGET=android epsilon.official.apk
$(Q) cp output/release/simulator/android/app/outputs/apk/release/android-release-unsigned.apk output/all_official/epsilon.apk
$(Q) echo "BUILD_FIRMWARE SIMULATOR IOS"
$(Q) $(MAKE) PLATFORM=simulator TARGET=ios clean
$(Q) $(MAKE) PLATFORM=simulator TARGET=ios IOS_PROVISIONNING_PROFILE=$(IOS_MOBILE_PROVISION) output/release/simulator/ios/app/epsilon.official.ipa
$(Q) cp output/release/simulator/ios/app/epsilon.official.ipa output/all_official/epsilon.ipa

View File

@@ -20,30 +20,3 @@ $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ex
fi
$(Q) $(PYTHON) build/device/dfu.py -u $(word 1,$^)
.PHONY: %.two_binaries
%.two_binaries: %.elf
@echo "Building an internal and an external binary for $<"
$(Q) $(OBJCOPY) -O binary -j .text.external -j .rodata.external -j .exam_mode_buffer $(BUILD_DIR)/$< $(BUILD_DIR)/$(basename $<).external.bin
$(Q) $(OBJCOPY) -O binary -R .text.external -R .rodata.external -R .exam_mode_buffer $(BUILD_DIR)/$< $(BUILD_DIR)/$(basename $<).internal.bin
@echo "Padding $(basename $<).external.bin and $(basename $<).internal.bin"
$(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin
$(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).internal.bin
.PHONY: binpack
binpack:
rm -rf build/binpack
mkdir -p build/binpack
${MAKE} clean
${MAKE} $(BUILD_DIR)/flasher.light.bin
cp $(BUILD_DIR)/flasher.light.bin build/binpack
${MAKE} clean
${MAKE} $(BUILD_DIR)/bench.flash.bin
${MAKE} $(BUILD_DIR)/bench.ram.bin
cp $(BUILD_DIR)/bench.ram.bin $(BUILD_DIR)/bench.flash.bin build/binpack
${MAKE} clean
${MAKE} epsilon.onboarding.update.two_binaries
cp $(BUILD_DIR)/epsilon.onboarding.update.internal.bin $(BUILD_DIR)/epsilon.onboarding.update.external.bin build/binpack
${MAKE} clean
cd build && for binary in flasher.light.bin bench.flash.bin bench.ram.bin epsilon.onboarding.internal.bin epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done
cd build && tar cvfz binpack-`git rev-parse HEAD | head -c 7`.tgz binpack
rm -rf build/binpack

View File

@@ -3,21 +3,15 @@ base_src = $(liba_src) $(kandinsky_src) $(escher_src) $(libaxx_src) $(poincare_s
epsilon_src = $(base_src) $(ion_default_src) $(apps_default_src)
epsilon_official_src = $(base_src) $(ion_default_src) $(apps_official_default_src)
epsilon_onboarding_src = $(base_src) $(ion_default_src) $(apps_onboarding_src)
epsilon_official_onboarding_src = $(base_src) $(ion_default_src) $(apps_official_onboarding_src)
epsilon_onboarding_update_src = $(base_src) $(ion_default_src) $(apps_onboarding_update_src)
epsilon_official_onboarding_update_src = $(base_src) $(ion_default_src) $(apps_official_onboarding_update_src)
epsilon_onboarding_beta_src = $(base_src) $(ion_default_src) $(apps_onboarding_beta_src)
epsilon_official_onboarding_beta_src = $(base_src) $(ion_default_src) $(apps_official_onboarding_beta_src)
$(BUILD_DIR)/epsilon.$(EXE): $(call object_for,$(epsilon_src))
$(BUILD_DIR)/epsilon.official.$(EXE): $(call object_for,$(epsilon_official_src))
$(BUILD_DIR)/epsilon.onboarding.$(EXE): $(call object_for,$(epsilon_onboarding_src))
$(BUILD_DIR)/epsilon.official.onboarding.$(EXE): $(call object_for,$(epsilon_official_onboarding_src))
$(BUILD_DIR)/epsilon.onboarding.update.$(EXE): $(call object_for,$(epsilon_onboarding_update_src))
$(BUILD_DIR)/epsilon.official.onboarding.update.$(EXE): $(call object_for,$(epsilon_official_onboarding_update_src))
$(BUILD_DIR)/epsilon.onboarding.beta.$(EXE): $(call object_for,$(epsilon_onboarding_beta_src))
$(BUILD_DIR)/epsilon.official.onboarding.beta.$(EXE): $(call object_for,$(epsilon_official_onboarding_beta_src))
$(BUILD_DIR)/epsilon.onboarding.$(EXE): $(call object_for, $(base_src) $(ion_default_src) $(apps_onboarding_src))
$(BUILD_DIR)/epsilon.official.onboarding.$(EXE): $(call object_for,$(base_src) $(ion_default_src) $(apps_official_onboarding_src))
$(BUILD_DIR)/epsilon.onboarding.update.$(EXE): $(call object_for, $(base_src) $(ion_default_src) $(apps_onboarding_update_src))
$(BUILD_DIR)/epsilon.official.onboarding.update.$(EXE): $(call object_for,$(base_src) $(ion_default_src) $(apps_official_onboarding_update_src))
$(BUILD_DIR)/epsilon.onboarding.beta.$(EXE): $(call object_for, $(base_src) $(ion_default_src) $(apps_onboarding_beta_src))
$(BUILD_DIR)/epsilon.official.onboarding.beta.$(EXE): $(call object_for,$(base_src) $(ion_default_src) $(apps_official_onboarding_beta_src))
test_base_src = $(base_src) $(apps_tests_src) $(runner_src) $(tests_src)
@@ -41,3 +35,5 @@ endef
-include build/targets.$(PLATFORM).mak
$(foreach extension,$(HANDY_TARGETS_EXTENSIONS),$(foreach executable,$(HANDY_TARGETS),$(eval $(call handy_target_rule,$(executable),$(extension)))))
include build/targets.all.mak

View File

@@ -1,17 +1,6 @@
$(BUILD_DIR)/epsilon.js: EMSCRIPTEN_INIT_FILE = 1
$(BUILD_DIR)/epsilon%packed.js: EMSCRIPTEN_INIT_FILE = 0
$(BUILD_DIR)/test.headless.js: EMSCRIPTEN_MODULARIZE = 0
$(BUILD_DIR)/epsilon.packed.js: $(call object_for,$(epsilon_src))
.PHONY: workshop_python_emulator
workshop_python_emulator:
$(MAKE) PLATFORM=simulator TARGET=web clean_for_apps_selection
$(MAKE) PLATFORM=simulator TARGET=web EPSILON_APPS=code
$(MAKE) PLATFORM=simulator TARGET=web clean_for_apps_selection
.PHONY: clean_for_apps_selection
clean_for_apps_selection:
@echo "CLEAN BEFORE CHANGING EPSILON_APPS"
$(Q) rm -f $(BUILD_DIR)/apps/apps_container_storage.o
$(Q) rm -f $(BUILD_DIR)/apps/i18n.*
$(BUILD_DIR)/epsilon.official.packed.js: $(call object_for,$(epsilon_official_src))

View File

@@ -132,7 +132,7 @@ EMFLAGS += -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0
# Configure LDFLAGS
EMSCRIPTEN_MODULARIZE ?= 1
LDFLAGS += -s MODULARIZE=$(EMSCRIPTEN_MODULARIZE) -s 'EXPORT_NAME="Epsilon"'
EMSCRIPTEN_INIT_FILE ?= 0
EMSCRIPTEN_INIT_FILE ?= 1
LDFLAGS += --memory-init-file $(EMSCRIPTEN_INIT_FILE)
SFLAGS += $(EMFLAGS)

2
docs/build/index.md vendored
View File

@@ -54,7 +54,7 @@ git clone https://github.com/numworks/epsilon.git
## Run Epsilon on your computer
Once the SDK has been installed, just open your terminal (Msys2, Terminal.app, xterm, etc...) and type the following commands:
Once the SDK has been installed, just open your terminal (Msys2, Terminal.app, xterm) and type the following commands:
```
make PLATFORM=simulator clean

View File

@@ -46,12 +46,11 @@ The choice of a programming language is a controversial topic. Not all of them c
- It is a [system](https://en.wikipedia.org/wiki/System_programming_language) programming language, which is something we need since we have to write some low-level code.
- It has excellent tooling: several extremly high-quality compilers
- It is used for several high-profile projects LLVM, WebKit, MySQL, Photoshop, etc... This ensures a strong ecosystem of tools, code and documentation.
- It is used for several high-profile projects LLVM, WebKit, MySQL, Photoshop This ensures a strong ecosystem of tools, code and documentation.
- It easily allows Object-Oriented Programming, which is a convenient abstraction.
Of course knowing a tool means knowing its limits. C++ isn't exempt of defaults:
- It *is* a complicated language. The C++ 11 specification is 1300 pages long.
- It *is* a complicated language. The C++ 11 specification is 1'300 pages long.
- It allows for a lot of abstractions, which is a double-edged sword. It can allow for some very tricky code, and it's very easy to have complex operations being run without noticing.
If you want to contribute to Epsilon, you'll need to learn some C++.
@@ -66,7 +65,7 @@ The stack memory is possibly the most used area of memory. It contains all local
#### Heap memory
Unfortunately, local variables can't answer all use cases, and sometimes one need to allocate memory that lives longer than a function call. This is traditionally done by using a pair of *malloc* / *free* functions.
Unfortunately, local variables can't answer all use cases, and sometimes one need to allocate memory that lives longer than a function call. This is traditionally done by using a pair of `malloc` / `free` functions.
This raises a lot of potential problems that can trigger unpredictable dynamic behaviors:
@@ -77,7 +76,7 @@ This raises a lot of potential problems that can trigger unpredictable dynamic b
<dd class="text-justify">Memory allocation has to be contiguous. So the allocation algorithm has to use a smart heuristic to ensure that it will not fragment its allocated space too much.</dd>
</dl>
Some automatic memory management solutions do exist (garbage collection, smart pointers), but they all come with a cost. We decided to manually manage dynamic memory, but to use it as sparingly as possible.
We decided to avoid `malloc` altogether and to use a mix of static allocation and a pool of relocatable garbage-collected nodes for manipulating mathematical expressions.
### Writing code that runs on the bare metal
@@ -90,8 +89,8 @@ In practice, this means that the firmware will need to know in advance how the m
- Where will we store read-only variables?
- Where will the code live in memory?
The firmware will also need to take special care of the system initialization. There is no such thing as a "main" function on a firmware. Instead, on Cortex-M4 devices, after reset the CPU simply jumps to the address contained at address 0x00000000 (which happens to be the first bytes of flash memory). So if your firmware starts by 0x12345678, code execution will start at address 0x12345678.
The firmware will also need to take special care of the system initialization. There is no such thing as a "main" function on a firmware. Instead, on Cortex-M devices, after reset the CPU simply jumps to the address contained at address 0x00000000 (which happens to be the first bytes of flash memory). So if your firmware starts by 0x12345678, code execution will start at address 0x12345678.
Enforcing such a careful memory layout would be an impossible job without the proper tool. Fortunately, embedded linkers can be scripted and allow this kind of tailor-made configuration. You'll find Epsilon's linker script in "ion/src/device/boot/flash.ld" - it is heavily commented and should be self-explanatory.
Enforcing such a careful memory layout would be an impossible job without the proper tool. Fortunately, embedded linkers can be scripted and allow this kind of tailor-made configuration. You'll find Epsilon's linker script in [ion/src/device/n0110/flash.ld](https://github.com/numworks/epsilon/blob/master/ion/src/device/n0110/flash.ld) - it is heavily commented and should be self-explanatory.
That being said, there are additional things the OS usually takes care of which we need to do ourselves : for example, initialize global variables to zero. This is done in the "ion/src/device/boot/rt0.cpp" file, which is worth reading too.
That being said, there are additional things the OS usually takes care of which we need to do ourselves : for example, initialize global variables to zero. This is done in the [ion/src/device/shared/boot/rt0.cpp](https://github.com/numworks/epsilon/blob/master/ion/src/device/shared/boot/rt0.cpp) file, which is worth reading too.

View File

@@ -11,25 +11,25 @@ By providing multiple implementations of the Ion functions, we therefore can get
## Device
This is the reference platform corresponding to the actual device. To really understand what the code is doing, you'll need to refer to our <a href="../../../hardware/electrical/">Electrical Engineering</a> pages. Among other thing, Ion is responsible for handling the boot process and the memory layout of the code on the device:
This is the reference platform corresponding to the actual device. To really understand what the code is doing, you'll need to refer to our [Electrical Engineering](https://www.numworks.com/resources/engineering/hardware/electrical/) pages. Among other thing, Ion is responsible for handling the boot process and the memory layout of the code on the device:
### Boot
On boot, the Cortex core interprets the beginning of Flash memory as an array of addresses, each having a specific meaning. For example, the first value gives the address of the stack and the second one the address the processor jumps to right after reset. This list of addresses is called the <a href="https://github.com/numworks/epsilon/blob/master/ion/src/device/boot/isr.c">ISR table</a>.
On boot, the Cortex core interprets the beginning of Flash memory as an array of addresses, each having a specific meaning. For example, the first value gives the address of the stack and the second one the address the processor jumps to right after reset. This list of addresses is called the [ISR table](https://github.com/numworks/epsilon/blob/master/ion/src/device/shared/boot/isr.c).
### Memory layout
Like we saw in the previous paragraph, the MCU has a specific memory layout (for example, Flash starts at address 0x08000000) and expects certain values at certain addresses. To ensure the firmware is laid out in memory exactly how the processor expects it, we use a custom <a href="https://github.com/numworks/epsilon/blob/master/ion/src/device/boot/flash.ld">linker script</a>.
Like we saw in the previous paragraph, the MCU has a specific memory layout (for example, Flash starts at address 0x08000000) and expects certain values at certain addresses. To ensure the firmware is laid out in memory exactly how the processor expects it, we use a custom [linker script](https://github.com/numworks/epsilon/blob/master/ion/src/device/n0110/flash.ld).
## Simulator
The simulator platform implements Ion calls using the <a href="http://www.fltk.org/">FLTK</a> library. The result is a native GUI program that can run under a variety of operating systems such as Windows, macOS or most Linux distributions.
The simulator platform implements Ion calls using the [SDL](https://www.libsdl.org/) library. The result is a native GUI program that can run under a variety of operating systems such as Windows, macOS or most Linux distributions.
It's very practical to code using the simulator, but one has to pay attention to the differences from an actual device: it'll be significantly faster, and will have a lot more memory. So code written using the simulator should always be thoroughly tested on an actual device.
## Emscripten
The emscripten platform implements Ion calls for a Web browser. This lets us build a version of Epsilon that can run directly in a browser, as shown in our <a href="/simulator/">online simulator</a>. The C++ code is transpiled to JavaScript using Emscripten, and then packaged in a webpage.
The emscripten platform implements Ion calls for a Web browser. This lets us build a version of Epsilon that can run directly in a browser, as shown in our [online simulator](https://www.numworks.com/simulator/). The C++ code is transpiled to JavaScript using Emscripten, and then packaged in a webpage.
Building on Emscripten takes quite a lot of time so you will most likely not want to use it for development purposes. But obviously it's a very neat feature for end users who can give the calculator a spin straight from their browser.

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -1,5 +1,5 @@
---
title: Poincare
title: Poincaré
---
# Poincaré
@@ -9,7 +9,7 @@ Poincare takes text input such as `1+2*3` and turns it into a tree structure, th
Each node of a tree represents either an operator or a value. All nodes have a type (`Type::Addition`, `Type::Multiplication`...) and some also store a value (ie `Type::Rational`).
![A example expression tree of Epsilon software](<%= p "expression_tree.svg" %>){:class="img-right"}
![A example expression tree of Epsilon software](<%= p "expression-tree.svg" %>){:class="img-right"}
According to their types, expressions are childless (`Type::Rational`) or store pointers to their children (we call those children operands). To ease tree traversal, each node also keeps a pointer to its parent: that information is somewhat redundant but makes dealing with the expression tree much easier. `Multiplication` and `Addition` are the only type that can hold an infinite number of operands. Other expressions have a fixed number of operands: for instance, an `AbsoluteValue` will only ever have one child.
## RTTI: Run-time type information
@@ -43,7 +43,6 @@ To sort those operands, we defined an order on expressions with the following fe
* The order relationship is depth-first recursive: if two expressions are equal in type and values, we compare their operands starting with the last.
* To compare two expressions, we first sort their commutative children to ensure the unicity of expression representations. This guarantees that the order is total on expressions.
![Order relationship on expressions](<%= p "order.svg" %>){:class="img-responsive"}
In the example, both root nodes are r so we compare their last operands. Both are equal to $$\pi$$ so we compare the next operands. As 3 > 2, we can conclude on the order relation between the expressions.

View File

@@ -4,7 +4,7 @@
#include <escher/view.h>
class TransparentView : public View {
public:
protected:
void markRectAsDirty(KDRect rect) override;
};

View File

@@ -116,14 +116,18 @@ bool TextArea::handleEvent(Ion::Events::Event event) {
if (event == Ion::Events::ShiftUp || event == Ion::Events::ShiftDown) {
selectUpDown(event == Ion::Events::ShiftUp);
return true;
} else if (event == Ion::Events::ShiftLeft) {
} else if (event == Ion::Events::AlphaLeft) {
contentView()->moveCursorGeo(-INT_MAX/2, 0);
} else if (event == Ion::Events::ShiftRight) {
TextInput::scrollToCursor();
} else if (event == Ion::Events::AlphaRight) {
contentView()->moveCursorGeo(INT_MAX/2, 0);
} else if (event == Ion::Events::ShiftUp) {
TextInput::scrollToCursor();
} else if (event == Ion::Events::AlphaUp) {
contentView()->moveCursorGeo(0, -INT_MAX/2);
} else if (event == Ion::Events::ShiftDown) {
TextInput::scrollToCursor();
} else if (event == Ion::Events::AlphaDown) {
contentView()->moveCursorGeo(0, INT_MAX/2);
TextInput::scrollToCursor();
} else if (event == Ion::Events::Left) {
if (contentView()->resetSelection()) {
return true;

View File

@@ -2,7 +2,7 @@
void TransparentView::markRectAsDirty(KDRect rect) {
if (m_superview) {
m_superview->markRectAsDirty(KDRect(rect.translatedBy(m_frame.origin())));
m_superview->markRectAsDirty(rect.translatedBy(m_frame.origin()));
}
View::markRectAsDirty(rect);
}

View File

@@ -53,6 +53,7 @@ Event getEvent(int * timeout);
ShiftAlphaStatus shiftAlphaStatus();
void setShiftAlphaStatus(ShiftAlphaStatus s);
void removeShift();
bool isShiftActive();
bool isAlphaActive();
bool isLockActive();
@@ -182,6 +183,11 @@ constexpr Event ShiftThree = Event::ShiftKey(Keyboard::Key::Three);
// Alpha
constexpr Event AlphaLeft = Event::AlphaKey(Keyboard::Key::Left);
constexpr Event AlphaRight = Event::AlphaKey(Keyboard::Key::Right);
constexpr Event AlphaUp = Event::AlphaKey(Keyboard::Key::Up);
constexpr Event AlphaDown = Event::AlphaKey(Keyboard::Key::Down);
constexpr Event Colon = Event::AlphaKey(Keyboard::Key::XNT);
constexpr Event SemiColon = Event::AlphaKey(Keyboard::Key::Var);
constexpr Event DoubleQuotes = Event::AlphaKey(Keyboard::Key::Toolbox);

View File

@@ -30,7 +30,7 @@ static constexpr EventData s_dataForEvent[4*Event::PageSize] = {
TL(), TL(), TL(), TL(), TL(), U(),
TL(), TL(), TL(), TL(), U(), U(),
// Alpha
U(), U(), U(), U(), U(), U(),
TL(), TL(), TL(), TL(), U(), U(),
U(), U(), U(), U(), U(), U(),
U(), U(), T(":"), T(";"), T("\""), T("%"),
T("a"), T("b"), T("c"), T("d"), T("e"), T("f"),
@@ -75,7 +75,7 @@ static constexpr const char * s_nameForEvent[255] = {
nullptr, nullptr, nullptr, "BrightnessPlus", "BrightnessMinus", nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
//Alpha,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
"AlphaLeft", "AlphaUp", "AlphaDown", "AlphaRight", nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, "Colon", "SemiColon", "DoubleQuotes", "Percent",
"LowerA", "LowerB", "LowerC", "LowerD", "LowerE", "LowerF",

View File

@@ -30,7 +30,7 @@ static constexpr EventData s_dataForEvent[4*Event::PageSize] = {
TL(), TL(), TL(), TL(), TL(), U(),
TL(), TL(), TL(), TL(), U(), U(),
// Alpha
U(), U(), U(), U(), U(), U(),
TL(), TL(), TL(), TL(), U(), U(),
U(), U(), U(), U(), U(), U(),
U(), U(), T(":"), T(";"), T("\""), T("%"),
T("a"), T("b"), T("c"), T("d"), T("e"), T("f"),
@@ -75,7 +75,7 @@ static constexpr const char * s_nameForEvent[255] = {
nullptr, nullptr, nullptr, "BrightnessPlus", "BrightnessMinus", nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
//Alpha,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
"AlphaLeft", "AlphaUp", "AlphaDown", "AlphaRight", nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, "Colon", "SemiColon", "DoubleQuotes", "Percent",
"LowerA", "LowerB", "LowerC", "LowerD", "LowerE", "LowerF",

View File

@@ -259,7 +259,7 @@ void Endpoint0::clearForOutTransactions(uint16_t wLength) {
setOutNAK(false);
}
uint16_t Endpoint0::receiveSomeData() {
int Endpoint0::receiveSomeData() {
// If it is the first chunk of data to be received, m_transferBufferLength is 0.
uint16_t packetSize = MIN(k_maxPacketSize, m_request.wLength() - m_transferBufferLength);
uint16_t sizeOfPacketRead = readPacket(m_largeBuffer + m_transferBufferLength, packetSize);

View File

@@ -57,7 +57,7 @@ public:
void clearForOutTransactions(uint16_t wLength);
private:
uint16_t receiveSomeData();
int receiveSomeData();
uint16_t readPacket(void * buffer, uint16_t length);
uint16_t writePacket(const void * buffer, uint16_t length);

View File

@@ -11,6 +11,16 @@ ShiftAlphaStatus shiftAlphaStatus() {
return sShiftAlphaStatus;
}
void removeShift() {
if (sShiftAlphaStatus == ShiftAlphaStatus::Shift) {
sShiftAlphaStatus = ShiftAlphaStatus::Default;
} else if (sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlpha ) {
sShiftAlphaStatus = ShiftAlphaStatus::Alpha;
} else if (sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlphaLock) {
sShiftAlphaStatus = ShiftAlphaStatus::AlphaLock;
}
}
bool isShiftActive() {
return sShiftAlphaStatus == ShiftAlphaStatus::Shift || sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlpha || sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlphaLock;
}

View File

@@ -34,10 +34,15 @@ $(BUILD_DIR)/app/res/%.xml: ion/src/simulator/android/src/res/%.xml | $$(@D)/.
.PHONY: force_remake
$(BUILD_DIR)/app/libs/%/libepsilon.so: force_remake $$(@D)/.
$(Q) echo "MAKE NDK_ABI=$*"
$(Q) $(MAKE) NDK_ABI=$* epsilon.so
$(Q) cp $(BUILD_DIR)/$*/epsilon.so $@
define rule_for_libepsilon
$$(BUILD_DIR)/app/libs/%/lib$(1): force_remake $$$$(@D)/.
$(Q) echo "MAKE NDK_ABI=$$*"
$(Q) $$(MAKE) NDK_ABI=$$* $(1)
$(Q) cp $$(BUILD_DIR)/$$*/$(1) $$@
endef
$(eval $(call rule_for_libepsilon,epsilon.so))
$(eval $(call rule_for_libepsilon,epsilon.official.so))
# If NDK_ABI is not defined, we will re-trigger a build for each avaialble ABI.
# This is used to build APKs, which needs to embbed a binary for each ABI.
@@ -46,22 +51,28 @@ ifndef NDK_ABI
NDK_ABIS = armeabi-v7a arm64-v8a x86 x86_64
epsilon_apk_deps = $(patsubst %,$(BUILD_DIR)/app/libs/%/libepsilon.so,$(NDK_ABIS))
epsilon_apk_deps += $(subst ion/src/simulator/android/src/res,$(BUILD_DIR)/app/res,$(wildcard ion/src/simulator/android/src/res/*/*))
epsilon_apk_deps = $(subst ion/src/simulator/android/src/res,$(BUILD_DIR)/app/res,$(wildcard ion/src/simulator/android/src/res/*/*))
epsilon_apk_deps += $(addprefix $(BUILD_DIR)/app/res/,mipmap/ic_launcher.png mipmap-v26/ic_launcher_foreground.png)
.PHONY: gradle_%
gradle_%: $(epsilon_apk_deps)
define rule_for_gradle
.PHONY: gradle_$1_$2
gradle_$1_$2: $$(epsilon_apk_deps) $$(patsubst %,$$(BUILD_DIR)/app/libs/%/libepsilon$2so,$(NDK_ABIS))
@echo "GRADLE ion/src/simulator/android/build.gradle"
$(Q) ANDROID_HOME=$(ANDROID_HOME) EPSILON_VERSION=$(EPSILON_VERSION) BUILD_DIR=$(BUILD_DIR) ion/src/simulator/android/gradlew -b ion/src/simulator/android/build.gradle $*
$(Q) ANDROID_HOME=$(ANDROID_HOME) EPSILON_VERSION=$(EPSILON_VERSION) BUILD_DIR=$(BUILD_DIR) ion/src/simulator/android/gradlew -b ion/src/simulator/android/build.gradle $1
endef
$(eval $(call rule_for_gradle,assembleCodesigned,.))
$(eval $(call rule_for_gradle,assembleRelease,.))
$(eval $(call rule_for_gradle,assembleCodesigned,.official.))
$(eval $(call rule_for_gradle,assembleRelease,.official.))
DEFAULT = epsilon.apk
.PHONY: epsilon.apk
.PHONY: epsilon%apk
ifdef ANDROID_SIGNING_STORE_FILE
epsilon.apk: gradle_assembleCodesigned
epsilon%apk: gradle_assembleCodesigned_%
else
epsilon.apk: gradle_assembleRelease
epsilon%apk: gradle_assembleRelease_%
$(warning Building without code signing. Define ANDROID_SIGNING_STORE_FILE, ANDROID_SIGNING_STORE_PASSWORD, ANDROID_SIGNING_KEY_ALIAS and ANDROID_SIGNING_KEY_PASSWORD for a signed build.)
endif

View File

@@ -35,7 +35,7 @@ SIMULATOR_ICONSET = $(SIMULATOR_ASSETS_PATH)/AppIcon.appiconset
include ion/src/simulator/shared/apple/Makefile
$(call simulator_app_plist,Info.plist): ion/src/simulator/ios/Info.plist $(call simulator_app_resource,Assets.car)
$(call simulator_app_plist,%,Info.plist): ion/src/simulator/ios/Info.plist $(call simulator_app_resource,%,Assets.car)
$(call rule_label,PLUTIL)
$(Q) cp $< $@
$(Q) plutil -insert "BuildMachineOSBuild" -string "$(IOS_BUILD_MACHINE_OS_BUILD)" $@
@@ -54,22 +54,23 @@ $(call simulator_app_plist,Info.plist): ion/src/simulator/ios/Info.plist $(call
$(Q) plutil -replace CFBundleIcons -json `plutil -extract CFBundleIcons json -o - $(BUILD_DIR)/app/assets/partial.plist` $@
$(Q) plutil -replace CFBundleIcons~ipad -json `plutil -extract CFBundleIcons~ipad json -o - $(BUILD_DIR)/app/assets/partial.plist` $@
$(call simulator_app_resource,launch.storyboardc): ion/src/simulator/ios/launch.storyboard | $$(@D)/.
$(call simulator_app_resource,%,launch.storyboardc): ion/src/simulator/ios/launch.storyboard | $$(@D)/.
$(call rule_label,IBTOOL)
$(Q) $(IBTOOL) --minimum-deployment-target $(APPLE_PLATFORM_MIN_VERSION) --compile $@ $^
ifdef IOS_PROVISIONNING_PROFILE
$(call simulator_app_resource,embedded.mobileprovision): $(IOS_PROVISIONNING_PROFILE) | $$(@D)/.
$(call simulator_app_resource,%,embedded.mobileprovision): $(IOS_PROVISIONNING_PROFILE) | $$(@D)/.
$(call rule_label,COPY)
$(Q) cp $^ $@
$(BUILD_DIR)/app/entitlements.plist: $(call simulator_app_resource,embedded.mobileprovision)
$(BUILD_DIR)/app/entitlements.plist: $(IOS_PROVISIONNING_PROFILE)
$(call rule_label,SCMS)
$(Q) security cms -D -i $(IOS_PROVISIONNING_PROFILE) | plutil -extract Entitlements xml1 - -o $@
SIMULATOR_APP_DEPS += $(BUILD_DIR)/app/entitlements.plist
simulator_app_deps += $(BUILD_DIR)/app/entitlements.plist
simulator_app_deps += $(call simulator_app_resource,$1,embedded.mobileprovision)
else
$(call simulator_app_resource,embedded.mobileprovision):
$(call simulator_app_resource,%,embedded.mobileprovision):
$(warning Building without a provisionning profile. Please define IOS_PROVISIONNING_PROFILE to point to the .mobileprovision file you want to use.)
endif
@@ -77,19 +78,19 @@ $(SIMULATOR_ICONSET)/Contents.json: ion/src/simulator/ios/icon_assets.json $(SIM
$(call rule_label,COPY)
$(Q) cp $< $@
$(call simulator_app_resource,Assets.car): $(SIMULATOR_ICONSET)/Contents.json | $$(@D)/.
$(call simulator_app_resource,%,Assets.car): $(SIMULATOR_ICONSET)/Contents.json | $$(@D)/.
$(call rule_label,ACTOOL)
$(Q) $(ACTOOL) --compile $(BUILD_DIR)/app/Payload/Epsilon.app --minimum-deployment-target $(APPLE_PLATFORM_MIN_VERSION) --platform $(APPLE_SDK) --app-icon AppIcon --output-partial-info-plist $(BUILD_DIR)/app/assets/partial.plist $(SIMULATOR_ASSETS_PATH) > /dev/null
$(Q) $(ACTOOL) --compile $(BUILD_DIR)/app/Payload/$*.app --minimum-deployment-target $(APPLE_PLATFORM_MIN_VERSION) --platform $(APPLE_SDK) --app-icon AppIcon --output-partial-info-plist $(BUILD_DIR)/app/assets/partial.plist $(SIMULATOR_ASSETS_PATH) > /dev/null
SIMULATOR_APP_DEPS += $(call simulator_app_resource,\
simulator_app_deps += $(call simulator_app_resource,$(1), \
Assets.car \
launch.storyboardc \
)
$(BUILD_DIR)/app/epsilon.ipa: $(SIMULATOR_APP_DEPS)
$(BUILD_DIR)/app/epsilon%ipa: $(subst ..,.,$(call simulator_app_deps,Epsilon$*))
ifdef IOS_PROVISIONNING_PROFILE
$(call rule_label,SIGN)
$(Q) codesign --force --entitlements $(BUILD_DIR)/app/entitlements.plist --sign "iPhone Distribution: NumWorks" $(BUILD_DIR)/app/Payload/Epsilon.app
$(Q) codesign --force --entitlements $(BUILD_DIR)/app/entitlements.plist --sign "Apple Distribution: NumWorks" $(BUILD_DIR)/app/Payload/Epsilon.app
endif
$(call rule_label,ZIP)
$(Q) cd $(dir $@) ; zip -qr9 $(notdir $@) Payload
@@ -99,7 +100,7 @@ DEFAULT := $(BUILD_DIR)/app/epsilon.ipa
endif
ifeq ($(APPLE_PLATFORM),ios-simulator)
.PHONY: epsilon_run
epsilon_run: $(SIMULATOR_APP_DEPS)
xcrun simctl install booted $(BUILD_DIR)/app/Payload/Epsilon.app
.PHONY: epsilon%run
epsilon%run: $(subst _.,.,$(call simulator_app_deps,Epsilon$*))
xcrun simctl install booted $(BUILD_DIR)/app/Payload/Epsilon$(subst _,.,$*)app
endif

View File

@@ -22,7 +22,7 @@ SIMULATOR_ICONSET = $(BUILD_DIR)/app/assets/app.iconset
include ion/src/simulator/shared/apple/Makefile
$(call simulator_app_plist,Info.plist): ion/src/simulator/macos/Info.plist
$(call simulator_app_plist,%,Info.plist): ion/src/simulator/macos/Info.plist
$(call rule_label,PLUTIL)
$(Q) cp $< $@
$(Q) plutil -insert "LSMinimumSystemVersion" -string "$(MACOS_MIN_VERSION)" $@
@@ -31,12 +31,21 @@ $(call simulator_app_plist,Info.plist): ion/src/simulator/macos/Info.plist
# macOS uses icns files
$(call simulator_app_resource,app.icns): $(SIMULATOR_ICONS) | $$(@D)/.
.SECONDARY: $(SIMULATOR_ICONS) | $$(@D)/.
$(call simulator_app_resource,%,app.icns): $(SIMULATOR_ICONS) | $$(@D)/.
$(call rule_label,ICNUTIL)
$(Q) iconutil --convert icns --output $@ $(SIMULATOR_ICONSET)
SIMULATOR_APP_DEPS += $(call simulator_app_resource,app.icns)
simulator_app_deps += $(call simulator_app_resource,$(1),app.icns)
simulator_app_deps_unofficial = $(call simulator_app_deps,Epsilon)
simulator_app_deps_official = $(call simulator_app_deps,Epsilon.official)
.PHONY: Epsilon.app Epsilon.official.app
Epsilon.app: $(simulator_app_deps_unofficial)
Epsilon.official.app: $(simulator_app_deps_official)
ifndef ARCH
DEFAULT := $(SIMULATOR_APP_DEPS)
DEFAULT := Epsilon.app
endif

View File

@@ -2,27 +2,37 @@
# The only things that have to be customized per platform are the icons and the
# Info.plist.
SIMULATOR_APP_PATH = $(BUILD_DIR)/app/Payload/Epsilon.app
SIMULATOR_APP_PATH = $(BUILD_DIR)/app/Payload
simulator_app_binary = $(addprefix $(SIMULATOR_APP_PATH)/$(SIMULATOR_APP_BINARY_PATH),$(1))
simulator_app_resource = $(addprefix $(SIMULATOR_APP_PATH)/$(SIMULATOR_APP_RESOURCE_PATH),$(1))
simulator_app_plist = $(addprefix $(SIMULATOR_APP_PATH)/$(SIMULATOR_APP_PLIST_PATH),$(1))
simulator_app_binary = $(addprefix $(SIMULATOR_APP_PATH)/$(1).app/$(SIMULATOR_APP_BINARY_PATH),$(2))
simulator_app_resource = $(addprefix $(SIMULATOR_APP_PATH)/$(1).app/$(SIMULATOR_APP_RESOURCE_PATH),$(2))
simulator_app_plist = $(addprefix $(SIMULATOR_APP_PATH)/$(1).app/$(SIMULATOR_APP_PLIST_PATH),$(2))
# Epsilon binary
.PHONY: force_remake
$(BUILD_DIR)/%/epsilon.bin: force_remake
$(Q) echo "MAKE ARCH=$*"
$(Q) $(MAKE) ARCH=$*
define rule_for_epsilon
$$(BUILD_DIR)/%/$(1): force_remake
$(Q) echo "MAKE ARCH=$$*"
$(Q) $$(MAKE) ARCH=$$* $(1)
endef
$(call simulator_app_binary,Epsilon): $(patsubst %,$(BUILD_DIR)/%/epsilon.bin,$(ARCHS)) | $$(@D)/.
$(call rule_label,LIPO)
$(Q) $(LIPO) -create $^ -output $@
$(eval $(call rule_for_epsilon,epsilon.bin))
$(eval $(call rule_for_epsilon,epsilon.official.bin))
define rule_for_lipo
$$(call simulator_app_binary,$1,Epsilon): $$(patsubst %,$(BUILD_DIR)/%/$2.bin,$$(ARCHS)) | $$$$(@D)/.
$$(call rule_label,LIPO)
$(Q) $$(LIPO) -create $$^ -output $$@
endef
$(eval $(call rule_for_lipo,Epsilon,epsilon))
$(eval $(call rule_for_lipo,Epsilon.official,epsilon.official))
# Background image
$(call simulator_app_resource,background.jpg): ion/src/simulator/assets/background.jpg | $$(@D)/.
$(call simulator_app_resource,%,background.jpg): ion/src/simulator/assets/background.jpg | $$(@D)/.
$(call rule_label,COPY)
$(Q) cp $^ $@
@@ -44,6 +54,6 @@ $(addprefix $(SIMULATOR_ICONSET)/,icon_%.png): ion/src/simulator/assets/logo.svg
# Export simulator app dependencies
SIMULATOR_APP_DEPS += $(call simulator_app_binary,Epsilon)
SIMULATOR_APP_DEPS += $(call simulator_app_plist,Info.plist)
SIMULATOR_APP_DEPS += $(call simulator_app_resource,background.jpg)
simulator_app_deps += $(call simulator_app_binary,$(1),Epsilon)
simulator_app_deps += $(call simulator_app_plist,$(1),Info.plist)
simulator_app_deps += $(call simulator_app_resource,$(1),background.jpg)

View File

@@ -64,10 +64,14 @@ namespace Ion {
namespace Events {
static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) {
/* If an event is detected, we want to remove the Shift modifier to mimic the
* device behaviour. If no event was detected, we restore the previous
* ShiftAlphaStatus. */
Ion::Events::ShiftAlphaStatus previousShiftAlphaStatus = Ion::Events::shiftAlphaStatus();
Ion::Events::removeShift();
if (event.keysym.mod & KMOD_CTRL) {
switch (event.keysym.sym) {
case SDLK_BACKSPACE:
return Clear;
case SDLK_x:
return Cut;
case SDLK_c:
@@ -88,14 +92,8 @@ static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) {
}
}
switch (event.keysym.sym) {
case SDLK_ESCAPE:
return Home;
case SDLK_RETURN:
return OK;
case SDLK_v:
return Var;
case SDLK_BACKSPACE:
return Clear;
case SDLK_x:
return Exp;
case SDLK_n:
@@ -124,38 +122,12 @@ static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) {
return Ans;
}
}
if (event.keysym.mod & KMOD_SHIFT) {
switch(event.keysym.sym) {
case SDLK_UP:
return ShiftUp;
case SDLK_DOWN:
return ShiftDown;
case SDLK_LEFT:
return ShiftLeft;
case SDLK_RIGHT:
return ShiftRight;
}
}
switch(event.keysym.sym) {
case SDLK_UP:
return Up;
case SDLK_DOWN:
return Down;
case SDLK_LEFT:
return Left;
case SDLK_RIGHT:
return Right;
case SDLK_RETURN:
return EXE;
case SDLK_ESCAPE:
return Back;
case SDLK_TAB:
return Toolbox;
case SDLK_BACKSPACE:
return Backspace;
case SDLK_AC_BACK:
return Termination;
}
// No event was detected, restore the previous ShiftAlphaStatus.
Ion::Events::setShiftAlphaStatus(previousShiftAlphaStatus);
return None;
}
@@ -180,6 +152,12 @@ static Event eventFromSDLTextInputEvent(SDL_TextInputEvent event) {
}
char character = event.text[0];
if (character >= 32 && character < 127) {
/* We remove the shift, otherwise it might stay activated when it shouldn't.
* For instance on a French keyboard, to input "1", we first press "Shift"
* (which activates the Shift modifier on the calculator), then we press
* "&", transformed by eventFromSDLTextInputEvent into the text "1". If we
* do not remove the Shift here, it would still be pressed afterwards. */
Ion::Events::removeShift();
return sEventForASCIICharAbove32[character-32];
}
return None;
@@ -193,6 +171,7 @@ Event getPlatformEvent() {
}
#endif
SDL_Event event;
Event result = None;
while (SDL_PollEvent(&event)) {
// The while is important: it'll do a fast-pass over all useless SDL events
if (event.type == SDL_WINDOWEVENT) {
@@ -201,16 +180,35 @@ Event getPlatformEvent() {
}
}
if (event.type == SDL_QUIT) {
return Termination;
result = Termination;
break;
}
if (event.type == SDL_KEYDOWN) {
return eventFromSDLKeyboardEvent(event.key);
if (IonSimulatorSDLKeyDetectedByScan(event.key.keysym.scancode)) {
continue;
}
result = eventFromSDLKeyboardEvent(event.key);
break;
}
if (event.type == SDL_TEXTINPUT) {
return eventFromSDLTextInputEvent(event.text);
result = eventFromSDLTextInputEvent(event.text);
break;
}
}
return None;
if (result != None) {
/* When events are not being processed - for instance when a Python script
* is being executed - SDL puts all ingoing events in a queue.
* When events processing goes back to normal, all the queued events are
* processed, which can result in weird behaviours (for instance, really
* fast navigation in the calculator, "instantanate" text input, ...).
* These behaviours are even more visible if the script contains an infinite
* loop.
* To prevent that, we flush all queued events after encountering a non-null
* event -> the first event from the queue will still be processed, but not
* the subsequent ones. */
SDL_FlushEvents(0, UINT32_MAX);
}
return result;
}
}

View File

@@ -22,6 +22,34 @@ void IonSimulatorKeyboardKeyUp(int keyNumber) {
namespace Ion {
namespace Keyboard {
class KeySDLKeyPair {
public:
constexpr KeySDLKeyPair(Ion::Keyboard::Key key, SDL_Scancode SDLKey) :
m_key(key),
m_SDLKey(SDLKey)
{}
Ion::Keyboard::Key key() const { return m_key; }
SDL_Scancode SDLKey() const { return m_SDLKey; }
private:
Ion::Keyboard::Key m_key;
SDL_Scancode m_SDLKey;
};
constexpr static KeySDLKeyPair sKeyPairs[] = {
KeySDLKeyPair(Ion::Keyboard::Key::Down, SDL_SCANCODE_DOWN),
KeySDLKeyPair(Ion::Keyboard::Key::Up, SDL_SCANCODE_UP),
KeySDLKeyPair(Ion::Keyboard::Key::Left, SDL_SCANCODE_LEFT),
KeySDLKeyPair(Ion::Keyboard::Key::Right, SDL_SCANCODE_RIGHT),
KeySDLKeyPair(Ion::Keyboard::Key::Shift, SDL_SCANCODE_LSHIFT),
KeySDLKeyPair(Ion::Keyboard::Key::Shift, SDL_SCANCODE_RSHIFT),
KeySDLKeyPair(Ion::Keyboard::Key::EXE, SDL_SCANCODE_RETURN),
KeySDLKeyPair(Ion::Keyboard::Key::Back, SDL_SCANCODE_ESCAPE),
KeySDLKeyPair(Ion::Keyboard::Key::Toolbox, SDL_SCANCODE_TAB),
KeySDLKeyPair(Ion::Keyboard::Key::Backspace, SDL_SCANCODE_BACKSPACE)
};
constexpr int sNumberOfKeyPairs = sizeof(sKeyPairs)/sizeof(KeySDLKeyPair);
State scan() {
// We need to tell SDL to get new state from the host OS
SDL_PumpEvents();
@@ -32,13 +60,12 @@ State scan() {
// Grab this opportunity to refresh the display if needed
Simulator::Main::refresh();
#if EPSILON_SDL_SCREEN_ONLY
// In this case, keyboard states will be sent over another channel
return sKeyboardState;
#else
// Start with a "clean" state
State state;
#if EPSILON_SDL_SCREEN_ONLY
// In this case, keyboard states are sent over another channel.
state = sKeyboardState;
#else
// Register a key for the mouse, if any
SDL_Point p;
Uint32 mouseState = SDL_GetMouseState(&p.x, &p.y);
@@ -46,10 +73,27 @@ State scan() {
Key k = Simulator::Layout::keyAt(&p);
state.setKey(k);
}
return state;
#endif
// Catch the physical keyboard events
const uint8_t * SDLstate = SDL_GetKeyboardState(NULL);
for (int i = 0; i < sNumberOfKeyPairs; i++) {
KeySDLKeyPair pair = sKeyPairs[i];
if (SDLstate[pair.SDLKey()]) {
state.setKey(pair.key());
}
}
return state;
}
}
}
bool IonSimulatorSDLKeyDetectedByScan(SDL_Scancode key) {
for (int i = 0; i < Ion::Keyboard::sNumberOfKeyPairs; i++) {
if (key == Ion::Keyboard::sKeyPairs[i].SDLKey()) {
return true;
}
}
return false;
}

Some files were not shown because too many files have changed in this diff Show More