Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.exe
.vscode/
Cube.code-workspace
CMake*
build
99 changes: 99 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# EquationParser

Type an equation. See a 3D shape.

EquationParser takes a math string like `((x^2+y^2)^0.5 - 15)^2 + z^2 < 15` and renders the implicit 3D surface it describes — no mesh, no model, just an inequality over `x`, `y`, `z` evaluated at every point in space.

![demo](assets/demo.gif)

---

## What it does

You write an equation as a C string. The parser evaluates it at every point on a 3D grid. Wherever the condition holds, that point is part of the surface. The result gets rendered as a 3D shape.

```c
char *str = "((x^2+y^2)^0.5 - 15)^2 + z^2 < 15"; // donut
```

```c
char *str = "x^4 - 5*x^2 + y^4 - 5*y^2 + z^4 - 5*z^2 + 11.8 < 0"; // tanglecube
```

```c
char *str = "min(min(x^2+y^2, y^2+z^2), x^2+z^2) < 1"; // three intersecting cylinders
```

Any implicit surface you can express as an inequality over `x`, `y`, `z` — just type it.

---

## Equation syntax

| Category | What to write |
|---|---|
| Variables | `x` `y` `z` |
| Arithmetic | `+` `-` `*` `/` `^` |
| Grouping | `(` `)` |
| Comparison | `<` `>` `=` `<=` `>=` |
| Logic | `&` `\|` |
| Functions | `abs(v)` `min(a, b)` `max(a, b)` |
| Numbers | `3` `1.5` `0.75` etc. |

---

## How it works

**1. Tokenizer** — scans the string character by character into typed tokens: numbers, variables, operators, functions.

**2. Shunting-yard** — reorders tokens into Reverse Polish Notation, respecting operator precedence, parentheses, and unary operators.

**3. RPN evaluator** — walks the token queue, substitutes `x/y/z` from the current point, and applies operators via a number stack. Returns a single value.

**4. Visualization** — iterates over a 3D voxel grid, calls the evaluator at each point, and renders wherever the inequality holds.

Parse once, evaluate millions of points.

---

## API

```c
// Parse a string into a reusable RPN queue
tokenQueue q = init("(x^2 + y^2 + z^2)^0.5 < 1");

// Evaluate at any (x, y, z) point — non-destructive, call repeatedly
point p = {0.5, 0.5, 0.0};
double result = evalPolish(&q, p);

free_queue(&q);
```

### Functions

| Function | Description |
|---|---|
| `tokenQueue init(char* str)` | Parse equation string → RPN queue |
| `double evalPolish(tokenQueue* queue, point p)` | Evaluate queue at point `p` |
| `int parseRule(char* rule, token** res)` | Tokenize string, return token count |
| `void convertToPolish(...)` | Convert token array to RPN |

---

## Project structure

```
EquationParser/
├── include/ # Headers — parser.h, types, operator table
└── src/ # Sources — parser, stack, queue, operators
```

---

## Building

Plain C, no external dependencies:

```bash
gcc -o parser src/*.c -Iinclude -lm
```
Binary file added assets/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 11 additions & 12 deletions eval.h → include/eval.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#ifndef _EVAL
#define _EVAL
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
#include <ctype.h>
#include "stack.h"

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

typedef enum {LEFT, RIGHT, NONE} ASSOC;
#include "stack.h"

typedef enum
{
LEFT,
RIGHT,
NONE
} ASSOC;

typedef struct op
{
Expand All @@ -18,7 +21,7 @@ typedef struct op
ASSOC assoc;
char isUnary;
double (*evalfunc)(double, double);
}operator;
} operator;

double evalplus(double a, double b);
double evalminus(double a, double b);
Expand All @@ -36,10 +39,6 @@ double evalmin(double a, double b);
double evaland(double a, double b);
double evalor(double a, double b);





operator* getop(char c);

#endif
14 changes: 13 additions & 1 deletion parser.h → include/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,19 @@
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<windows.h>
#include<time.h>

// Cross-platform sleep and clear screen
#ifdef _WIN32
#include<windows.h>
#define SLEEP(ms) Sleep(ms)
#define CLEAR_SCREEN() system("cls")
#else
#include<unistd.h>
#define SLEEP(ms) usleep((ms) * 1000)
#define CLEAR_SCREEN() system("clear")
#endif

#include "stack.h"
#include "eval.h"

Expand All @@ -25,5 +36,6 @@ int parseRule(char* rule, token** tokens);
void printTokens(token* tokens, int size);
void convertToPolish(tokenQueue* queue, token* tokens, int size);
double evalPolish(tokenQueue* queue, point p);
tokenQueue init(char* str);

#endif
27 changes: 27 additions & 0 deletions include/renderer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef RENDER
#define RENDER

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct rgb
{
int r;
int g;
int b;
} RGB;

typedef struct angles
{
float sinA, sinB, sinC;
float cosA, cosB, cosC;
} Angles;

void calculatePoint(int i, int j, int k, int width, int height, float *zbuffer, char *buffer, RGB *colorbuffer, Angles* angle);
char map(float a);
RGB ColorMap(float a);
void renderObject(int width, int height, char *buffer, RGB *colorbuffer, char *PrevBuffer);

#endif
65 changes: 65 additions & 0 deletions include/stack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#ifndef _STACK_H
#define _STACK_H
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

#include "eval.h"

typedef enum
{
T_NUMBER,
T_OPERATOR,
T_VARIABLE,
} T_TYPE;

typedef struct token
{
union value
{
double num; // T_NUMBER
operator* op; // T_OPERATOR
char var; // T_VARIABLE
} value;
T_TYPE type;

} token;

typedef struct tokenNode
{
token _token;
struct tokenNode* next;
} tokenNode;

typedef enum
{
NOT_OK,
OK
} Tboolean;

typedef struct
{
tokenNode* tokenTop;
int top;
} tokenStack;

typedef struct tokenQueue
{
tokenNode *head, *tail;
int size;
} tokenQueue;

void initialize_queue(tokenQueue*);
void enqueue(tokenQueue*, token token);
Tboolean dequeue(tokenQueue* queue, token* token);
void print_queue(tokenQueue* queue);
void free_queue(tokenQueue* queue);
int convert_to_array(token* tokens, tokenQueue* queue);

void initialize_stack(tokenStack* Pstack);
void push(tokenStack* Pstack, token item);
Tboolean pop(tokenStack* Pstack, token* Pitem);
Tboolean peek(tokenStack* PStack, token* token);
void free_stack(tokenStack* Pstack);

#endif
File renamed without changes.
90 changes: 90 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "parser.h"
#include "renderer.h"

#define WIDTH 140
#define HEIGHT 45
#define SIZE (WIDTH * HEIGHT)

// Size of object
const float cWidth = 40;

// EQUATION
char* str = "x^4 - 5*x^2 + y^4 - 5*y^2 + z^4 - 5*z^2 + 11.8 < 0";

// Buffers for output points
char buffer[SIZE];
// Coordinates of projection z-buffering
float zbuffer[SIZE];
// Buffer for color
RGB colorbuffer[SIZE];
// Prev buffer
char PrevBuffer[SIZE];

float A, B, C; // Angles
int background = ' ';
float deltaA, deltaB, deltaC; // delta of Angle

int main()
{
float i, j, g;

CLEAR_SCREEN();

// delta of Angles for rotation
deltaA = 0.09;
deltaB = 0.05;
deltaC = -0.05;

tokenQueue queue = init(str);

// Start
printf("The program will start in 5sec, open console on FULL SCREEN");
SLEEP(5000);

while (1)
{
memset(buffer, background, sizeof(buffer));
memset(zbuffer, 0, sizeof(zbuffer));

Angles ang = {sin(A), sin(B), sin(C), cos(A), cos(B), cos(C)};

for (i = -cWidth / 2; i < cWidth / 2; i += 1)
{
for (j = -cWidth / 2; j < cWidth / 2; j += 1)
{
for (g = -cWidth / 2; g <= cWidth / 2; g += 1)
{
point p = {i, j, g};
if (evalPolish(&queue, p))
{
calculatePoint(i, j, g, WIDTH, HEIGHT, zbuffer, buffer,
colorbuffer, &ang);
}
}
}
}

// Move cursor to home position
printf("%c[%d;%df", 0x1B, 0, 0);

// Coordinates of X Y Z (Debug)
printf("%0.2f %0.2f %0.2f\n", A, B, C);

// Print all points
renderObject(WIDTH, HEIGHT, buffer, colorbuffer, PrevBuffer);

// Change angles
if (abs(C) >= 100)
{
deltaC *= -1;
}
if (abs(B) >= 100) deltaB *= -1;
if (abs(A) >= 100) deltaA *= -1;

C += deltaC; // X rotation (roll)
B += deltaB; // Y rotation (pitch)
A += deltaA; // Z rotation (yaw)

SLEEP(10);
}
}
Loading