From 9eb672bb5f1673f5741ce43c1834c5147f7f5c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 11 Mar 2019 12:14:22 +0100 Subject: [PATCH 01/10] [apps/shared] Fix ExpressionFieldDelegateApp::layoutFieldDidReceiveEvent Layouts have two potential serializations. For example, HorizontalLayout(CharLayout(2), CharLayout(a)) can be serialized as: "2a" and "2*a". In layoutFieldDidReceiveEvent, we want to check that the longest serialisation is bounded by maxBufferSize. (We could have used Layout::serializeParsedExpression but we don't to avoid parsing the expression twice) --- apps/shared/expression_field_delegate_app.cpp | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/apps/shared/expression_field_delegate_app.cpp b/apps/shared/expression_field_delegate_app.cpp index 84588f838..95dd683bf 100644 --- a/apps/shared/expression_field_delegate_app.cpp +++ b/apps/shared/expression_field_delegate_app.cpp @@ -23,8 +23,14 @@ bool ExpressionFieldDelegateApp::layoutFieldDidReceiveEvent(LayoutField * layout layoutField->app()->displayWarning(I18n::Message::SyntaxError); return true; } - char buffer[TextField::maxBufferSize()]; - int bufferSize = TextField::maxBufferSize(); + /* An acceptable layout has to be parsable and serialized in a fixed-size + * buffer. We check all that here. */ + /* Step 1: Simple layout serialisation. Resulting texts can be parsed but + * not displayed, like: + * - 2a + * - log_{2}(x) */ + constexpr int bufferSize = TextField::maxBufferSize(); + char buffer[bufferSize]; int length = layoutField->layout().serializeForParsing(buffer, bufferSize); if (length >= bufferSize-1) { /* If the buffer is totally full, it is VERY likely that writeTextInBuffer @@ -32,7 +38,24 @@ bool ExpressionFieldDelegateApp::layoutFieldDidReceiveEvent(LayoutField * layout displayWarning(I18n::Message::SyntaxError); return true; } - if (!isAcceptableText(buffer)) { + // Step 2: Parsing + Poincare::Expression e = Poincare::Expression::Parse(buffer); + if (e.isUninitialized()) { + // Unparsable expression + displayWarning(I18n::Message::SyntaxError); + return true; + } + /* Step 3: Expression serialization. Tesulting texts are parseable and + * displayable, like: + * - 2*a + * - log(x,2) */ + length = e.serialize(buffer, bufferSize, Poincare::Preferences::sharedPreferences()->displayMode()); + if (length >= bufferSize-1) { + // Same comment as before + displayWarning(I18n::Message::SyntaxError); + return true; + } + if (!isAcceptableExpression(e)) { displayWarning(I18n::Message::SyntaxError); return true; } From dcd83923ef6ec71e2e7bdc583c88dcd75e61a3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Mar 2019 12:15:29 +0100 Subject: [PATCH 02/10] [scripts] Fix the emscripten simulator.zip build --- scripts/targets.emscripten.mak | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/targets.emscripten.mak b/scripts/targets.emscripten.mak index 7c9900fe0..436890c53 100644 --- a/scripts/targets.emscripten.mak +++ b/scripts/targets.emscripten.mak @@ -1,12 +1,12 @@ $(BUILD_DIR)/epsilon.packed.js: LDFLAGS += --memory-init-file 0 -$(BUILD_DIR)epsilon.packed.js: $(objs) $(app_objs) $(app_image_objs) +$(BUILD_DIR)/epsilon.packed.js: $(objs) $(call object_for,$(epsilon_src)) -$(BUILD_DIR)/simulator.zip: epsilon.packed.js +$(BUILD_DIR)/simulator.zip: $(BUILD_DIR)/epsilon.packed.js @rm -rf $(basename $@) - @mkdir $(basename $@) - @cp epsilon.packed.js $(basename $@)/epsilon.js + @mkdir -p $(basename $@) + @cp $^ $(basename $@)/epsilon.js @cp ion/src/emscripten/background.jpg $(basename $@)/ @cp ion/src/emscripten/simulator.html $(basename $@)/ @echo "ZIP $@" - @zip -r -9 $@ $(basename $@) > /dev/null + @zip -r -9 -j $@ $(basename $@) > /dev/null @rm -rf $(basename $@) From 43bd5e70443b217019b9ee3e476d27e4887f074c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Mar 2019 16:15:38 +0100 Subject: [PATCH 03/10] [scripts] Fix PLATFORM=device build --- scripts/platform.device.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/platform.device.mak b/scripts/platform.device.mak index 4c6be1d4c..ed95acbfc 100644 --- a/scripts/platform.device.mak +++ b/scripts/platform.device.mak @@ -6,4 +6,4 @@ EPSILON_BOOT_PROMPT = update EPSILON_DEVICE_BENCH ?= 1 SFLAGS += -DEPSILON_DEVICE_BENCH=$(EPSILON_DEVICE_BENCH) -python/port/port.o: CXXFLAGS += -DMP_PORT_USE_STACK_SYMBOLS=1 +$(BUILD_DIR)/python/port/port.o: CXXFLAGS += -DMP_PORT_USE_STACK_SYMBOLS=1 From 147c1b4592b9f1d24680747469ebeba08df372de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Mar 2019 13:51:01 +0100 Subject: [PATCH 04/10] [python/turtle] Make the turtle faster on emscripten It was slower than on the calculator --- python/port/mod/turtle/turtle.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/python/port/mod/turtle/turtle.cpp b/python/port/mod/turtle/turtle.cpp index 48d66c173..cb7cbb51e 100644 --- a/python/port/mod/turtle/turtle.cpp +++ b/python/port/mod/turtle/turtle.cpp @@ -323,11 +323,24 @@ bool Turtle::draw(bool force) { m_drawn = true; } - if (m_mileage > 1000) { + /* We sleep every time the turtle walks a mileageLimit amount, to allow user + * interruptions. The length of each sleep is determined by the speed of the + * turtle. + * With emscripten, sleep gives control to the web browser, which decides when + * to return from sleep: this makes the turtle significantly slower on the web + * emulator than on the calculator. We thus decided to sleep less often on the + * emscripten platform. */ +#if __EMSCRIPTEN__ + constexpr KDCoordinate mileageLimit = 10000; +#else + constexpr KDCoordinate mileageLimit = 1000; +#endif + + if (m_mileage > mileageLimit) { if (micropython_port_interruptible_msleep(1 + (m_speed == 0 ? 0 : 3 * (k_maxSpeed - m_speed)))) { return true; } - m_mileage -= 1000; + m_mileage -= mileageLimit; } return false; } From ddb22ba1a28ef11ca34cf7930b36343ecc4b1845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Mar 2019 14:50:42 +0100 Subject: [PATCH 05/10] [python/turtle] Fix drawing of diagonal lines --- python/port/mod/turtle/turtle.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/python/port/mod/turtle/turtle.cpp b/python/port/mod/turtle/turtle.cpp index cb7cbb51e..20e9c15cd 100644 --- a/python/port/mod/turtle/turtle.cpp +++ b/python/port/mod/turtle/turtle.cpp @@ -94,8 +94,20 @@ bool Turtle::goTo(mp_float_t x, mp_float_t y) { mp_float_t oldy = m_y; mp_float_t xLength = absF(std::floor(x) - std::floor(oldx)); mp_float_t yLength = absF(std::floor(y) - std::floor(oldy)); - bool principalDirectionIsX = xLength >= yLength; - mp_float_t length = principalDirectionIsX ? xLength : yLength; + + enum PrincipalDirection { + None = 0, + X = 1, + Y = 2 + }; + + PrincipalDirection principalDirection = xLength > yLength ? + PrincipalDirection::X : + (xLength == yLength ? + PrincipalDirection::None : + PrincipalDirection::Y); + + mp_float_t length = principalDirection == PrincipalDirection::X ? xLength : yLength; if (length > 1) { // Tweening function @@ -106,8 +118,8 @@ bool Turtle::goTo(mp_float_t x, mp_float_t y) { * the computation of the position on the principal coordinate is done * using a barycenter, roundings might skip some pixels, which results in * a dotted line. */ - mp_float_t currentX = principalDirectionIsX ? oldx + (x > oldx ? i : -i) : x * progress + oldx * (1 - progress); - mp_float_t currentY = principalDirectionIsX ? y * progress + oldy * (1 - progress) : oldy + (y > oldy ? i : -i); + mp_float_t currentX = principalDirection == PrincipalDirection::Y ? x * progress + oldx * (1 - progress) : oldx + (x > oldx ? i : -i); + mp_float_t currentY = principalDirection == PrincipalDirection::X ? y * progress + oldy * (1 - progress) : oldy + (y > oldy ? i : -i); if (dot(currentX, currentY) || draw(false)) { // Keyboard interruption. Return now to let MicroPython process it. return true; From 2a34f955ca83973388bca5859c11e490efaf4d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Saviot?= Date: Mon, 11 Mar 2019 15:25:24 +0100 Subject: [PATCH 06/10] [python/turtle] Fix some turtle jumps There was a drawing glitch for instance when doing goto(100,100). It was due to the tutle mileage being overflowed. --- python/port/mod/turtle/turtle.cpp | 25 +++++++++---------------- python/port/mod/turtle/turtle.h | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/python/port/mod/turtle/turtle.cpp b/python/port/mod/turtle/turtle.cpp index 20e9c15cd..0b7dcad89 100644 --- a/python/port/mod/turtle/turtle.cpp +++ b/python/port/mod/turtle/turtle.cpp @@ -335,24 +335,11 @@ bool Turtle::draw(bool force) { m_drawn = true; } - /* We sleep every time the turtle walks a mileageLimit amount, to allow user - * interruptions. The length of each sleep is determined by the speed of the - * turtle. - * With emscripten, sleep gives control to the web browser, which decides when - * to return from sleep: this makes the turtle significantly slower on the web - * emulator than on the calculator. We thus decided to sleep less often on the - * emscripten platform. */ -#if __EMSCRIPTEN__ - constexpr KDCoordinate mileageLimit = 10000; -#else - constexpr KDCoordinate mileageLimit = 1000; -#endif - - if (m_mileage > mileageLimit) { + if (m_mileage > k_mileageLimit) { if (micropython_port_interruptible_msleep(1 + (m_speed == 0 ? 0 : 3 * (k_maxSpeed - m_speed)))) { return true; } - m_mileage -= mileageLimit; + m_mileage -= k_mileageLimit; } return false; } @@ -360,6 +347,7 @@ bool Turtle::draw(bool force) { bool Turtle::dot(mp_float_t x, mp_float_t y) { MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); + // Draw the dot if the pen is down if (m_penDown && hasDotBuffers()) { KDContext * ctx = KDIonContext::sharedContext(); KDRect rect( @@ -369,7 +357,12 @@ bool Turtle::dot(mp_float_t x, mp_float_t y) { ctx->blendRectWithMask(rect, m_color, m_dotMask, m_dotWorkingPixelBuffer); } - m_mileage += sqrt((x - m_x) * (x - m_x) + (y - m_y) * (y - m_y)) * 1000; + /* Increase the turtle's mileage. We need to make sure the mileage is not + * overflowed, otherwise we might skip some msleeps in draw. */ + uint16_t additionalMileage = sqrt((x - m_x) * (x - m_x) + (y - m_y) * (y - m_y)) * 1000; + m_mileage = ((m_mileage > k_mileageLimit) + && ((m_mileage + additionalMileage) < k_mileageLimit)) ? + k_mileageLimit + 1 : m_mileage + additionalMileage; m_x = x; m_y = y; diff --git a/python/port/mod/turtle/turtle.h b/python/port/mod/turtle/turtle.h index 2b9d83b9c..87aa58e8f 100644 --- a/python/port/mod/turtle/turtle.h +++ b/python/port/mod/turtle/turtle.h @@ -154,7 +154,21 @@ private: uint8_t m_speed; // Speed is between 0 and 10 KDCoordinate m_penSize; - KDCoordinate m_mileage; + + /* We sleep every time the turtle walks a mileageLimit amount, to allow user + * interruptions. The length of each sleep is determined by the speed of the + * turtle. + * With emscripten, sleep gives control to the web browser, which decides when + * to return from sleep: this makes the turtle significantly slower on the web + * emulator than on the calculator. We thus decided to sleep less often on the + * emscripten platform. */ +#if __EMSCRIPTEN__ + static constexpr uint16_t k_mileageLimit = 10000; +#else + static constexpr uint16_t k_mileageLimit = 1000; +#endif + + uint16_t m_mileage; bool m_drawn; }; From e40e626eec705f8eaa2608b85547a71213f440fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 12 Mar 2019 15:45:58 +0100 Subject: [PATCH 07/10] [graph] Values tab: select the right cell of the values table when displaying/undisplaying the derivative column --- .../values/storage_function_parameter_controller.cpp | 8 ++++---- apps/graph/values/storage_function_parameter_controller.h | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/graph/values/storage_function_parameter_controller.cpp b/apps/graph/values/storage_function_parameter_controller.cpp index 667ab51a0..737cafada 100644 --- a/apps/graph/values/storage_function_parameter_controller.cpp +++ b/apps/graph/values/storage_function_parameter_controller.cpp @@ -19,7 +19,9 @@ bool StorageFunctionParameterController::handleEvent(Ion::Events::Event event) { switch (selectedRow()) { case 0: { - function()->setDisplayDerivative(!function()->displayDerivative()); + bool isDisplayingDerivative = function()->displayDerivative(); + function()->setDisplayDerivative(!isDisplayingDerivative); + m_valuesController->selectCellAtLocation(isDisplayingDerivative ? m_selectedFunctionColumn : m_selectedFunctionColumn + 1, m_valuesController->selectedRow()); m_selectableTableView.reloadData(); return true; } @@ -57,9 +59,7 @@ int StorageFunctionParameterController::reusableCellCount() { void StorageFunctionParameterController::viewWillAppear() { StorageValuesFunctionParameterController::viewWillAppear(); - if (function()->displayDerivative()) { - m_valuesController->selectCellAtLocation(m_valuesController->selectedColumn()+1, m_valuesController->selectedRow()); - } + m_selectedFunctionColumn = m_valuesController->selectedColumn(); } void StorageFunctionParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) { diff --git a/apps/graph/values/storage_function_parameter_controller.h b/apps/graph/values/storage_function_parameter_controller.h index 205320927..c629c73b6 100644 --- a/apps/graph/values/storage_function_parameter_controller.h +++ b/apps/graph/values/storage_function_parameter_controller.h @@ -27,6 +27,8 @@ private: #endif MessageTableCellWithSwitch m_displayDerivativeColumn; StorageValuesController * m_valuesController; + // Index of the column corresponding to the function in the values controller + int m_selectedFunctionColumn; }; } From 2658015cdaa56c0eda848a354d564f6eb18076e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 12 Mar 2019 18:15:37 +0100 Subject: [PATCH 08/10] [poincare] Fix mis-implementation of (a^b)^c -> a^(b*c) which is applied if: - a > 0 - in Real: when b and c are integers - in other modes: when c is integer --- poincare/src/power.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 4c90df701..91302c26b 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -580,12 +580,13 @@ Expression Power::shallowReduce(Context & context, Preferences::ComplexFormat co if (childAtIndex(0).type() == ExpressionNode::Type::Power) { Power p = childAtIndex(0).convert(); // Check if a > 0 or c is Integer - if (p.childAtIndex(0).sign(&context) == ExpressionNode::Sign::Positive - || (childAtIndex(1).type() == ExpressionNode::Type::Rational - && childAtIndex(1).convert().integerDenominator().isOne())) - { + bool aPositive = p.childAtIndex(0).sign(&context) == ExpressionNode::Sign::Positive; + bool cInteger = (childAtIndex(1).type() == ExpressionNode::Type::Rational + && childAtIndex(1).convert().integerDenominator().isOne()); + if (aPositive || cInteger) { // Check that the complex format is not Real or that b is an integer - if (complexFormat != Preferences::ComplexFormat::Real || (p.childAtIndex(1).type() == ExpressionNode::Type::Rational && p.childAtIndex(1).convert().integerDenominator().isOne())) { + bool bInteger = (p.childAtIndex(1).type() == ExpressionNode::Type::Rational && p.childAtIndex(1).convert().integerDenominator().isOne()); + if (aPositive || complexFormat != Preferences::ComplexFormat::Real || bInteger) { return simplifyPowerPower(context, complexFormat, angleUnit, target); } } From caef2e8c6fa246205cc829d623f15a8dd5b1505b Mon Sep 17 00:00:00 2001 From: Romain Goyet Date: Wed, 13 Mar 2019 10:18:17 +0100 Subject: [PATCH 09/10] Add GitHub issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 25 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 22 ++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..c9f1c1e8d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,25 @@ +--- +name: Bug report +about: Epsilon is not working like it should? Let us know! +labels: 'bug' + +--- +#### Describe the bug +A clear and concise description of what the bug is. Please describe a **single** bug per issue. Feel free to create multiple issues though! + +#### Screenshots +Please provide at least one screenshot of the issue happening. This is by far the best way to quickly show any issue! To attach a screenshot, just go to our [online simulator](https://www.numworks.com/simulator), navigate to reproduce your issue, and click the "screenshot" button. Then drag'n'drop the file here! + +#### To Reproduce +Steps to reproduce the behavior: +1. Go to the '...' app +2. Type '....' +3. Scroll down to '....' +4. See error + +#### Expected behavior +A clear and concise description of what you expected to happen. + +#### Environment + - Epsilon version (Settings > About > Software version). + - The platform(s) on which the problem happens: online simulator, actual device, etc... diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..6bc39c490 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: Feature request +about: Suggest an idea for an improvement of Epsilon +labels: 'enhancement' + +--- +#### Problem you'd like to fix +Is your feature request related to a problem? Please provide a clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +Please describe a **single** improvement per issue. Feel free to open multiple issues though! + +#### Screenshots +If possible, please attach a screenshot. You can go on our [online simulator](https://www.numworks.com/simulator), use the screenshot button, and drag'n'drop the file here. + +#### Describe the solution you'd like +A clear and concise description of what you want to happen. + +#### Describe alternatives you've considered +A clear and concise description of any alternative solutions or features you've considered. + +#### Additional context +Add any other context or screenshots about the feature request here. From 506068a7258a041c892a80dfa365521a20fdb82b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 14 Mar 2019 15:24:26 +0100 Subject: [PATCH 10/10] [scripts] Fix flasher.elf build --- Makefile | 2 +- scripts/targets.device.mak | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 7bf60b37a..f96b6f82b 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ objs = $(call object_for,$(src)) # Those can be built directly with make executable.exe as a shortcut. They also # depends on $(objs) -executables = epsilon test +executables = epsilon test flasher define rules_for_executable $$(BUILD_DIR)/$(1).$$(EXE): $$(objs) diff --git a/scripts/targets.device.mak b/scripts/targets.device.mak index d94372a59..52ba503c1 100644 --- a/scripts/targets.device.mak +++ b/scripts/targets.device.mak @@ -47,8 +47,8 @@ openocd: openocd -f scripts/device/openocd.cfg ifeq ($(EPSILON_USB_DFU_XIP)$(EPSILON_DEVICE_BENCH),10) -$(BUILD_DIR)/flasher.$(EXE): LDFLAGS = --gc-sections -T ion/src/device/usb/flasher.ld -$(BUILD_DIR)/flasher.$(EXE): $(objs) $(usb_objs) ion/src/device/usb/flasher.o +$(BUILD_DIR)/flasher.$(EXE): LDSCRIPT = ion/src/device/usb/flasher.ld +$(BUILD_DIR)/flasher.$(EXE): $(objs) $(usb_objs) $(BUILD_DIR)/ion/src/device/usb/flasher.o else $(BUILD_DIR)/flasher.$(EXE): @echo "Error: flasher.elf requires EPSILON_DEVICE_BENCH=0 EPSILON_USB_DFU_XIP=1"