Skip to content

Instantly share code, notes, and snippets.

@craftpage
Created June 29, 2024 09:15
Show Gist options
  • Save craftpage/dd448038f954d06c7f8f0fe67474fd76 to your computer and use it in GitHub Desktop.
Save craftpage/dd448038f954d06c7f8f0fe67474fd76 to your computer and use it in GitHub Desktop.
#include <M5Cardputer.h>
#include <M5Unified.h>
#include <cmath>
#include <random>
using namespace std;
// 画面の定義
M5GFX display;
M5Canvas canvas(&M5Cardputer.Display);
// フォントの定義
const GFXfont *font_num = &FreeSansBold9pt7b; // 数字用フォント
const GFXfont *font_op = &FreeSans9pt7b; // 演算子用フォント
// 入力状態
String input_string = "";
bool show_result = false; // 結果表示フラグ
// 関数のプロトタイプ宣言
double parse_function(String func, String expr, int &index);
double calculate(String expression);
double parse_term(String expression, int &index);
double parse_factor(String expression, int &index);
double parse_power(String expression, int &index);
// 階乗を計算する関数
double factorial(double n) {
if (n < 0) {
return NAN; // エラー:負の数の階乗は定義されない
} else if (n == 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
// 乱数生成器
random_device rd;
mt19937 generator(rd());
uniform_real_distribution<double> distribution(0.0, 1.0);
// 式全体を計算する関数
double calculate(String expression) {
int index = 0;
return parse_term(expression, index);
}
// 加算と減算を処理する関数
double parse_term(String expression, int &index) {
double result = parse_factor(expression, index);
while (index < expression.length()) {
char c = expression.charAt(index);
if (c == '+' || c == '-') {
index++;
if (c == '+') {
result += parse_factor(expression, index);
} else {
result -= parse_factor(expression, index);
}
} else {
break;
}
}
return result;
}
// 乗算と除算を処理する関数
double parse_factor(String expression, int &index) {
double result = parse_power(expression, index);
while (index < expression.length()) {
char c = expression.charAt(index);
if (c == '*' || c == '/') {
index++;
if (c == '*') {
result *= parse_power(expression, index);
} else {
double denominator = parse_power(expression, index);
if (denominator == 0) {
return NAN; // ゼロ除算エラー
}
result /= denominator;
}
} else {
break;
}
}
return result;
}
// 指数計算を処理する関数
double parse_power(String expression, int &index) {
double result = parse_function_or_number(expression, index);
while (index < expression.length() && expression.charAt(index) == '^') {
index++;
double exponent = parse_function_or_number(expression, index);
result = pow(result, exponent);
}
return result;
}
// 数値または関数呼び出しを処理する関数
double parse_function_or_number(String expression, int &index) {
while (index < expression.length() && isspace(expression.charAt(index))) {
index++;
}
if (index >= expression.length()) {
return NAN;
}
char c = expression.charAt(index);
if (isdigit(c) || c == '.') {
// 数値を解析
String numStr = "";
while (index < expression.length() && (isdigit(expression.charAt(index)) || expression.charAt(index) == '.')) {
numStr += expression.charAt(index++);
}
return numStr.toFloat();
} else if (c == '(') {
// 括弧内の式を計算
index++;
double result = calculate(expression.substring(index));
while (index < expression.length() && expression.charAt(index) != ')') {
index++;
}
index++;
return result;
} else if (isalpha(c)) {
// 関数呼び出しまたは数学定数を解析
String funcOrConst = "";
while (index < expression.length() && isalpha(expression.charAt(index))) {
funcOrConst += expression.charAt(index++);
}
// 数学定数の処理
if (funcOrConst == "PI") {
return M_PI;
} else if (funcOrConst == "E") {
return M_E;
} else { // 関数の場合
return parse_function(funcOrConst, expression, index);
}
} else {
return NAN;
}
}
// 関数呼び出しを処理する関数
double parse_function(String func, String expr, int &index) {
double num = 0;
int start = index + 1; // '('の次の文字
int parenCount = 1;
int i = start;
while (i < expr.length() && parenCount > 0) {
if (expr.charAt(i) == '(') parenCount++;
if (expr.charAt(i) == ')') parenCount--;
i++;
}
if (parenCount != 0) return NAN; // 括弧の不一致
String subExpr = expr.substring(start, i - 1);
// 負数の処理
if (subExpr.startsWith("-")) {
num = -calculate(subExpr.substring(1));
} else {
num = calculate(subExpr);
}
index = i;
// C++の算術関数に対応 (fmodとpowは引数を2つ取るため個別処理)
if (func == "sin") {
return sin(num);
} else if (func == "cos") {
return cos(num);
} else if (func == "tan") {
return tan(num);
} else if (func == "log") {
return log10(num);
} else if (func == "abs") {
return fabs(num);
} else if (func == "sqrt") {
return sqrt(num);
} else if (func == "exp") {
return exp(num);
} else if (func == "floor") {
return floor(num);
} else if (func == "ceil") {
return ceil(num);
} else if (func == "round") {
return round(num);
} else if (func == "trunc") {
return trunc(num);
} else if (func == "sinh") {
return sinh(num);
} else if (func == "cosh") {
return cosh(num);
} else if (func == "tanh") {
return tanh(num);
} else if (func == "asin") {
return asin(num);
} else if (func == "acos") {
return acos(num);
} else if (func == "atan") {
return atan(num);
} else if (func == "fmod") {
index++; // '('を読み飛ばす
double num2 = calculate(expr.substring(index));
while (index < expr.length() && expr.charAt(index) != ')') {
index++;
}
index++; // ')'を読み飛ばす
return fmod(num, num2);
} else if (func == "pow") {
index++; // '('を読み飛ばす
double num2 = calculate(expr.substring(index));
while (index < expr.length() && expr.charAt(index) != ')') {
index++;
}
index++; // ')'を読み飛ばす
return pow(num, num2);
} else if (func == "fact") {
return factorial(num);
} else if (func == "random") { // 乱数を返す
return distribution(generator);
} else {
return NAN; // 無効な関数
}
}
// 画面描画
void drawScreen() {
canvas.fillScreen(TFT_BLACK);
canvas.setTextColor(TFT_WHITE);
// 入力欄
canvas.drawString(input_string, 10, 30, font_num);
// 結果表示
if (show_result) {
double result = calculate(input_string);
String result_string = String(result);
canvas.drawString(result_string, 10, 50, font_num);
}
canvas.pushSprite(0, 0);
}
// セットアップ
void setup() {
auto cfg = M5.config();
M5Cardputer.begin(cfg, true);
M5Cardputer.Display.setRotation(1);
M5Cardputer.Display.setTextSize(0.5);
M5Cardputer.Display.drawRect(0, 0, M5Cardputer.Display.width(),
M5Cardputer.Display.height() - 28, TFT_BLUE);
M5Cardputer.Display.setTextFont(&fonts::FreeSerifBoldItalic18pt7b);
M5Cardputer.Display.fillRect(0, M5Cardputer.Display.height() - 4,
M5Cardputer.Display.width(), 4, TFT_BLUE);
canvas.setTextFont(&fonts::FreeSerifBoldItalic18pt7b);
canvas.setTextSize(0.5);
canvas.createSprite(M5Cardputer.Display.width() - 8,
M5Cardputer.Display.height() - 36);
canvas.setTextScroll(true);
canvas.setTextColor(TFT_CYAN); // シアン色のテキスト
canvas.println("Press Key and Enter to Input Text");
canvas.pushSprite(4, 4);
M5Cardputer.Display.drawString(input_string, 4, M5Cardputer.Display.height() - 24);
drawScreen();
}
// メインループ
void loop() {
M5Cardputer.update();
if (M5Cardputer.Keyboard.isChange()) {
if (M5Cardputer.Keyboard.isPressed()) {
Keyboard_Class::KeysState status = M5Cardputer.Keyboard.keysState();
for (auto i : status.word) {
input_string += i;
}
if (status.del) {
input_string.remove(input_string.length() - 1);
}
if (status.enter) {
canvas.println(input_string + " = " + calculate(input_string));
input_string.remove(0, 2);
canvas.pushSprite(4, 4);
show_result = !show_result;
if (show_result) {
input_string = ""; // 結果表示後に入力欄をクリア
}
}
M5Cardputer.Display.fillRect(0, M5Cardputer.Display.height() - 28, M5Cardputer.Display.width(), 25, TFT_NAVY);
M5Cardputer.Display.setTextColor(TFT_CYAN);
M5Cardputer.Display.drawString(input_string, 4, M5Cardputer.Display.height() - 24);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment