From 23aef526221b892acb2496cff5eb6c9e5bf0199c Mon Sep 17 00:00:00 2001 From: Felix Raimundo Date: Wed, 6 Apr 2016 12:54:26 +0200 Subject: [PATCH 1/3] First part for adding history to the user input. Change-Id: I9d2ff28641f0d3b7fe382a765ad8cc084840c2ef --- app/app.cpp | 179 +++++++++++++++++++++++++++++++++----------------- app/utils.cpp | 20 ++++-- app/utils.h | 18 ++++- 3 files changed, 149 insertions(+), 68 deletions(-) diff --git a/app/app.cpp b/app/app.cpp index 2651db497..46b459262 100644 --- a/app/app.cpp +++ b/app/app.cpp @@ -5,35 +5,11 @@ extern "C" { } #include +#include "../poincare/src/layout/string_layout.h" #include "utils.h" -void draw_lines_from_center() { - KDCoordinate width = SCREEN_WIDTH; - KDCoordinate height = SCREEN_HEIGHT; - - KDColor c = 0xFF; - - KDPoint center = KDPointMake(width/2, height/2); - int step = 2; - - for (KDCoordinate x=0; xcreateLayout(); + user_expression.simplified = user_expression.expression->simplify(); + user_expression.simplified_layout = user_expression.simplified->createLayout(); + } else { + user_expression.expression_layout = + new StringLayout(kParsingErrorMessage, strlen(kParsingErrorMessage)); + } + return user_expression; +} + +static int16_t print_user_input(user_expression_t user_expression, int16_t yOffset) { + if (user_expression.expression_layout) { + int16_t height = user_expression.expression_layout->size().height; + if (yOffset + height < SCREEN_HEIGHT) { + user_expression.expression_layout->draw(KDPointMake(0, yOffset)); + } + yOffset += height; + } + if (user_expression.simplified_layout) { + int16_t height = user_expression.simplified_layout->size().height; + if (yOffset + height < SCREEN_HEIGHT) { + int16_t xOffset = SCREEN_WIDTH - user_expression.simplified_layout->size().width; + user_expression.simplified_layout->draw(KDPointMake(xOffset, yOffset)); + } + yOffset += height; + } + return yOffset; } static void interactive_expression_parsing() { + UserExpressions user_inputs; while (1) { - char * text_input = get_text(); - clear_screen(); - Expression * e = Expression::parse(text_input); - if (e) { - ExpressionLayout * l = e->createLayout(); - int16_t yOffset = 10; - if (l) { - l->draw(KDPointMake(0, yOffset)); - yOffset += l->size().height; - delete l; - } - Expression * simplified = e->simplify(); - // Print the simplification. - if (simplified) { - ExpressionLayout * simplified_l = simplified->createLayout(); - if (simplified_l) { - int16_t xOffset = SCREEN_WIDTH - simplified_l->size().width; - simplified_l->draw(KDPointMake(xOffset, yOffset)); - delete simplified_l; - } - if (simplified != e) { - delete simplified; - } - } - delete e; - } else { - KDDrawString("PARSING ERROR", KDPointMake(10,10), 0); + text_event_t text_event = get_text(nullptr); + if (text_event.event == EQUAL) { + user_inputs.append_expression(create_user_input(text_event.text)); + } + int16_t yOffset = 0; + for (int i=0; iSCREEN_HEIGHT) { + break; + } } - // We dealocate the memory allocated by get_text; - free(text_input); } } diff --git a/app/utils.cpp b/app/utils.cpp index 01efea1d3..79bfcdf39 100644 --- a/app/utils.cpp +++ b/app/utils.cpp @@ -1,10 +1,12 @@ extern "C" { +#include #include #include #include -#include } +#include "utils.h" + #define PROMPT_HEIGHT 30 void clear_screen() { @@ -105,18 +107,27 @@ static int get_trig_input(char* input) { } } -char* get_text() { +text_event_t get_text(char* txt) { char input[255] = {0}; int index = 0; int max = 0; input[max] = ' '; input[max+1] = '\0'; + text_event_t text_event = {nullptr, ERROR}; + + if (txt != nullptr) { + assert(false); + } while (1) { clear_prompt(); print_prompt(input, index); ion_event_t event = ion_get_event(); if (event == EQUAL) { + input[max] = '\0'; + text_event.event = EQUAL; + text_event.text = (char*) malloc(sizeof(char) * (index + 1)); + memcpy(text_event.text, input, (size_t) (index + 1)); break; } else if (event == LEFT_ARROW) { index--; @@ -158,8 +169,5 @@ char* get_text() { } clear_prompt(); - input[max] = '\0'; - char* output = (char*) malloc(sizeof(char) * (index + 1)); - memcpy(output, input, (size_t) (index + 1)); - return output; + return text_event; } diff --git a/app/utils.h b/app/utils.h index ca78fcae1..bc48f0b8d 100644 --- a/app/utils.h +++ b/app/utils.h @@ -1,9 +1,21 @@ #ifndef APP_UTILS_H #define APP_UTILS_H -/* Returns a pointer to an input text, allocated by the functions (it is thus - * the caller's role to free it). */ -char* get_text(); +extern "C" { +#include +} + +typedef struct { + char* text; + ion_event_t event; +} text_event_t; + +/* Returns a pointer to an input text allocated by the functions. + * Also returns an event, which is its return reason. + * + * This function can get a text to work on instead of starting from an empty + * string. */ +text_event_t get_text(char* txt); void clear_screen(); From 4e8ee1bf0208cecccc01b9e235f21ec1e84713e4 Mon Sep 17 00:00:00 2001 From: Felix Raimundo Date: Wed, 6 Apr 2016 15:19:31 +0200 Subject: [PATCH 2/3] Prototype of the history for user inputs. Change-Id: I676e8b80ec092891117827eda5213c6f1cfac733 --- app/app.cpp | 46 ++++++++++++++++++++++++++++++++++++---------- app/utils.cpp | 19 +++++++++++++++---- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/app/app.cpp b/app/app.cpp index 46b459262..6a71e3007 100644 --- a/app/app.cpp +++ b/app/app.cpp @@ -1,4 +1,5 @@ extern "C" { +#include #include #include #include @@ -85,7 +86,7 @@ class UserExpressions { } // Gets the i th latest expression. - user_expression_t get_expression(uint8_t index) { + user_expression_t get_expression(uint8_t index) const { //assert(index < m_numberOfExpressions); int pos = (m_position - index) % INPUT_MEMORY; if (pos < 0) { @@ -94,7 +95,7 @@ class UserExpressions { return m_expressions[pos]; } - uint8_t numberOfExpressions() { + uint8_t numberOfExpressions() const { return m_numberOfExpressions; } @@ -139,20 +140,45 @@ static int16_t print_user_input(user_expression_t user_expression, int16_t yOffs return yOffset; } +static void print_user_inputs(const UserExpressions& user_inputs, uint8_t startingAt) { + int16_t yOffset = 0; + for (uint8_t i=startingAt; iSCREEN_HEIGHT) { + break; + } + } +} + static void interactive_expression_parsing() { - UserExpressions user_inputs; + UserExpressions user_inputs = UserExpressions(); + int index = 0; while (1) { - text_event_t text_event = get_text(nullptr); + text_event_t text_event; + if (index == 0) { + text_event = get_text(nullptr); + } else { + text_event = get_text(user_inputs.get_expression(index - 1).text_input); + } if (text_event.event == EQUAL) { user_inputs.append_expression(create_user_input(text_event.text)); - } - int16_t yOffset = 0; - for (int i=0; iSCREEN_HEIGHT) { - break; + } else if (text_event.event == UP_ARROW) { + index--; + if (index < 0) { + index = 0; } + } else if (text_event.event == DOWN_ARROW) { + if (user_inputs.numberOfExpressions() != 0) { + index++; + if (index >= user_inputs.numberOfExpressions()) { + index = user_inputs.numberOfExpressions() - 1; + } + } + } else { + assert(false); // unreachable. } + clear_screen(); + print_user_inputs(user_inputs, index ? index - 1 : 0); } } diff --git a/app/utils.cpp b/app/utils.cpp index 79bfcdf39..1604e4b6c 100644 --- a/app/utils.cpp +++ b/app/utils.cpp @@ -111,14 +111,17 @@ text_event_t get_text(char* txt) { char input[255] = {0}; int index = 0; int max = 0; - input[max] = ' '; - input[max+1] = '\0'; text_event_t text_event = {nullptr, ERROR}; if (txt != nullptr) { - assert(false); + index = strlen(txt); + max = index; + memcpy(input, txt, (size_t) index); } + input[max] = ' '; + input[max+1] = '\0'; + while (1) { clear_prompt(); print_prompt(input, index); @@ -165,9 +168,17 @@ text_event_t get_text(char* txt) { input[i] = input[i+1]; } max--; + } else if (event == UP_ARROW) { + text_event.event = UP_ARROW; + break; + } else if (event == DOWN_ARROW) { + text_event.event = DOWN_ARROW; + break; + } else { + assert(false); // unreachable. } } - + clear_prompt(); return text_event; } From f334977c6ac4ec14c63c970d11fe48607d02e565 Mon Sep 17 00:00:00 2001 From: Felix Raimundo Date: Thu, 7 Apr 2016 12:38:08 +0200 Subject: [PATCH 3/3] Nicer plot Change-Id: I6f080a4c26ad8ce574661228a4d4b53632d2f870 --- app/Makefile | 5 +- app/app.cpp | 49 +++++++---------- app/plot.cpp | 73 ++++++++++++++++++++++++++ app/plot.h | 7 +++ app/utils.cpp | 6 +++ ion/include/ion/events.h | 1 + ion/src/shared/events.c | 2 +- ion/src/simulator/keyboard/fltkkbd.cpp | 2 +- 8 files changed, 111 insertions(+), 34 deletions(-) create mode 100644 app/plot.cpp create mode 100644 app/plot.h diff --git a/app/Makefile b/app/Makefile index d662c1a4d..af3cc5a0c 100644 --- a/app/Makefile +++ b/app/Makefile @@ -1,6 +1,7 @@ app_objs += $(addprefix app/,\ - app.o\ - utils.o) + app.o\ + plot.o\ + utils.o) products += $(app_objs) app.elf app.hex app.bin app.elf: $(app_objs) diff --git a/app/app.cpp b/app/app.cpp index 6a71e3007..0af824ecb 100644 --- a/app/app.cpp +++ b/app/app.cpp @@ -8,32 +8,14 @@ extern "C" { #include #include "../poincare/src/layout/string_layout.h" +#include "plot.h" #include "utils.h" -const char* kParsingErrorMessage = "PARSING ERROR"; - -void plot(Expression * e, float xMin, float xMax, float yMin, float yMax) { - Context plotContext; - KDCoordinate screenWidth = SCREEN_WIDTH; - KDCoordinate screenHeight = SCREEN_HEIGHT; - KDPoint previousPoint; - for (KDCoordinate i=0;iapproximate(plotContext); - KDCoordinate j = ((y-yMin)/(yMax-yMin)*screenHeight); - KDPoint currentPoint = KDPointMake(i,screenHeight-j); - if (i>0) { - KDDrawLine(previousPoint, currentPoint, 0xFF); - } - previousPoint = currentPoint; - } -} +static const char* kParsingErrorMessage = "PARSING ERROR"; ////////////////////////////////////////////////////// -#define INPUT_MEMORY 15 +static constexpr uint8_t kInputMemory = 15; typedef struct user_expression_t { char* text_input; @@ -47,24 +29,24 @@ class UserExpressions { public: UserExpressions() { m_numberOfExpressions = 0; - m_position = INPUT_MEMORY; - for (int i=0; i +#include +#include +#include +} + +#include + +#include "utils.h" +#include "plot.h" + +constexpr KDCoordinate kScreenWidth = SCREEN_WIDTH; +const KDCoordinate kScreenHeight = SCREEN_HEIGHT; + +// For now we only plot the axes at the origin. +// TODO: print axes not at the origin with some values too. +// TODO: label the axes. +// TODO: put the values on the axes. +static void plot_axes(float xMin, float xMax, float yMin, float yMax) { + if (xMin < 0 && xMax > 0) { + float total = xMax - xMin; + float ratio = xMax / total; + KDCoordinate width = ratio * kScreenWidth; + KDDrawLine(KDPointMake(width, 0), KDPointMake(width, kScreenHeight-1), 0xff); + } + if (yMin < 0 && yMax > 0) { + float total = yMax - yMin; + float ratio = yMax / total; + KDCoordinate height = ratio * kScreenHeight; + KDDrawLine(KDPointMake(0, height), KDPointMake(kScreenWidth-1, height), 0xff); + } +} + +// TODO: Add a cursor. +// TODO: print the expression. +void plot(Expression * e, float xMin, float xMax, float yMin, float yMax) { + assert(e); + + Context plotContext; + + // Plot the original axes. + plot_axes(xMin, xMax, yMin, yMax); + + // We need to initialize the first point. + Float xExp = Float(xMin); + plotContext.setExpressionForSymbolName(&xExp, "x"); + float y = e->approximate(plotContext); + KDCoordinate j = ((y-yMin) / (yMax-yMin) * kScreenHeight); + if (j < 0) { + j = 0; + } else if (j >= kScreenHeight) { + j = kScreenHeight - 1; + } + KDPoint previousPoint = KDPointMake(0, kScreenHeight-j); + + for (KDCoordinate i = 1; i < kScreenWidth; i++) { + float x = xMin + (xMax-xMin)/kScreenWidth*i; + Float xExp = Float(x); + plotContext.setExpressionForSymbolName(&xExp, "x"); + float y = e->approximate(plotContext); + KDCoordinate j = ((y-yMin) / (yMax-yMin) * kScreenHeight); + KDPoint currentPoint = KDPointMake(i, kScreenHeight-j); + if (currentPoint.y < 0) { + currentPoint.y = 0; + } else if (currentPoint.y >= kScreenHeight) { + currentPoint.y = kScreenHeight - 1; + } + KDDrawLine(previousPoint, currentPoint, 0xFF); + previousPoint = currentPoint; + } + ion_get_event(); // We wait for a text input. +} diff --git a/app/plot.h b/app/plot.h new file mode 100644 index 000000000..f7a4570bb --- /dev/null +++ b/app/plot.h @@ -0,0 +1,7 @@ +#ifndef APP_PLOT_H +#define APP_PLOT_H + +// Note that currently the only variable is 'x' +void plot(Expression * e, float xMin, float xMax, float yMin, float yMax); + +#endif diff --git a/app/utils.cpp b/app/utils.cpp index 1604e4b6c..d3b7be9bc 100644 --- a/app/utils.cpp +++ b/app/utils.cpp @@ -174,6 +174,12 @@ text_event_t get_text(char* txt) { } else if (event == DOWN_ARROW) { text_event.event = DOWN_ARROW; break; + } else if (event == PLOT) { + text_event.event = PLOT; + input[max] = '\0'; + text_event.text = (char*) malloc(sizeof(char) * (index + 1)); + memcpy(text_event.text, input, (size_t) (index + 1)); + break; } else { assert(false); // unreachable. } diff --git a/ion/include/ion/events.h b/ion/include/ion/events.h index babf18ad8..18ac3c759 100644 --- a/ion/include/ion/events.h +++ b/ion/include/ion/events.h @@ -79,6 +79,7 @@ typedef enum { DOWN_ARROW, TRIG_MENU, DELETE, + PLOT, ERROR = 0xffffffff, } ion_event_t; diff --git a/ion/src/shared/events.c b/ion/src/shared/events.c index e49b0a031..d57e3a457 100644 --- a/ion/src/shared/events.c +++ b/ion/src/shared/events.c @@ -50,7 +50,7 @@ static ion_key_event_t ion_get_key_event() { static const ion_event_t kEventForKeyDown[ION_NUMBER_OF_KEYS] = { UPPER_CASE_A, UPPER_CASE_B, UPPER_CASE_C, UPPER_CASE_D, UPPER_CASE_E, UPPER_CASE_F, UPPER_CASE_G, DELETE, UP_ARROW, DOWN_ARROW, - UPPER_CASE_K, UPPER_CASE_L, TRIG_MENU, LEFT_ARROW, RIGHT_ARROW, + UPPER_CASE_K, PLOT, TRIG_MENU, LEFT_ARROW, RIGHT_ARROW, SEVEN, EIGHT, NINE, LEFT_PARENTHESIS, RIGHT_PARENTHESIS, FOUR, FIVE, SIX, PRODUCT, DIVISION, ONE, TWO, THREE, PLUS, MINUS, diff --git a/ion/src/simulator/keyboard/fltkkbd.cpp b/ion/src/simulator/keyboard/fltkkbd.cpp index 0df48c990..e08b341ea 100644 --- a/ion/src/simulator/keyboard/fltkkbd.cpp +++ b/ion/src/simulator/keyboard/fltkkbd.cpp @@ -7,7 +7,7 @@ static const char* kCharForKey[KEYBOARD_ROWS * KEYBOARD_COLUMNS] = { "A", "B", "C", "D", "E", "F", "G", "DEL", "UP", "DOWN", - "K", "L", "TRIG", "LEFT", "RIGHT", + "K", "PLOT", "TRIG", "LEFT", "RIGHT", "7", "8", "9", "(", ")", "4", "5", "6", "*", "/", "1", "2", "3", "+", "-",