Last active
June 8, 2022 02:09
-
-
Save RanolP/21574164cc799b729116f8a342aa1740 to your computer and use it in GitHub Desktop.
놀라운 그래프 C 단일 파일
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
/* | |
* y = f(x) 꼴의 그래프를 그리는 프로그램입니다. | |
* 이 파일을 빌드하기 위해서는 gdi32가 필요합니다. | |
* Dev C++ 기준 | |
* 1. 파일 > 새로 만들기 > 프로젝트 에서 Console Application을 만듭니다. | |
* 2. 해당 프로젝트가 열린 상태에서, 프로젝트 > 프로젝트 옵션을 열고, 매개변수들 탭에서 Linker 칸에 "-lgdi32" (따옴표 안의 것)을 넣습니다. | |
* | |
* 사용 방법 | |
* 1. y = f(x)의 그래프에서 f(x)에 해당하는 부분을 입력합니다. | |
* 이 때, 연산자 우선순위는 적용되지 않으며 왼쪽부터 결합됩니다 | |
* 지원하는 함수는 sin, gamma가 있습니다. | |
* 2. 함수에 대입할 x 값의 최소, 최대 범위를 각각 입력합니다. | |
* 3. GUI 창이 함수의 그래프 개형을 보여줍니다. | |
* | |
* 예시 입력: | |
* sin(x) | |
* -3.141592 | |
* 3.141592 | |
*/ | |
#include <windows.h> | |
#include <math.h> | |
#include <tgmath.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#define DEBUG printf | |
#define DEBUG DEBUG_RELEASE | |
void DEBUG_RELEASE() { | |
} | |
// Expression 관련 | |
// 수식을 컴퓨터가 인식할 수 있게 해석하는 데에 필요합니다. | |
typedef struct Expression { | |
int id; | |
void* handle; | |
int skip; | |
} Expression; | |
typedef struct Literal { | |
double value; | |
} Literal; | |
typedef struct BinaryOp { | |
char id; | |
Expression* lhs; | |
Expression* rhs; | |
} BinaryOp; | |
static struct { | |
} VariableX; | |
typedef struct UnaryOp { | |
char id; | |
Expression* handle; | |
} UnaryOp; | |
typedef struct FuncCall { | |
char* name; | |
Expression* argument; | |
} FuncCall; | |
// 수식에 x를 대입해 계산합니다. | |
double evaluate(Expression* expr, double x) { | |
switch (expr->id) { | |
case 0: { | |
Literal* handle = (Literal*) expr->handle; | |
return handle->value; | |
} | |
case 1: { | |
BinaryOp* handle = (BinaryOp*) expr->handle; | |
double lhs = evaluate(handle->lhs, x); | |
double rhs = evaluate(handle->rhs, x); | |
// 중위 연산자 목록입니다. | |
switch (handle->id) { | |
case '+': { | |
return lhs + rhs; | |
} | |
case '-': { | |
return lhs - rhs; | |
} | |
case '*': { | |
return lhs * rhs; | |
} | |
case '^': { | |
return pow(lhs, rhs); | |
} | |
case '/': { | |
return lhs / rhs; | |
} | |
default: { | |
return (double) NAN; | |
} | |
} | |
} | |
case 2: { | |
return x; | |
} | |
case 3: { | |
UnaryOp* handle = (UnaryOp*) expr->handle; | |
double value = evaluate(handle->handle, x); | |
// 전위 연산자 목록입니다. | |
switch (handle->id) { | |
case '-': { | |
return -value; | |
} | |
default: { | |
return (double) NAN; | |
} | |
} | |
} | |
case 4: { | |
FuncCall* handle = (FuncCall*) expr->handle; | |
DEBUG("eval funccall %s\n", handle->name); | |
double value = evaluate(handle->argument, x); | |
// 함수 목록입니다. | |
if (strcmp(handle->name, "sin") == 0) { | |
return sin(value); | |
} else if (strcmp(handle->name, "gamma") == 0) { | |
DEBUG("gamma!!"); | |
return tgamma(value); | |
} else { | |
return (double) NAN; | |
} | |
} | |
default: { | |
return (double) NAN; | |
} | |
} | |
} | |
// 수식을 파싱하는 부분입니다. | |
// 손으로 짠 재귀 하향 파서입니다. | |
Expression* parse(char* src); | |
Expression* parse_parenthesis(char* src) { | |
int skip = 0; | |
while (*src == ' ') { | |
skip += 1; | |
src++; | |
} | |
if (*src == '(') { | |
DEBUG("Brace Open\n"); | |
src++; | |
skip++; | |
Expression* result = parse(src); | |
if (result == NULL) { | |
return NULL; | |
} | |
src += result->skip; | |
skip += result->skip; | |
while (*src == ' ') { | |
skip += 1; | |
src++; | |
} | |
DEBUG("Brace Close: %c\n", *src); | |
if (*src == ')') { | |
result->skip = skip + 1; | |
return result; | |
} else { | |
return NULL; | |
} | |
} else { | |
return NULL; | |
} | |
} | |
Expression* parse_func(char* src) { | |
int skip = 0; | |
while (*src == ' ') { | |
skip += 1; | |
src++; | |
} | |
if (*src == 'x') { | |
return NULL; | |
} | |
char* name = (char*) malloc(sizeof(char) * 20); | |
int len = 0; | |
while ('a' <= *src && *src <= 'z') { | |
DEBUG("FuncFrag %c\n", *src); | |
name[len++] = *src++; | |
skip++; | |
} | |
if (len == 0) { | |
return NULL; | |
} | |
name[len] = '\0'; | |
DEBUG("FUNCCALL %s()\n", name); | |
FuncCall* call = (FuncCall*) malloc(sizeof(FuncCall)); | |
call->name = name; | |
call->argument = parse_parenthesis(src); | |
Expression* result = malloc(sizeof(Expression)); | |
result->id = 4; | |
result->skip = skip + call->argument->skip; | |
result->handle = call; | |
return result; | |
} | |
Expression* parse_simple(char* src) { | |
Expression* paren = parse_parenthesis(src); | |
if (paren != NULL) { | |
return paren; | |
} | |
Expression* func = parse_func(src); | |
if (func != NULL) { | |
return func; | |
} | |
int skip = 0; | |
while (*src == ' ') { | |
skip += 1; | |
src++; | |
} | |
if (*src == 'x') { | |
Expression* result = (Expression*) malloc(sizeof(Expression)); | |
result->id = 2; | |
result->handle = &VariableX; | |
result->skip = skip + 1; | |
return result; | |
} | |
char current; | |
double literal = NAN; | |
double dotScale = 1; | |
while ((current = *src++) != '\0') { | |
skip += 1; | |
if ('0' <= current && current <= '9') { | |
if (isnan(literal)) { | |
literal = 0; | |
} | |
if (dotScale < 1) { | |
literal += dotScale * (current - '0'); | |
dotScale *= 0.1; | |
} else { | |
literal = literal * 10 + (double) (current - '0'); | |
} | |
} else if (current == '.') { | |
if (isnan(literal)) { | |
return NULL; | |
} | |
dotScale = 0.1; | |
} else { | |
if (current != '\0') { | |
skip -= 1; | |
} | |
break; | |
} | |
} | |
DEBUG("value=%lf, skips=%d\n", literal, skip); | |
if (isnan(literal)) { | |
return NULL; | |
} | |
Literal* lit = (Literal*) malloc(sizeof(Literal)); | |
lit->value = literal; | |
Expression* result = (Expression*) malloc(sizeof(Expression)); | |
result->id = 0; | |
result->handle = lit; | |
result->skip = skip; | |
return result; | |
} | |
Expression* parse(char* src) { | |
int skip = 0; | |
Expression* result = NULL; | |
while (*src != '\0') { | |
DEBUG("%c, parse_simple\n", *src); | |
Expression* expr = parse_simple(src); | |
if (expr != NULL) { | |
if (result != NULL) { | |
return NULL; | |
} | |
result = expr; | |
src += expr->skip; | |
skip += expr->skip; | |
} else { | |
while (*src == ' ') { | |
src++; | |
skip++; | |
} | |
char current = *src++; | |
DEBUG("current=%c\n", current); | |
if (current == ')') { | |
break; | |
} else { | |
skip++; | |
} | |
if (result == NULL) { | |
UnaryOp* op = (UnaryOp*) malloc(sizeof(UnaryOp)); | |
op->id = current; | |
op->handle = parse_simple(src); | |
if (op->handle == NULL) { | |
return NULL; | |
} | |
result = (Expression*) malloc(sizeof(Expression)); | |
result->id = 3; | |
result->handle = op; | |
src += op->handle->skip; | |
skip += op->handle->skip; | |
} else { | |
BinaryOp* op = (BinaryOp*) malloc(sizeof(BinaryOp)); | |
op->id = current; | |
op->lhs = result; | |
op->rhs = parse_simple(src); | |
if (op->rhs == NULL) { | |
return NULL; | |
} | |
result = (Expression*) malloc(sizeof(Expression)); | |
result->id = 1; | |
result->handle = op; | |
src += op->rhs->skip; | |
skip += op->rhs->skip; | |
} | |
} | |
} | |
if (result != NULL) { | |
result->skip = skip; | |
} | |
return result; | |
} | |
// 본격적인 GUI 앱의 시작입니다. | |
LPCTSTR className = TEXT("Granola"); | |
LPCTSTR title = TEXT("Granola Graphs"); | |
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); | |
Expression* expr; | |
double domain_min; | |
double domain_max; | |
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR lpszCmdParam, int nCmdShow) { | |
char* str = malloc(sizeof(char) * 1000); | |
printf("y = "); | |
scanf("%1000[^\n]", str); | |
expr = parse(str); | |
if (expr == NULL) { | |
printf("Syntax Error"); | |
} | |
printf("min value of x = "); | |
scanf("%lf", &domain_min); | |
printf("max value of x = "); | |
scanf("%lf", &domain_max); | |
printf("%lf to %lf", domain_min, domain_max); | |
WNDCLASS windowClass; | |
windowClass.cbClsExtra = 0; | |
windowClass.cbWndExtra = 0; | |
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); | |
windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |
windowClass.hInstance = instance; | |
windowClass.lpfnWndProc = WndProc; | |
windowClass.lpszClassName = className; | |
windowClass.lpszMenuName = NULL; | |
windowClass.style = CS_HREDRAW | CS_VREDRAW; | |
RegisterClass(&windowClass); | |
HWND window = CreateWindow( | |
className, | |
title, | |
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
800, | |
600, | |
NULL, | |
(HMENU) NULL, | |
instance, | |
NULL | |
); | |
ShowWindow(window, nCmdShow); | |
MSG message; | |
while (GetMessage(&message, NULL, 0, 0)) { | |
TranslateMessage(&message); | |
DispatchMessage(&message); | |
} | |
return (int) message.wParam; | |
} | |
LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) { | |
switch (message) { | |
case WM_DESTROY: { | |
PostQuitMessage(0); | |
return 0; | |
} | |
case WM_PAINT: { | |
RECT rect; | |
GetClientRect(window, &rect); | |
PAINTSTRUCT paint; | |
HDC hdc = BeginPaint(window, &paint); | |
// x축, y축을 나타내는 점 목록입니다. | |
POINT axis[4] = { | |
(POINT) { | |
rect.left, | |
(rect.bottom + rect.top) / 2 | |
}, | |
(POINT) { | |
rect.right, | |
(rect.bottom + rect.top) / 2 | |
}, | |
(POINT) { | |
(rect.left + rect.right) / 2, | |
rect.top | |
}, | |
(POINT) { | |
(rect.left + rect.right) / 2, | |
rect.bottom | |
} | |
}; | |
DWORD size[2] = { 2, 2 }; | |
PolyPolyline(hdc, axis, size, 2); | |
double STEP = 0.1; | |
double STRETCH_VERTICAL = 100; | |
long long int SIZE = roundl((domain_max - domain_min) / STEP); | |
POINT* samples = (POINT*) malloc(sizeof(POINT) * SIZE); | |
long long int i; | |
double x = domain_min; | |
// -domain_min부터 domain_max까지 STEP 간격으로 대입해 점 목록을 만듭니다. | |
for (i = 0; i < SIZE; i++) { | |
x += STEP; | |
samples[i] = (POINT) { | |
rect.left + (800 - 500) / 2 + 500 * i / SIZE, | |
(rect.top + rect.bottom) / 2 - evaluate(expr, x) * STRETCH_VERTICAL | |
}; | |
} | |
// 만든 점 목록을 직선으로 이어 그립니다. | |
Polyline(hdc, samples, SIZE); | |
EndPaint(window, &paint); | |
return 0; | |
} | |
default: { | |
return DefWindowProc(window, message, wParam, lParam); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For example: the Taylor series of sin function