Created
June 29, 2024 09:15
-
-
Save craftpage/dd448038f954d06c7f8f0fe67474fd76 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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