├── .gitignore
├── README.md
├── build.bat
├── infinite_power.gif
├── report
├── graphics
│ ├── initial_screen.png
│ ├── jkff_normal.png
│ └── jkff_simulating.png
└── report.pdf
└── src
├── component.c
├── component.h
├── draw.c
├── draw.h
├── interaction.c
├── interaction.h
├── main.c
├── program.c
└── program.h
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin
2 | /fonts
3 | /libs
4 | *.exe
5 | *.json
6 | *.aux
7 | *.log
8 | report/src/*.pdf
9 | *.out
10 | *.toc
11 | *.zip
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Minimalogic
2 | ## Behold! UNLIMITED POWER!!
3 |
4 | 
5 | Does any other digital logic simulator allow you to do THIS? Maybe not! Definitely not!!
6 |
7 |
8 | ## What is Minimalogic?
9 | Minimalogic is a simple digital logic simulator made in C using SDL2 as a project for completion of the course Computer Programming [CT 401]. It was coded with a specific objective of getting the simulation job done easily, the curiousity emanated from our regular Digital Logics lab.
10 | Year: I
11 | Part: I
12 | ### Minimalogic was made by:
13 |
14 | * Aditi Kharel (PUL077BEI008)
15 | * Ashutosh Bohara (PUL077BEI012)
16 | * Pallavi Paudel (PUL077BEI027)
17 | * Rijan Shrestha (PUL077BEI034)
18 |
19 | ## Building and Executing
20 |
21 | **This project can only be built on x64 Windows systems**
22 | 1. Clone this repository
23 | ```shell
24 | git clone https://github.com/First-Sem-C-Project/Minimalogic/
25 | ```
26 | 2. Change directory to 'Minimalogic'
27 | ```shell
28 | cd .\Minimalogic
29 | ```
30 | 3. Execute the build.bat script
31 | ```shell
32 | .\build.bat
33 | ```
34 | 4. Wait for build.bat to finish. It will download libraries and fonts required by the project and then build it.
35 | 5. Go to `bin\ClangBuild` or `bin\GccBuild` or `bin\MSVCBuild` and run `Minimalogic.exe`
36 | 6. Enjoy?
37 |
38 | _Note: Compiling with gcc might fail_
39 |
40 | ## Some ScreenShots from 'MinimaLogic Simulator'
41 | 
42 | 
43 | 
44 |
--------------------------------------------------------------------------------
/build.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | setlocal
4 |
5 | set SourceFiles=../../src/*.c
6 | set OutputName=MinimaLogic.exe
7 |
8 | set CLFlags=-Od
9 | set CLANGFlags=-g -gcodeview
10 | set GCCFlags=-O
11 |
12 | if "%1" neq "optimize" goto DoneConfig
13 | set CLFlags=/Ox
14 | set CLANGFlags=-O3 -gcodeview
15 | set GCCFlags=-O3
16 |
17 | echo -------------------------------------
18 | echo Optimize Build configured
19 | echo -------------------------------------
20 | :DoneConfig
21 |
22 | if not exist "fonts" mkdir fonts
23 | pushd fonts
24 | if exist "anka-coder-narrow/AnkaCoder-C75-r.ttf" goto SkipDownloadUIFont
25 | echo ----------------------------------------
26 | echo Downloading UI Font
27 | echo ----------------------------------------
28 | curl "https://fontlibrary.org/assets/downloads/anka-coder-narrow/4ed9dd9fcfa020d9f9c3de3fffb2b2ad/anka-coder-narrow.zip" --output anka.zip
29 | mkdir anka-coder-narrow
30 | tar -zxvf anka.zip -C anka-coder-narrow
31 | del anka.zip
32 | :SkipDownloadUIFont
33 | if exist "segment7/Segment7Standard.otf" goto SkipDownloadLEDFont
34 | echo ----------------------------------------
35 | echo Downloading LED Font
36 | echo ----------------------------------------
37 | curl "https://fontlibrary.org/assets/downloads/segment7/4cc82137fc130708919bf201c0dc9aae/segment7.zip" --output seg.zip
38 | mkdir segment7
39 | tar -zxvf seg.zip -C segment7
40 | del seg.zip
41 | :SkipDownloadLEDFont
42 | popd
43 |
44 | if not exist "libs" mkdir libs
45 | pushd libs
46 | where cl >nul 2>nul
47 | IF %ERRORLEVEL% NEQ 0 goto CheckForClang
48 | goto DownloadSDL
49 | :CheckForClang
50 | where clang >nul 2>nul
51 | IF %ERRORLEVEL% NEQ 0 goto SkipDownloadSDL
52 | :DownloadSDL
53 | if exist "SDL2/" goto SkipDownloadSDL
54 | mkdir SDL2
55 | echo ----------------------------------------
56 | echo Downloading SDL
57 | echo ----------------------------------------
58 | curl "https://www.libsdl.org/release/SDL2-devel-2.0.14-VC.zip" --output SDL2.zip
59 | tar -zxvf SDL2.zip -C SDL2
60 | del SDL2.zip
61 | echo ----------------------------------------
62 | echo Downloading SDL_ttf
63 | echo ----------------------------------------
64 | curl "https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-devel-2.0.15-VC.zip" --output SDL2_ttf.zip
65 | tar -zxvf SDL2_ttf.zip -C SDL2
66 | del SDL2_ttf.zip
67 | xcopy /Y SDL2\SDL2_ttf-2.0.15\include\SDL_ttf.h SDL2\SDL2-2.0.14\include
68 | ren "SDL2\SDL2-2.0.14\include" "SDL2"
69 |
70 | :SkipDownloadSDL
71 |
72 | where gcc >nul 2>nul
73 | IF %ERRORLEVEL% NEQ 0 goto SkipDownloadSDLMinGw
74 |
75 | if exist "SDL2MinGw/" goto SkipDownloadSDLMinGw
76 | mkdir SDL2MinGw
77 | echo ----------------------------------------
78 | echo Downloading SDLMinGw
79 | echo ----------------------------------------
80 | curl "https://www.libsdl.org/release/SDL2-devel-2.0.14-mingw.tar.gz" --output SDL2MinGw.tar.gz
81 | tar -xf SDL2MinGw.tar.gz -C SDL2MinGw
82 | del SDL2MinGw.tar.gz
83 | echo ----------------------------------------
84 | echo Downloading SDLMinGw_ttf
85 | echo ----------------------------------------
86 | curl "https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-devel-2.0.15-mingw.tar.gz" --output SDL2MinGw_ttf.tar.gz
87 | tar -xf SDL2MinGw_ttf.tar.gz -C SDL2MinGw
88 | del SDL2MinGw_ttf.tar.gz
89 | xcopy /Y SDL2MinGw\SDL2_ttf-2.0.15\x86_64-w64-mingw32\include\SDL2\SDL_ttf.h SDL2MinGw\SDL2-2.0.14\x86_64-w64-mingw32\include\SDL2
90 |
91 | :SkipDownloadSDLMinGw
92 | popd
93 |
94 | set SDL2_Include="../../libs/SDL2/SDL2-2.0.14/"
95 | set SDL2_Library="../../libs/SDL2/SDL2-2.0.14/lib/x64/"
96 | set SDL2_DLL="..\..\libs\SDL2\SDL2-2.0.14\lib\x64\SDL2.dll"
97 |
98 | set SDL2_ttf_Library="../../libs/SDL2/SDL2_ttf-2.0.15/lib/x64"
99 | set SDL2_ttf_DLL="..\..\libs\SDL2\SDL2_ttf-2.0.15\lib\x64\*.dll"
100 |
101 | set SDL2MinGw_Include="../../libs/SDL2MinGw/SDL2-2.0.14/x86_64-w64-mingw32/include/"
102 | set SDL2MinGw_Library="../../libs/SDL2MinGw/SDL2-2.0.14/x86_64-w64-mingw32/lib/"
103 | set SDL2MinGw_DLL="..\..\libs\SDL2MinGw\SDL2-2.0.14\x86_64-w64-mingw32\bin\SDL2.dll"
104 |
105 | set SDL2MinGw_ttf_Library="../../libs/SDL2MinGw/SDL2_ttf-2.0.15/x86_64-w64-mingw32/lib/"
106 | set SDL2MinGw_ttf_DLL="..\..\libs\SDL2MinGw\SDL2_ttf-2.0.15\x86_64-w64-mingw32\bin\*.dll"
107 | set UIFont="..\..\fonts\anka-coder-narrow\AnkaCoder-C75-r.ttf"
108 | set LEDFont="..\..\fonts\segment7\*.otf"
109 |
110 | if not exist "bin" mkdir bin
111 |
112 | echo ----------------------------------------
113 | :MSVC
114 | where cl >nul 2>nul
115 | IF %ERRORLEVEL% NEQ 0 goto SkipMSVC
116 | echo Building with MSVC
117 | if not exist "bin\MSVCBuild" mkdir bin\MSVCBuild
118 | pushd bin\MSVCBuild
119 | xcopy %SDL2_DLL% .\ /Y
120 | xcopy %SDL2_ttf_DLL% .\ /Y
121 | if not exist "ui_font.ttf" (
122 | xcopy %UIFont% .\ /Y
123 | ren "AnkaCoder-C75-r.ttf" "ui_font.ttf"
124 | )
125 | if not exist "led_font.otf" (
126 | xcopy %LEDFont% .\ /Y
127 | ren "Segment7Standard.otf" "led_font.otf"
128 | )
129 | call cl -I%SDL2_Include% %CLFlags% -nologo -Zi -EHsc %SourceFiles% -Fe%OutputName% /link /LIBPATH:%SDL2_Library% SDL2.lib SDL2main.lib Shell32.lib Comdlg32.lib /LIBPATH:%SDL2_ttf_Library% SDL2_ttf.lib /subsystem:windows
130 | popd
131 | echo MSVC Build Complete
132 | echo ----------------------------------------
133 | goto CLANG
134 |
135 | :SkipMSVC
136 | echo MSVC not found. Skipping build with MSVC.
137 | echo ----------------------------------------
138 |
139 | :CLANG
140 | where clang >nul 2>nul
141 | IF %ERRORLEVEL% NEQ 0 goto SkipCLANG
142 | echo Building with CLANG
143 | if not exist "bin\ClangBuild" mkdir bin\ClangBuild
144 | pushd bin\ClangBuild
145 | xcopy %SDL2_DLL% .\ /Y
146 | xcopy %SDL2_ttf_DLL% .\ /Y
147 | if not exist "ui_font.ttf" (
148 | xcopy %UIFont% .\ /Y
149 | ren "AnkaCoder-C75-r.ttf" "ui_font.ttf"
150 | )
151 | if not exist "led_font.otf" (
152 | xcopy %LEDFont% .\ /Y
153 | ren "Segment7Standard.otf" "led_font.otf"
154 | )
155 | call clang -I%SDL2_Include% -L%SDL2_Library% -L%SDL2_ttf_Library% %CLANGFlags% %SourceFiles% -o %OutputName% -lSDL2main -lSDL2 -lSDL2_ttf -lShell32 -lComdlg32 -Xlinker -subsystem:windows
156 | echo Clang Build Complete
157 | echo ----------------------------------------
158 | popd
159 | goto GCC
160 |
161 | :SkipCLANG
162 | echo Clang not found. Skipping build with Clang.
163 | echo ----------------------------------------
164 |
165 | :GCC
166 | where gcc >nul 2>nul
167 | IF %ERRORLEVEL% NEQ 0 goto SkipGCC
168 | echo Building with GCC
169 | if not exist "bin\GccBuild" mkdir bin\GccBuild
170 | pushd bin\GccBuild
171 | xcopy %SDL2MinGw_DLL% .\ /Y
172 | xcopy %SDL2MinGw_ttf_DLL% .\ /Y
173 | if not exist "ui_font.ttf" (
174 | xcopy %UIFont% .\ /Y
175 | ren "AnkaCoder-C75-r.ttf" "ui_font.ttf"
176 | )
177 | if not exist "led_font.otf" (
178 | xcopy %LEDFont% .\ /Y
179 | ren "Segment7Standard.otf" "led_font.otf"
180 | )
181 | call gcc -I%SDL2MinGw_Include% -L%SDL2MinGw_Library% -L%SDL2MinGw_ttf_Library% %GCCFlags% %SourceFiles% -o %OutputName% -w -Wl,-subsystem,windows -lmingw32 -lSDL2main -lSDL2 -lSDL2_ttf -lComdlg32
182 | echo Gcc Build Complete
183 | echo ----------------------------------------
184 | popd
185 | goto Finished
186 |
187 | :SkipGCC
188 | echo Gcc not found. Skipping build with Gcc.
189 | echo ----------------------------------------
190 |
191 | :Finished
192 |
--------------------------------------------------------------------------------
/infinite_power.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/First-Sem-C-Project/Minimalogic/914cd1a2c487a514a56a5379bf3c4e3b86ea8499/infinite_power.gif
--------------------------------------------------------------------------------
/report/graphics/initial_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/First-Sem-C-Project/Minimalogic/914cd1a2c487a514a56a5379bf3c4e3b86ea8499/report/graphics/initial_screen.png
--------------------------------------------------------------------------------
/report/graphics/jkff_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/First-Sem-C-Project/Minimalogic/914cd1a2c487a514a56a5379bf3c4e3b86ea8499/report/graphics/jkff_normal.png
--------------------------------------------------------------------------------
/report/graphics/jkff_simulating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/First-Sem-C-Project/Minimalogic/914cd1a2c487a514a56a5379bf3c4e3b86ea8499/report/graphics/jkff_simulating.png
--------------------------------------------------------------------------------
/report/report.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/First-Sem-C-Project/Minimalogic/914cd1a2c487a514a56a5379bf3c4e3b86ea8499/report/report.pdf
--------------------------------------------------------------------------------
/src/component.c:
--------------------------------------------------------------------------------
1 | #include "component.h"
2 | #define SCALE 12
3 |
4 | extern Component ComponentList[256];
5 | extern int time;
6 | extern bool AlreadyUpdated[256];
7 |
8 | static void Tick(Component *component);
9 | static void orGate(Component *component);
10 | static void andGate(Component *component);
11 | static void notGate(Component *component);
12 | static void norGate(Component *component);
13 | static void xorGate(Component *component);
14 | static void xnorGate(Component *component);
15 | static void nandGate(Component *component);
16 | static void DoNothing(Component *component);
17 | static void Decode(Component *component);
18 | static int BinToDec(bool[4]);
19 |
20 | static void (*operate[g_total])(Component *component) = {DoNothing, DoNothing, Tick, notGate, Decode, Decode, andGate, orGate, nandGate, norGate, xorGate, xnorGate};
21 |
22 | static void SetInputs(Component *component)
23 | {
24 | for (int i = 0; i < component->inum; i++)
25 | {
26 | if (component->inpSrc[i].x != -1)
27 | {
28 | if (!AlreadyUpdated[component->inpSrc[i].x])
29 | {
30 | AlreadyUpdated[component->inpSrc[i].x] = true;
31 | update(component->inputs[i]);
32 | }
33 | }
34 | }
35 | }
36 |
37 | void update(Component *component)
38 | {
39 | SetInputs(component);
40 | operate[component->type](component);
41 | }
42 |
43 | void GetWidthHeight(int *w, int *h, Type type, int size)
44 | {
45 | if (type < g_not)
46 | {
47 | *w = SCALE;
48 | *h = SCALE;
49 | }
50 | else if (type == g_not)
51 | {
52 | *w = 3 * SCALE;
53 | *h = SCALE;
54 | }
55 | else if (type >= g_and)
56 | {
57 | *w = 4 * SCALE;
58 | *h = size * SCALE;
59 | }
60 | else
61 | {
62 | *w = 5 * SCALE;
63 | *h = 8 * SCALE;
64 | }
65 | }
66 |
67 | void SetIOPos(Component *component)
68 | {
69 | for (int i = component->inum; i < MAX_INPUTS; i++)
70 | {
71 | component->inpPos[i].y = -1;
72 | component->inpPos[i].x = -1;
73 | }
74 | for (int i = component->onum; i < MAX_TERM_NUM; i++)
75 | {
76 | component->outPos[i].y = -1;
77 | component->outPos[i].x = -1;
78 | }
79 | for (int i = 0; i < component->inum; i++)
80 | {
81 | component->inpPos[i].x = component->start.x;
82 | component->inpPos[i].y = (2 * component->start.y + component->size) / component->inum * (i + 1);
83 | }
84 | for (int i = 0; i < component->onum; i++)
85 | {
86 | component->outPos[i].x = component->start.x + component->width - 1;
87 | component->outPos[i].y = (2 * component->start.y + component->size) / component->onum * (i + 1);
88 | }
89 | }
90 |
91 | void ClearIO(Component *component)
92 | {
93 | for (int i = 0; i < MAX_TERM_NUM; i++)
94 | {
95 | if (i < MAX_INPUTS)
96 | {
97 | component->inpSrc[i] = (Pair){-1, -1};
98 | component->inputs[i] = false;
99 | }
100 | component->outputs[i] = false;
101 | }
102 | }
103 |
104 | static Component SingleInputComponent(Type type, Pair pos)
105 | {
106 | Component component;
107 | component.size = 1;
108 | component.width = 1 + 2 * (type == g_not);
109 | component.size *= SCALE;
110 | component.width *= SCALE;
111 | component.start = pos;
112 | component.childCount = 0;
113 | component.type = type;
114 | ClearIO(&component);
115 | if (type == g_not)
116 | {
117 | component.inum = 1;
118 | component.onum = 1;
119 | }
120 | else if (type == probe)
121 | {
122 | component.inum = 1;
123 | component.onum = 0;
124 | }
125 | else
126 | {
127 | component.inum = 0;
128 | component.onum = 1;
129 | }
130 | SetIOPos(&component);
131 | return component;
132 | }
133 |
134 | static Component MultiInputComponent(Type type, int inpNum, Pair pos)
135 | {
136 | Component component;
137 | component.start = pos;
138 | component.size = inpNum;
139 | component.inum = inpNum;
140 | component.onum = 1;
141 | component.width = 4;
142 | component.size *= SCALE;
143 | component.width *= SCALE;
144 | component.childCount = 0;
145 | component.type = type;
146 | ClearIO(&component);
147 | SetIOPos(&component);
148 | return component;
149 | }
150 |
151 | static Component MultiOutComponent(Type type, Pair pos)
152 | {
153 | Component component;
154 | component.width = 5;
155 | component.start = pos;
156 | component.childCount = 0;
157 |
158 | switch (type)
159 | {
160 | case d_oct:
161 | component.inum = 3;
162 | component.onum = 8;
163 | break;
164 | case d_4x16:
165 | component.inum = 4;
166 | component.onum = 16;
167 | break;
168 | default:
169 | break;
170 | }
171 |
172 | component.size = 8;
173 | component.size *= SCALE;
174 | component.width *= SCALE;
175 | component.type = type;
176 | ClearIO(&component);
177 | SetIOPos(&component);
178 | return component;
179 | }
180 |
181 | Component GetComponent(Type type, char inpNum, Pair pos)
182 | {
183 | if (type <= g_not)
184 | return SingleInputComponent(type, pos);
185 | else if (type < g_and)
186 | return MultiOutComponent(type, pos);
187 | else
188 | return MultiInputComponent(type, inpNum, pos);
189 | }
190 |
191 | static void andGate(Component *component)
192 | {
193 | if (component->inpSrc[0].x >= 0)
194 | {
195 | component->outputs[0] = component->inputs[0]->outputs[component->inpSrc[0].y];
196 | }
197 | else
198 | {
199 | component->outputs[0] = false;
200 | }
201 | for (int i = 1; i < component->inum; i++)
202 | {
203 | if (component->inpSrc[i].x >= 0)
204 | {
205 | component->outputs[0] = component->outputs[0] && component->inputs[i]->outputs[component->inpSrc[i].y];
206 | }
207 | else
208 | {
209 | component->outputs[0] = false;
210 | break;
211 | }
212 | }
213 | }
214 |
215 | static void orGate(Component *component)
216 | {
217 | if (component->inpSrc[0].x >= 0)
218 | {
219 | component->outputs[0] = component->inputs[0]->outputs[component->inpSrc[0].y];
220 | }
221 | else
222 | {
223 | component->outputs[0] = false;
224 | }
225 | for (int i = 1; i < component->inum; i++)
226 | {
227 | if (component->inpSrc[i].x >= 0)
228 | {
229 | component->outputs[0] = component->outputs[0] || component->inputs[i]->outputs[component->inpSrc[i].y];
230 | }
231 | else
232 | {
233 | component->outputs[0] = component->outputs[0] || false;
234 | }
235 | }
236 | }
237 |
238 | static void nandGate(Component *component)
239 | {
240 | andGate(component);
241 | component->outputs[0] = !component->outputs[0];
242 | }
243 |
244 | static void norGate(Component *component)
245 | {
246 | orGate(component);
247 | component->outputs[0] = !component->outputs[0];
248 | }
249 |
250 | static void xorGate(Component *component)
251 | {
252 | if (component->inpSrc[0].x >= 0)
253 | {
254 | component->outputs[0] = component->inputs[0]->outputs[component->inpSrc[0].y];
255 | }
256 | else
257 | {
258 | component->outputs[0] = false;
259 | }
260 | for (int i = 1; i < component->inum; i++)
261 | {
262 | if (component->inpSrc[i].x >= 0)
263 | {
264 | component->outputs[0] = (component->outputs[0] && !component->inputs[i]->outputs[component->inpSrc[i].y]) ||
265 | (!component->outputs[0] && component->inputs[i]->outputs[component->inpSrc[i].y]);
266 | }
267 | }
268 | }
269 |
270 | static void xnorGate(Component *component)
271 | {
272 | xorGate(component);
273 | component->outputs[0] = !component->outputs[0];
274 | }
275 |
276 | void notGate(Component *component)
277 | {
278 | if (component->inpSrc[0].x >= 0)
279 | {
280 | component->outputs[0] = !component->inputs[0]->outputs[component->inpSrc[0].y];
281 | }
282 | else
283 | {
284 | component->outputs[0] = true;
285 | }
286 | }
287 |
288 | static void Tick(Component *component)
289 | {
290 | if (time == 0)
291 | component->outputs[0] = !component->outputs[0];
292 | }
293 |
294 | static void DoNothing(Component *component) {}
295 |
296 | static void Decode(Component *component)
297 | {
298 | bool toDecode[4];
299 | char stop = component->type == d_oct ? 3 : 4;
300 | toDecode[0] = false;
301 | for (int i = 0; i < stop; i++)
302 | {
303 | if (component->inpSrc[i].x >= 0)
304 | toDecode[i + (component->type == d_oct)] = component->inputs[i]->outputs[component->inpSrc[i].y];
305 | else
306 | toDecode[i + (component->type == d_oct)] = false;
307 | }
308 | for (int i = 0; i < component->onum; i++)
309 | component->outputs[i] = false;
310 | component->outputs[BinToDec(toDecode)] = true;
311 | };
312 |
313 | static int BinToDec(bool binary[4])
314 | {
315 | return binary[0] * 8 + binary[1] * 4 + binary[2] * 2 + binary[3];
316 | }
317 |
--------------------------------------------------------------------------------
/src/component.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #define MAX_TERM_NUM 16
4 | #define MIN_INPUT_NUM 2
5 | #define MAX_INPUTS 5
6 | typedef struct
7 | {
8 | int x, y;
9 | } Pair;
10 |
11 | typedef enum
12 | {
13 | state,
14 | probe,
15 | clock,
16 | g_not,
17 | d_oct,
18 | d_4x16,
19 | g_and,
20 | g_or,
21 | g_nand,
22 | g_nor,
23 | g_xor,
24 | g_xnor,
25 | g_total
26 | } Type;
27 |
28 | typedef struct _component
29 | {
30 | Pair start, inpPos[MAX_INPUTS], outPos[MAX_TERM_NUM], inpSrc[MAX_INPUTS];
31 | unsigned char size, inum, onum, width, childCount;
32 | bool outputs[MAX_TERM_NUM];
33 | Type type;
34 | struct _component *inputs[MAX_INPUTS];
35 | } Component;
36 |
37 | void GetWidthHeight(int *w, int *h, Type type, int size);
38 | Component GetComponent(Type, char, Pair);
39 | void SetIOPos(Component *component);
40 | void update(Component *);
41 | void ClearIO(Component *);
42 |
--------------------------------------------------------------------------------
/src/draw.c:
--------------------------------------------------------------------------------
1 | #include "draw.h"
2 | #define cell(y, x) grid[y * GRID_ROW + x]
3 |
4 | #define BG1 59, 55, 53, 255
5 | #define BG2 69, 66, 62, 255
6 | #define BG 41, 41, 41, 255
7 |
8 | #define MAX_WIRE_PTS 20
9 | #define AND_COLOR RED
10 | #define OR_COLOR ORANGE
11 | #define NAND_COLOR GREEN
12 | #define NOR_COLOR PURPLE
13 | #define XOR_COLOR YELLOW
14 | #define XNOR_COLOR VOMIT_GREEN
15 | #define NOT_COLOR GRAY
16 | #define LED_COLOR BLUE
17 | #define NO_COLOR 100, 100, 100
18 | #define HIGH_COLOR 180, 51, 48
19 | #define LOW_COLOR 51, 48, 180
20 | #define WIRE_NEUTRAL 58, 160, 61
21 | #define WIRE_HIGH_D 100, 31, 28
22 | #define WIRE_LOW_D 31, 28, 100
23 | #define WIRE_NEUTRAL_D 28, 100, 31
24 | extern Component ComponentList[256];
25 | extern unsigned char componentCount;
26 |
27 | static SDL_Point tmpWire[2];
28 | extern SDL_Window *window;
29 | extern SDL_Renderer *renderer;
30 | extern Button Components[g_total];
31 | extern Button FileMenu[fm_total];
32 | extern Button SideMenu[sm_total];
33 | extern Button confirmYes;
34 | extern Button confirmNo;
35 | extern Button confirmCancel;
36 |
37 | extern int characterWidth[127 - 32];
38 | extern SDL_Texture *characters[127 - 32];
39 | extern SDL_Texture *displayChars[16];
40 | extern TTF_Font *font;
41 | extern TTF_Font *displayFont;
42 |
43 | extern SDL_Rect InputsCount;
44 | extern SDL_Rect InputsCountText;
45 |
46 | static char *compoTexts[g_total] = {
47 | "STATE",
48 | "PROBE",
49 | "CLOCK",
50 | "NOT",
51 | "OCT DECODER",
52 | "4 TO 16 DECODER",
53 | "AND",
54 | "OR",
55 | "NAND",
56 | "NOR",
57 | "XOR",
58 | "XNOR"};
59 |
60 | static SDL_Color compoColors[g_total] = {
61 | {NO_COLOR},
62 | {NO_COLOR},
63 | {NO_COLOR},
64 | {NOT_COLOR},
65 | {LED_COLOR},
66 | {LED_COLOR},
67 | {AND_COLOR},
68 | {OR_COLOR},
69 | {NAND_COLOR},
70 | {NOR_COLOR},
71 | {XOR_COLOR},
72 | {XNOR_COLOR}};
73 |
74 | static void DisplayText(char *message, SDL_Rect parent)
75 | {
76 | char *tmp = message;
77 | int totalWidth = 0;
78 | float factor = 1;
79 | for (; *tmp; tmp++)
80 | totalWidth += characterWidth[*tmp - 32];
81 | SDL_Rect charDest = {.y = parent.y, .h = parent.h};
82 |
83 | if (totalWidth > parent.w)
84 | {
85 | factor = parent.w / (float)totalWidth;
86 | tmp = message;
87 | totalWidth = 0;
88 | for (; *tmp; tmp++)
89 | totalWidth += characterWidth[*tmp - 32] * factor;
90 | }
91 | charDest.x = parent.x + (parent.w - totalWidth) / 2;
92 |
93 | for (int i = 0; *message; message++, i++)
94 | {
95 | charDest.w = characterWidth[*message - 32] * factor;
96 | SDL_RenderCopy(renderer, characters[*message - 32], NULL, &charDest);
97 | charDest.x += charDest.w;
98 | }
99 | }
100 |
101 | static void RenderGateText(SDL_Rect compo, Type type)
102 | {
103 | if (type >= g_and || type == g_not)
104 | {
105 | SDL_Rect textRect = {compo.x + compo.w / 2, compo.y + compo.h / 2 - CELL_SIZE * SCALE / 2,
106 | 0, CELL_SIZE * SCALE};
107 | char *tmp = compoTexts[type];
108 | for (; *tmp; tmp++)
109 | textRect.w += characterWidth[*tmp - 32];
110 | textRect.x -= textRect.w / 2;
111 | DisplayText(compoTexts[type], textRect);
112 | }
113 | }
114 |
115 | static char *SideMenuButtonText[sm_total] = {
116 | "",
117 | "Components",
118 | "+",
119 | "-",
120 | "Undo",
121 | "Redo",
122 | "",
123 | "Delete Component",
124 | "Clear Grid",
125 | "File Menu"};
126 |
127 | static char *FileMenuButtonText[fm_total] = {
128 | "New File",
129 | "Open File",
130 | "Save",
131 | "Save As",
132 | "Exit Menu",
133 | "Exit Program"};
134 |
135 | static void DrawMenu(bool menuExpanded, bool simulating, bool snap, Selection choice)
136 | {
137 | int w, h;
138 | SDL_GetWindowSize(window, &w, &h);
139 | SDL_Rect menuBg = {0, 0, MENU_WIDTH, h};
140 | SDL_SetRenderDrawColor(renderer, BG1);
141 | SDL_RenderFillRect(renderer, &menuBg);
142 | SDL_SetRenderDrawColor(renderer, SideMenu[sm_run].color.r, SideMenu[sm_run].color.g,
143 | SideMenu[sm_run].color.b, 255);
144 | SDL_RenderFillRect(renderer, &SideMenu[sm_run].buttonRect);
145 | if (simulating)
146 | DisplayText("STOP", SideMenu[sm_run].buttonRect);
147 | else
148 | DisplayText("RUN", SideMenu[sm_run].buttonRect);
149 |
150 | SDL_SetRenderDrawColor(renderer, SideMenu[sm_compo].color.r,
151 | SideMenu[sm_compo].color.g, SideMenu[sm_compo].color.b,
152 | 255);
153 | for (int i = 1; i < sm_total; i++)
154 | {
155 | if (i == sm_inc || i == sm_dec && choice.type < g_and)
156 | continue;
157 | SDL_RenderFillRect(renderer, &SideMenu[i].buttonRect);
158 | DisplayText(SideMenuButtonText[i], SideMenu[i].buttonRect);
159 | }
160 |
161 | if (snap)
162 | DisplayText("Snap to Grid: On", SideMenu[sm_snap].buttonRect);
163 | else
164 | DisplayText("Snap to Grid: Off", SideMenu[sm_snap].buttonRect);
165 |
166 | if (choice.type >= g_and)
167 | {
168 | SDL_SetRenderDrawColor(renderer, BLACK, 255);
169 | SDL_RenderFillRect(renderer, &InputsCount);
170 | char tmptxt[10] = "Inputs: ";
171 | tmptxt[8] = (char)(choice.size - 2 + 50);
172 | DisplayText(tmptxt, InputsCount);
173 |
174 | SDL_SetRenderDrawColor(renderer, SideMenu[sm_inc].color.r, SideMenu[sm_inc].color.g,
175 | SideMenu[sm_inc].color.b, 255);
176 | SDL_RenderFillRect(renderer, &SideMenu[sm_inc].buttonRect);
177 | DisplayText("+", SideMenu[sm_inc].buttonRect);
178 | SDL_RenderFillRect(renderer, &SideMenu[sm_dec].buttonRect);
179 | DisplayText("-", SideMenu[sm_dec].buttonRect);
180 | }
181 |
182 | if (menuExpanded)
183 | {
184 | SDL_Rect wrapper = {SideMenu[sm_compo].buttonRect.x,
185 | SideMenu[sm_compo].buttonRect.y +
186 | SideMenu[sm_compo].buttonRect.h,
187 | SideMenu[sm_compo].buttonRect.w, 2 + g_total * (25 + 2)};
188 | SDL_SetRenderDrawColor(renderer, BG2);
189 | SDL_RenderFillRect(renderer, &wrapper);
190 |
191 | for (int i = 0; i < g_total; i++)
192 | {
193 | SDL_SetRenderDrawColor(renderer, Components[i].color.r,
194 | Components[i].color.g, Components[i].color.b, 255);
195 | SDL_RenderFillRect(renderer, &Components[i].buttonRect);
196 | DisplayText(compoTexts[i], Components[i].buttonRect);
197 | }
198 | }
199 | }
200 |
201 | static void DrawConfirmationScreen(ConfirmationFlags flag)
202 | {
203 | int w, h;
204 | SDL_GetWindowSize(window, &w, &h);
205 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
206 | SDL_Rect darken = {.x = 0, .y = 0, .w = w, .h = h};
207 | SDL_SetRenderDrawColor(renderer, BLACK, 100);
208 | SDL_RenderFillRect(renderer, &darken);
209 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
210 | SDL_Rect box;
211 | SDL_SetRenderDrawColor(renderer, BG2);
212 | if (flag != fileMenuFlag)
213 | {
214 | box = (SDL_Rect){.x = w / 2 - 200, .y = h / 2 - 100, .w = 400, .h = 200};
215 | SDL_Rect message = {.x = box.x + 10, box.y + box.h / 4, box.w - 20, MENU_FONT_SIZE};
216 | SDL_RenderFillRect(renderer, &box);
217 | if (flag == clearGrid)
218 | DisplayText("Clear Grid? This action cannot be undone.", message);
219 | else if (flag == q_saveNewFile || flag == o_saveNewFile || flag == n_saveNewFile)
220 | {
221 | SDL_SetRenderDrawColor(renderer, confirmCancel.color.r, confirmCancel.color.g, confirmCancel.color.b, 255);
222 | SDL_RenderFillRect(renderer, &confirmCancel.buttonRect);
223 | DisplayText("Cancel", confirmCancel.buttonRect);
224 | DisplayText("Do you want to save your work?", message);
225 | }
226 | else if (flag == q_saveChanges || flag == o_saveChanges || flag == n_saveChanges)
227 | {
228 | SDL_SetRenderDrawColor(renderer, confirmCancel.color.r, confirmCancel.color.g, confirmCancel.color.b, 255);
229 | SDL_RenderFillRect(renderer, &confirmCancel.buttonRect);
230 | DisplayText("Cancel", confirmCancel.buttonRect);
231 | DisplayText("Save changes to the file?", message);
232 | }
233 | SDL_SetRenderDrawColor(renderer, confirmYes.color.r, confirmYes.color.g, confirmYes.color.b, 255);
234 | SDL_RenderFillRect(renderer, &confirmYes.buttonRect);
235 | DisplayText("Yes", confirmYes.buttonRect);
236 | SDL_SetRenderDrawColor(renderer, confirmNo.color.r, confirmNo.color.g, confirmNo.color.b, 255);
237 | SDL_RenderFillRect(renderer, &confirmNo.buttonRect);
238 | DisplayText("No", confirmNo.buttonRect);
239 | }
240 |
241 | if (flag == fileMenuFlag)
242 | {
243 | box = (SDL_Rect){.x = FileMenu[0].buttonRect.x - 10, .y = FileMenu[0].buttonRect.y - 10, .w = FileMenu[0].buttonRect.w + 20, .h = (FileMenu[fm_total - 1].buttonRect.y - FileMenu[0].buttonRect.y) + FileMenu[0].buttonRect.h + 20};
244 | SDL_RenderFillRect(renderer, &box);
245 | }
246 | for (int i = 0; i < fm_total && flag == fileMenuFlag; i++)
247 | {
248 | SDL_SetRenderDrawColor(renderer, BLACK, 255);
249 | SDL_RenderFillRect(renderer, &FileMenu[i].buttonRect);
250 | DisplayText(FileMenuButtonText[i], FileMenu[i].buttonRect);
251 | }
252 | }
253 |
254 | static void HoverOver(Pair button, bool menuExpanded, ConfirmationFlags showConfirmScreen)
255 | {
256 | if (button.x < 0)
257 | return;
258 | bool toHover = !showConfirmScreen;
259 | SDL_Rect border;
260 | if (button.x == cm)
261 | {
262 | toHover = menuExpanded;
263 | border = (SDL_Rect){Components[button.y].buttonRect.x - 1, Components[button.y].buttonRect.y - 1,
264 | Components[button.y].buttonRect.w + 2, Components[button.y].buttonRect.h + 2};
265 | }
266 | else if (button.x == con)
267 | {
268 | if (button.y > 0)
269 | border = (SDL_Rect){confirmYes.buttonRect.x - 1, confirmYes.buttonRect.y - 1,
270 | confirmYes.buttonRect.w + 2, confirmYes.buttonRect.h + 2};
271 | else if (!button.y)
272 | border = (SDL_Rect){confirmNo.buttonRect.x - 1, confirmNo.buttonRect.y - 1,
273 | confirmNo.buttonRect.w + 2, confirmNo.buttonRect.h + 2};
274 | else
275 | border = (SDL_Rect){confirmCancel.buttonRect.x - 1, confirmCancel.buttonRect.y - 1,
276 | confirmCancel.buttonRect.w + 2, confirmCancel.buttonRect.h + 2};
277 | toHover = showConfirmScreen;
278 | }
279 | else if (button.x == fm)
280 | {
281 | border = (SDL_Rect){FileMenu[button.y].buttonRect.x - 1, FileMenu[button.y].buttonRect.y - 1,
282 | FileMenu[button.y].buttonRect.w + 2, FileMenu[button.y].buttonRect.h + 2};
283 | toHover = showConfirmScreen;
284 | }
285 | else if (button.x == sm)
286 | {
287 | border = (SDL_Rect){SideMenu[button.y].buttonRect.x - 1, SideMenu[button.y].buttonRect.y - 1,
288 | SideMenu[button.y].buttonRect.w + 2, SideMenu[button.y].buttonRect.h + 2};
289 | toHover = !showConfirmScreen;
290 | }
291 | if (toHover)
292 | {
293 | SDL_SetRenderDrawColor(renderer, 25, 255, 0, 255);
294 | SDL_RenderDrawRect(renderer, &border);
295 | }
296 | }
297 |
298 | static void HighlightSelected(Type type)
299 | {
300 | for (int i = 0; i < g_total; i++)
301 | {
302 | if (Components[i].selection.type == type)
303 | {
304 | Components[i].color.r = 50;
305 | Components[i].color.g = 50;
306 | Components[i].color.b = 50;
307 | }
308 | }
309 | }
310 |
311 | void UnHighlight(Type type)
312 | {
313 | for (int i = 0; i < g_total; i++)
314 | {
315 | if (Components[i].selection.type == type)
316 | {
317 | Components[i].color.r = 0;
318 | Components[i].color.g = 0;
319 | Components[i].color.b = 0;
320 | }
321 | }
322 | }
323 |
324 | static void AnimateDropDown(char *animationFlag, bool menuExpanded, bool simulating, Selection choice, bool snap)
325 | {
326 | if (menuExpanded)
327 | {
328 | SDL_SetRenderDrawColor(renderer, BG1);
329 | SDL_Rect cover = {SideMenu[sm_compo].buttonRect.x,
330 | SideMenu[sm_compo].buttonRect.y +
331 | SideMenu[sm_compo].buttonRect.h +
332 | (2 * (*animationFlag) - 1) * (25 + 2),
333 | SideMenu[sm_compo].buttonRect.w,
334 | 2 + (g_total + 1 - 2 * (*animationFlag)) * (25 + 2)};
335 | SDL_RenderFillRect(renderer, &cover);
336 | *animationFlag += 1;
337 | }
338 | else
339 | {
340 | DrawMenu(true, simulating, snap, choice);
341 | SDL_SetRenderDrawColor(renderer, BG1);
342 | SDL_Rect cover = {SideMenu[sm_compo].buttonRect.x,
343 | SideMenu[sm_compo].buttonRect.y +
344 | SideMenu[sm_compo].buttonRect.h +
345 | (2 * (*animationFlag)) * (25 + 2),
346 | SideMenu[sm_compo].buttonRect.w,
347 | 2 + (g_total - 2 * (*animationFlag)) * (25 + 2)};
348 | SDL_RenderFillRect(renderer, &cover);
349 | *animationFlag -= 1;
350 | }
351 | }
352 |
353 | bool StartWiring(Pair pos)
354 | {
355 | tmpWire[0].x = pos.x;
356 | tmpWire[0].y = pos.y;
357 | tmpWire[1] = tmpWire[0];
358 |
359 | return true;
360 | }
361 |
362 | void WireEndPos(int x, int y)
363 | {
364 | tmpWire[1].x = x;
365 | tmpWire[1].y = y;
366 | }
367 |
368 | static SDL_Point BezierPoint(float t, SDL_Point p[4])
369 | {
370 | float tt = t * t;
371 | float ttt = tt * t;
372 | float u = 1 - t;
373 | float uu = u * u;
374 | float uuu = uu * u;
375 |
376 | return (SDL_Point){
377 | uuu * p[0].x + 3 * uu * t * p[1].x + 3 * u * tt * p[2].x + ttt * p[3].x,
378 | uuu * p[0].y + 3 * uu * t * p[1].y + 3 * u * tt * p[2].y + ttt * p[3].y};
379 | }
380 |
381 | // The wire looks jagged. Might need to implement anti-aliasing
382 | static void DrawWire(SDL_Point start, SDL_Point end, bool hilo, bool simulating)
383 | {
384 | SDL_Point wirePoints[MAX_WIRE_PTS];
385 | for (int i = 0; i < 3; i++)
386 | {
387 | if (abs(start.x - end.x) > abs(start.y - end.y))
388 | {
389 | start.y++;
390 | end.y++;
391 | }
392 | else
393 | {
394 | start.x++;
395 | end.x++;
396 | }
397 | SDL_Point p2 = {start.x + (end.x - start.x) / 3, start.y};
398 | SDL_Point p3 = {end.x - (end.x - start.x) / 3, end.y};
399 | if (i == 1)
400 | {
401 | if (hilo && simulating)
402 | SDL_SetRenderDrawColor(renderer, HIGH_COLOR, 255);
403 | else if (!hilo && simulating)
404 | SDL_SetRenderDrawColor(renderer, LOW_COLOR, 255);
405 | else
406 | SDL_SetRenderDrawColor(renderer, WIRE_NEUTRAL, 255);
407 | }
408 | else
409 | {
410 | if (hilo && simulating)
411 | SDL_SetRenderDrawColor(renderer, WIRE_HIGH_D, 255);
412 | else if (!hilo && simulating)
413 | SDL_SetRenderDrawColor(renderer, WIRE_LOW_D, 255);
414 | else
415 | SDL_SetRenderDrawColor(renderer, WIRE_NEUTRAL_D, 255);
416 | }
417 |
418 | for (int i = 0; i < MAX_WIRE_PTS; i++)
419 | {
420 | float t = (float)i / MAX_WIRE_PTS;
421 | wirePoints[i] = BezierPoint(t, (SDL_Point[4]){start, p2, p3, end});
422 | }
423 | wirePoints[0] = start;
424 | wirePoints[MAX_WIRE_PTS - 1] = end;
425 | SDL_RenderDrawLines(renderer, wirePoints, MAX_WIRE_PTS);
426 | }
427 | }
428 |
429 | static void DrawWires(Component component, int pad_x, int pad_y, bool simulating)
430 | {
431 | SDL_Point start, end;
432 | for (int i = 0; i < component.inum; i++)
433 | {
434 | if (component.inpSrc[i].x >= 0)
435 | {
436 | Component sender = ComponentList[component.inpSrc[i].x];
437 | start.x = component.inpPos[i].x * CELL_SIZE + pad_x + TERMINAL_SIZE / 2;
438 | start.y = component.start.y * CELL_SIZE + pad_y + (i + 1) * CELL_SIZE * component.size / component.inum - CELL_SIZE * component.size / component.inum / 2 - TERMINAL_SIZE / 2 + 3;
439 | end.x = sender.outPos[component.inpSrc[i].y].x * CELL_SIZE + pad_x + CELL_SIZE - TERMINAL_SIZE / 2;
440 | end.y = sender.start.y * CELL_SIZE + pad_y + (component.inpSrc[i].y + 1) * CELL_SIZE * sender.size / sender.onum - CELL_SIZE * sender.size / sender.onum / 2 - TERMINAL_SIZE / 2 + 3;
441 | DrawWire(start, end, component.inputs[i]->outputs[component.inpSrc[i].y], simulating);
442 | }
443 | }
444 | }
445 |
446 | static void DrawIOPins(Component component, int pad_x, int pad_y)
447 | {
448 | SDL_Rect pin;
449 | pin.w = TERMINAL_SIZE;
450 | pin.h = TERMINAL_SIZE;
451 | char hnum = 0, lnum = 0, bnum = 0;
452 | SDL_Rect high[MAX_TERM_NUM + MAX_INPUTS], low[MAX_TERM_NUM + MAX_INPUTS], border[MAX_TERM_NUM + MAX_INPUTS];
453 | for (int i = 0; i < component.inum; i++)
454 | {
455 | if (component.inpPos[i].x >= 0)
456 | {
457 | pin.x = component.inpPos[i].x * CELL_SIZE + pad_x;
458 | pin.y = component.start.y * CELL_SIZE + pad_y + (i + 0.5) * CELL_SIZE * component.size / component.inum - TERMINAL_SIZE / 2;
459 | if (component.inpSrc[i].x >= 0)
460 | {
461 | if (component.inputs[i]->outputs[component.inpSrc[i].y])
462 | {
463 | high[hnum] = pin;
464 | hnum += 1;
465 | }
466 | else
467 | {
468 | low[lnum] = pin;
469 | lnum += 1;
470 | }
471 | }
472 | else
473 | {
474 | low[lnum] = pin;
475 | lnum += 1;
476 | }
477 | border[bnum] = pin;
478 | bnum++;
479 | }
480 | }
481 | for (int i = 0; i < component.onum; i++)
482 | {
483 | if (component.outPos[i].x >= 0)
484 | {
485 | pin.x = component.outPos[i].x * CELL_SIZE + pad_x + CELL_SIZE - TERMINAL_SIZE + 1;
486 | pin.y = component.start.y * CELL_SIZE + pad_y + (i + 1) * CELL_SIZE * component.size / component.onum - CELL_SIZE * component.size / component.onum / 2 - TERMINAL_SIZE / 2;
487 | if (component.outputs[i])
488 | {
489 | high[hnum] = pin;
490 | hnum += 1;
491 | }
492 | else
493 | {
494 | low[lnum] = pin;
495 | lnum += 1;
496 | }
497 | border[bnum] = pin;
498 | bnum++;
499 | }
500 | }
501 | SDL_SetRenderDrawColor(renderer, HIGH_COLOR, 255);
502 | SDL_RenderFillRects(renderer, high, hnum);
503 | SDL_SetRenderDrawColor(renderer, LOW_COLOR, 255);
504 | SDL_RenderFillRects(renderer, low, lnum);
505 | SDL_SetRenderDrawColor(renderer, BLACK, 255);
506 | SDL_RenderDrawRects(renderer, border, bnum);
507 | }
508 |
509 | static void DrawComponent(int w, int h, Pair pos, Type type, int pad_x, int pad_y, int opacity, bool isHigh)
510 | {
511 | SDL_Rect compo;
512 | compo.w = w * CELL_SIZE - 1;
513 | compo.h = h * CELL_SIZE - 1;
514 | compo.x = pos.x * CELL_SIZE + pad_x + 1;
515 | compo.y = pos.y * CELL_SIZE + pad_y + 1;
516 | SDL_Color color = compoColors[type];
517 | if (type < g_not)
518 | {
519 | if (isHigh)
520 | SDL_SetRenderDrawColor(renderer, HIGH_COLOR, opacity);
521 | else
522 | SDL_SetRenderDrawColor(renderer, LOW_COLOR, opacity);
523 | }
524 | else
525 | {
526 | SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, opacity);
527 | }
528 | SDL_RenderFillRect(renderer, &compo);
529 | RenderGateText(compo, type);
530 | }
531 |
532 | static void DrawComponents(int pad_x, int pad_y)
533 | {
534 | for (int i = 0; i < componentCount; i++)
535 | {
536 | if (ComponentList[i].type != probe)
537 | DrawComponent(ComponentList[i].width, ComponentList[i].size, ComponentList[i].start, ComponentList[i].type, pad_x, pad_y, 255, ComponentList[i].outputs[0]);
538 | else if (ComponentList[i].inpSrc[0].y >= 0)
539 | DrawComponent(ComponentList[i].width, ComponentList[i].size, ComponentList[i].start, ComponentList[i].type, pad_x, pad_y, 255, ComponentList[i].inputs[0]->outputs[ComponentList[i].inpSrc[0].y]);
540 | else
541 | DrawComponent(ComponentList[i].width, ComponentList[i].size, ComponentList[i].start, ComponentList[i].type, pad_x, pad_y, 255, false);
542 | if (ComponentList[i].type == d_oct || ComponentList[i].type == d_4x16)
543 | {
544 | for (int j = 0; j < ComponentList[i].onum; j++)
545 | {
546 | if (ComponentList[i].outputs[j])
547 | {
548 | SDL_Rect display;
549 | display.w = ComponentList[i].width / 2 * CELL_SIZE;
550 | display.h = ComponentList[i].size / 2 * CELL_SIZE;
551 | display.x = ComponentList[i].start.x * CELL_SIZE + pad_x + ComponentList[i].width / 4 * CELL_SIZE;
552 | display.y = ComponentList[i].start.y * CELL_SIZE + pad_y + ComponentList[i].size / 4 * CELL_SIZE;
553 | SDL_RenderCopy(renderer, displayChars[j], NULL, &display);
554 | break;
555 | }
556 | }
557 | }
558 | DrawIOPins(ComponentList[i], pad_x, pad_y);
559 | }
560 | }
561 |
562 | static void DrawGrid(int pad_x, int pad_y)
563 | {
564 | SDL_SetRenderDrawColor(renderer, BG2);
565 | for (int x = 0; x <= GRID_ROW; x += SCALE)
566 | SDL_RenderDrawLine(renderer, pad_x + x * CELL_SIZE, pad_y, pad_x + x * CELL_SIZE, pad_y + GRID_COL * CELL_SIZE);
567 | for (int y = 0; y <= GRID_COL; y += SCALE)
568 | SDL_RenderDrawLine(renderer, pad_x + GRID_ROW * CELL_SIZE, pad_y + y * CELL_SIZE, pad_x, pad_y + y * CELL_SIZE);
569 | }
570 |
571 | void DrawCall(bool menuExpanded, bool drawingWire, int x, int y,
572 | Selection choiceComponent, int pad_x, int pad_y,
573 | bool simulating, char *dropDownAnimationFlag, Pair gridPos,
574 | int grid[GRID_ROW * GRID_COL], bool movingCompo, Pair selected, bool snap, ConfirmationFlags confirmationScreenFlag)
575 | {
576 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
577 | SDL_Rect highlight;
578 | highlight.w = CELL_SIZE - 1;
579 | highlight.h = CELL_SIZE - 1;
580 | SDL_SetRenderDrawColor(renderer, BG);
581 | SDL_RenderClear(renderer);
582 | DrawMenu(menuExpanded, simulating, snap, choiceComponent);
583 | HighlightSelected(choiceComponent.type);
584 | if (*dropDownAnimationFlag > 0 && *dropDownAnimationFlag < 6)
585 | AnimateDropDown(dropDownAnimationFlag, menuExpanded, simulating, choiceComponent, snap);
586 |
587 | DrawGrid(pad_x, pad_y);
588 | DrawComponents(pad_x, pad_y);
589 | for (int i = 0; i < componentCount; i++)
590 | DrawWires(ComponentList[i], pad_x, pad_y, simulating);
591 |
592 | if (selected.x >= 0 && selected.y >= 0 && !movingCompo)
593 | {
594 | Component selectedCompo = ComponentList[grid[selected.y * GRID_ROW + selected.x]];
595 | SDL_Rect selectedRect = {.x = selectedCompo.start.x * CELL_SIZE + pad_x + 1, .y = selectedCompo.start.y * CELL_SIZE + pad_y + 1, .w = selectedCompo.width * CELL_SIZE - 1, .h = selectedCompo.size * CELL_SIZE - 1};
596 | SDL_SetRenderDrawColor(renderer, GREEN, 255);
597 | SDL_RenderDrawRect(renderer, &selectedRect);
598 | }
599 |
600 | if (confirmationScreenFlag != none)
601 | DrawConfirmationScreen(confirmationScreenFlag);
602 | HoverOver(MouseIsOver(x, y, menuExpanded, choiceComponent, confirmationScreenFlag == fileMenuFlag), menuExpanded, confirmationScreenFlag);
603 |
604 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
605 | if (drawingWire && !confirmationScreenFlag)
606 | {
607 | SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
608 | DrawWire(tmpWire[0], tmpWire[1], false, false);
609 | }
610 |
611 | if (gridPos.x >= 0 && gridPos.x < GRID_ROW && gridPos.y >= 0 &&
612 | gridPos.y < GRID_COL && !movingCompo && !confirmationScreenFlag)
613 | {
614 | if (grid[gridPos.y * GRID_ROW + gridPos.x] < 0 && !drawingWire)
615 | {
616 | int w, h;
617 | GetWidthHeight(&w, &h, choiceComponent.type, choiceComponent.size);
618 | if (!simulating && PositionIsValid(grid, w, h, choiceComponent.pos))
619 | DrawComponent(w, h, choiceComponent.pos, choiceComponent.type, pad_x, pad_y, 150, false);
620 | else
621 | {
622 | highlight.x = gridPos.x * CELL_SIZE + pad_x;
623 | highlight.y = gridPos.y * CELL_SIZE + pad_y;
624 | SDL_SetRenderDrawColor(renderer, BLUE, 200);
625 | SDL_RenderFillRect(renderer, &highlight);
626 | }
627 | }
628 | else
629 | {
630 | bool done = false;
631 | Component toHighlight =
632 | ComponentList[grid[gridPos.y * GRID_ROW + gridPos.x]];
633 | SDL_SetRenderDrawColor(renderer, GREEN, 200);
634 | highlight.w = TERMINAL_SIZE;
635 | highlight.h = TERMINAL_SIZE;
636 | for (int i = 0; i < toHighlight.inum; i++)
637 | {
638 | highlight.x = toHighlight.inpPos[i].x * CELL_SIZE + pad_x;
639 | highlight.y = toHighlight.start.y * CELL_SIZE + pad_y + (i + 1) * CELL_SIZE * toHighlight.size / toHighlight.inum - CELL_SIZE * toHighlight.size / toHighlight.inum / 2 - TERMINAL_SIZE / 2;
640 | if (x >= highlight.x && x <= highlight.x + TERMINAL_SIZE &&
641 | y >= highlight.y && y <= highlight.y + TERMINAL_SIZE)
642 | {
643 | SDL_RenderFillRect(renderer, &highlight);
644 | done = true;
645 | break;
646 | }
647 | }
648 | for (int i = 0; i < toHighlight.onum && !done; i++)
649 | {
650 | highlight.x = toHighlight.outPos[i].x * CELL_SIZE + pad_x + CELL_SIZE - TERMINAL_SIZE + 1;
651 | highlight.y = toHighlight.start.y * CELL_SIZE + pad_y + (i + 1) * CELL_SIZE * toHighlight.size / toHighlight.onum - CELL_SIZE * toHighlight.size / toHighlight.onum / 2 - TERMINAL_SIZE / 2;
652 | if (x >= highlight.x && x <= highlight.x + TERMINAL_SIZE &&
653 | y >= highlight.y && y <= highlight.y + TERMINAL_SIZE)
654 | {
655 | SDL_RenderFillRect(renderer, &highlight);
656 | done = true;
657 | break;
658 | }
659 | }
660 | if (!done && !drawingWire)
661 | {
662 | SDL_SetRenderDrawColor(renderer, BLUE, 100);
663 | highlight.w = toHighlight.width * CELL_SIZE - 1;
664 | highlight.h = toHighlight.size * CELL_SIZE - 1;
665 | highlight.x = toHighlight.start.x * CELL_SIZE + pad_x + 1;
666 | highlight.y = toHighlight.start.y * CELL_SIZE + pad_y + 1;
667 | SDL_RenderFillRect(renderer, &highlight);
668 | }
669 | }
670 | }
671 | SDL_RenderPresent(renderer);
672 | }
673 |
674 | void PadGrid(int *pad_x, int *pad_y)
675 | {
676 | int w_width, w_height;
677 | SDL_GetWindowSize(window, &w_width, &w_height);
678 | if (w_width > MIN_WINDOW_WIDTH)
679 | *pad_x = (MENU_WIDTH + w_width - GRID_WIDTH) / 2;
680 | else
681 | *pad_x = MENU_WIDTH;
682 | if (w_height > MIN_WINDOW_HEIGHT)
683 | *pad_y = (w_height - GRID_HEIGHT) / 2;
684 | else
685 | *pad_y = 0;
686 | }
687 |
--------------------------------------------------------------------------------
/src/draw.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "program.h"
3 |
4 | typedef enum
5 | {
6 | none,
7 | fileMenuFlag,
8 | clearGrid,
9 | q_saveNewFile,
10 | q_saveChanges,
11 | o_saveNewFile,
12 | o_saveChanges,
13 | n_saveNewFile,
14 | n_saveChanges
15 | } ConfirmationFlags;
16 |
17 | bool StartWiring(Pair);
18 | void WireEndPos(int, int);
19 | void PadGrid(int *, int *);
20 | void DrawCall(bool, bool, int, int, Selection, int, int, bool, char *, Pair,
21 | int *, bool, Pair, bool, ConfirmationFlags);
22 | void UnHighlight(Type);
23 |
--------------------------------------------------------------------------------
/src/interaction.c:
--------------------------------------------------------------------------------
1 | #define _CRT_SECURE_NO_DEPRECATE
2 | #include "interaction.h"
3 | #include
4 | #include "SDL2/SDL_syswm.h"
5 |
6 | extern Button SideMenu[sm_total];
7 | extern Button FileMenu[fm_total];
8 | extern Button Components[g_total];
9 | extern Button confirmYes;
10 | extern Button confirmNo;
11 | extern Button confirmCancel;
12 |
13 | extern SDL_Rect InputsCount;
14 | extern SDL_Rect InputsCountText;
15 |
16 | Component ComponentList[256];
17 | unsigned char componentCount;
18 | extern int time;
19 | extern SDL_Window *window;
20 | char currentFile[256];
21 | Actions UndoBuffer[MAX_UNDOS];
22 |
23 | extern bool AlreadyUpdated[256];
24 |
25 | extern bool fileExists;
26 |
27 | Pair MouseIsOver(int cursorX, int cursorY, bool menuExpanded, Selection choice, bool fmenuOpen)
28 | {
29 | for (int i = 0; i < sm_total; i++)
30 | {
31 | if (cursorX > SideMenu[i].buttonRect.x &&
32 | cursorX < SideMenu[i].buttonRect.x + SideMenu[i].buttonRect.w &&
33 | cursorY > SideMenu[i].buttonRect.y &&
34 | cursorY < SideMenu[i].buttonRect.y + SideMenu[i].buttonRect.h)
35 | {
36 | if (i != sm_inc && i != sm_dec)
37 | return (Pair){sm, i};
38 | else if ((i == sm_inc || i == sm_dec) && choice.type >= g_and)
39 | return (Pair){sm, i};
40 | return (Pair){-1, -1};
41 | }
42 | }
43 | for (int i = 0; i < fm_total && fmenuOpen; i++)
44 | {
45 | if (cursorX > FileMenu[i].buttonRect.x &&
46 | cursorX < FileMenu[i].buttonRect.x + FileMenu[i].buttonRect.w &&
47 | cursorY > FileMenu[i].buttonRect.y &&
48 | cursorY < FileMenu[i].buttonRect.y + FileMenu[i].buttonRect.h)
49 | return (Pair){fm, i};
50 | }
51 | if (cursorX > confirmYes.buttonRect.x &&
52 | cursorX < confirmYes.buttonRect.x + confirmYes.buttonRect.w &&
53 | cursorY > confirmYes.buttonRect.y &&
54 | cursorY < confirmYes.buttonRect.y + confirmYes.buttonRect.h && !fmenuOpen)
55 | return (Pair){con, 1};
56 | else if (cursorX > confirmNo.buttonRect.x &&
57 | cursorX < confirmNo.buttonRect.x + confirmNo.buttonRect.w &&
58 | cursorY > confirmNo.buttonRect.y &&
59 | cursorY < confirmNo.buttonRect.y + confirmNo.buttonRect.h && !fmenuOpen)
60 | return (Pair){con, 0};
61 | else if (cursorX > confirmCancel.buttonRect.x &&
62 | cursorX < confirmCancel.buttonRect.x + confirmCancel.buttonRect.w &&
63 | cursorY > confirmCancel.buttonRect.y &&
64 | cursorY < confirmCancel.buttonRect.y + confirmCancel.buttonRect.h && !fmenuOpen)
65 | return (Pair){con, -1};
66 | for (int i = 0; i < g_total && menuExpanded; i++)
67 | {
68 | if (cursorX > Components[i].buttonRect.x &&
69 | cursorX < Components[i].buttonRect.x + Components[i].buttonRect.w &&
70 | cursorY > Components[i].buttonRect.y &&
71 | cursorY < Components[i].buttonRect.y + Components[i].buttonRect.h)
72 | {
73 | return (Pair){cm, i};
74 | }
75 | }
76 | return (Pair){-1, -1};
77 | }
78 |
79 | void ToggleSimulation(bool *running, unsigned char *updateOrder)
80 | {
81 | if (*running)
82 | {
83 | *running = false;
84 | SDL_Color green = {GREEN};
85 | SideMenu[sm_run].color = green;
86 | for (int i = 0; i < componentCount; i++)
87 | {
88 | if (ComponentList[i].type == state)
89 | continue;
90 | for (int j = 0; j < ComponentList[i].onum; j++)
91 | ComponentList[i].outputs[j] = false;
92 | }
93 | time = 0;
94 | }
95 | else
96 | {
97 | *running = true;
98 | for (int i = 0; i < 256; i++)
99 | updateOrder[i] = i;
100 | for (int i = 0; i < componentCount; i++)
101 | {
102 | for (int j = i; j < componentCount; j++)
103 | {
104 | if (ComponentList[i].childCount > ComponentList[j].childCount)
105 | {
106 | unsigned char tmp = updateOrder[i];
107 | updateOrder[i] = updateOrder[j];
108 | updateOrder[j] = tmp;
109 | }
110 | }
111 | }
112 | SDL_Color red = {RED};
113 | SideMenu[sm_run].color = red;
114 | }
115 | }
116 |
117 | void ToggleDropDown(bool *state, char *animationFlag)
118 | {
119 | if (*state)
120 | {
121 | *state = false;
122 | *animationFlag = 5;
123 | }
124 | else
125 | {
126 | *state = true;
127 | *animationFlag = 1;
128 | }
129 | }
130 |
131 | void NewProject(int *grid, bool *updated)
132 | {
133 | componentCount = 0;
134 | InitGrid(grid);
135 | SDL_SetWindowTitle(window, "MinimaLogic");
136 | *updated = false;
137 | fileExists = false;
138 | }
139 |
140 | void ReadFromFile(int *grid, char *fileName)
141 | {
142 | FILE *data = fopen(fileName, "rb");
143 |
144 | fread(&componentCount, sizeof(unsigned char), 1, data);
145 | fread(ComponentList, sizeof(Component), componentCount, data);
146 | fread(grid, sizeof(int), GRID_COL * GRID_ROW, data);
147 |
148 | for (int i = 0; i < componentCount; i++)
149 | {
150 | for (int j = 0; j < ComponentList[i].inum; j++)
151 | {
152 | ComponentList[i].inputs[j] = &ComponentList[ComponentList[i].inpSrc[j].x];
153 | }
154 | }
155 |
156 | fclose(data);
157 | }
158 |
159 | void SaveToFile(int *grid, char *fileName)
160 | {
161 | FILE *data = fopen(fileName, "wb");
162 | fwrite(&componentCount, sizeof(unsigned char), 1, data);
163 | for (int i = 0; i < componentCount; i++)
164 | {
165 | fwrite(&ComponentList[i], sizeof(Component), 1, data);
166 | }
167 | for (int i = 0; i < GRID_ROW * GRID_COL; i++)
168 | {
169 | fwrite(&grid[i], sizeof(int), 1, data);
170 | }
171 | fclose(data);
172 | }
173 |
174 | void UpdateWindowTitle(char *FileName){
175 | char size = 0;
176 | char count = 0;
177 | char name[50] = "";
178 | while (FileName[size] != '\0')
179 | {
180 | if (FileName[size] == '\\')
181 | count = 0;
182 | name[count] = FileName[size + 1];
183 | count++;
184 | size++;
185 | }
186 |
187 | char title[70] = "MinimaLogic";
188 | SDL_strlcat(title, "-", 70);
189 | SDL_strlcat(title, name, 70);
190 | SDL_SetWindowTitle(window, title);
191 |
192 | }
193 |
194 | void ChooseFile(int *grid, bool saving)
195 | {
196 | char FileName[256] = "";
197 |
198 | SDL_SysWMinfo wmInfo;
199 | SDL_VERSION(&wmInfo.version);
200 | SDL_GetWindowWMInfo(window, &wmInfo);
201 |
202 | OPENFILENAME ofn;
203 | memset(&ofn, 0, sizeof(ofn));
204 | ofn.lStructSize = sizeof(ofn);
205 | ofn.hwndOwner = wmInfo.info.win.window;
206 | ofn.hInstance = NULL;
207 | ofn.lpstrFilter = "Project Files (*.mlg)\0*.mlg";
208 | ofn.lpstrFile = FileName;
209 | ofn.nMaxFile = MAX_PATH;
210 | ofn.lpstrTitle = saving ? "Save File" : "Open File";
211 | ofn.lpstrDefExt = "mlg";
212 | ofn.Flags = OFN_NONETWORKBUTTON |
213 | OFN_FILEMUSTEXIST |
214 | OFN_HIDEREADONLY;
215 | if (!saving)
216 | {
217 | if (!GetOpenFileName(&ofn))
218 | {
219 | return;
220 | }
221 | else
222 | {
223 | ReadFromFile(grid, FileName);
224 | fileExists = true;
225 | SDL_strlcpy(currentFile, FileName, 256);
226 | }
227 | }
228 | else
229 | {
230 | if (!GetSaveFileName(&ofn))
231 | {
232 | return;
233 | }
234 | else
235 | {
236 | SaveToFile(grid, FileName);
237 | fileExists = true;
238 | SDL_strlcpy(currentFile, FileName, 256);
239 | }
240 | }
241 | UpdateWindowTitle(FileName);
242 | }
243 |
244 | bool PositionIsValid(int *grid, int w, int h, Pair pos)
245 | {
246 | if (pos.x + w > GRID_ROW || pos.y + h > GRID_COL)
247 | return false;
248 | for (int y = pos.y; y < pos.y + h; y++)
249 | {
250 | for (int x = pos.x; x < pos.x + w; x++)
251 | {
252 | if (cell(y, x) != -1)
253 | return false;
254 | }
255 | }
256 | return true;
257 | }
258 |
259 | char WireIsValid(int *grid, Pair gridPos, int x, int y, int pad_x, int pad_y)
260 | {
261 | if (grid[gridPos.y * GRID_ROW + gridPos.x] < 0)
262 | {
263 | return 0;
264 | }
265 | int index = grid[gridPos.y * GRID_ROW + gridPos.x];
266 | Component component = ComponentList[index];
267 | Pair pin;
268 | for (int i = 0; i < component.inum; i++)
269 | {
270 | pin.x = component.inpPos[i].x * CELL_SIZE + pad_x;
271 | pin.y = component.start.y * CELL_SIZE + pad_y + (i + 1) * CELL_SIZE * component.size / component.inum - CELL_SIZE * component.size / component.inum / 2 - TERMINAL_SIZE / 2;
272 | if (x >= pin.x && x <= pin.x + TERMINAL_SIZE && y >= pin.y &&
273 | y <= pin.y + TERMINAL_SIZE)
274 | return i + 1;
275 | }
276 | for (int i = 0; i < component.onum; i++)
277 | {
278 | pin.x = component.outPos[i].x * CELL_SIZE + pad_x + CELL_SIZE - TERMINAL_SIZE + 1;
279 | pin.y = component.start.y * CELL_SIZE + pad_y + (i + 1) * CELL_SIZE * component.size / component.onum - CELL_SIZE * component.size / component.onum / 2 - TERMINAL_SIZE / 2;
280 | if (x >= pin.x && x <= pin.x + TERMINAL_SIZE && y >= pin.y &&
281 | y <= pin.y + TERMINAL_SIZE)
282 | return -(i + 1);
283 | }
284 | return 0;
285 | }
286 |
287 | void InsertComponent(int *grid, Selection choice, int width, int height)
288 | {
289 | ComponentList[componentCount] =
290 | GetComponent(choice.type, choice.size, choice.pos);
291 | for (int y = choice.pos.y; y < choice.pos.y + height; y++)
292 | {
293 | for (int x = choice.pos.x; x < choice.pos.x + width; x++)
294 | {
295 | cell(y, x) = componentCount;
296 | }
297 | }
298 | componentCount++;
299 | }
300 |
301 | void DeleteComponent(int *grid, Pair gridPos)
302 | {
303 | if (cell(gridPos.y, gridPos.x) == -1 || gridPos.x < 0 || gridPos.y < 0)
304 | return;
305 | int toDelete = cell(gridPos.y, gridPos.x);
306 |
307 | for (int i = 0; i < GRID_COL; i++)
308 | {
309 | for (int j = 0; j < GRID_ROW; j++)
310 | {
311 | if (cell(i, j) == toDelete)
312 | cell(i, j) = -1;
313 | else if (cell(i, j) > toDelete)
314 | cell(i, j)--;
315 | }
316 | }
317 |
318 | for (int i = 0; i < componentCount; i++)
319 | {
320 | Component *compo = &ComponentList[i];
321 | for (int j = 0; j < compo->inum; j++)
322 | {
323 | if (compo->inpSrc[j].x == toDelete)
324 | {
325 | compo->inpSrc[j] = (Pair){-1, -1};
326 | compo->inputs[j] = NULL;
327 | }
328 | else if (compo->inpSrc[j].x > toDelete)
329 | {
330 | compo->inpSrc[j].x--;
331 | compo->inputs[j] = &ComponentList[compo->inpSrc[j].x];
332 | }
333 | }
334 | }
335 | for (int i = toDelete; i < componentCount - 1; i++)
336 | {
337 | ComponentList[i] = ComponentList[i + 1];
338 | }
339 | componentCount--;
340 | }
341 |
342 | void UpdateChildCount(int index, bool inc)
343 | {
344 | if (inc)
345 | ComponentList[index].childCount++;
346 | else
347 | ComponentList[index].childCount--;
348 | AlreadyUpdated[index] = true;
349 | Component compo = ComponentList[index];
350 | for (int i = 0; i < compo.inum; i++)
351 | {
352 | if (compo.inpSrc[i].x >= 0)
353 | {
354 | if (!AlreadyUpdated[compo.inpSrc[i].x])
355 | UpdateChildCount(compo.inpSrc[i].x, inc);
356 | }
357 | }
358 | }
359 |
360 | void ChangeNumofInputs(bool dec, Selection *choice)
361 | {
362 | if (dec)
363 | {
364 | if (choice->size > MIN_INPUT_NUM)
365 | choice->size--;
366 | if (Components[choice->type].selection.size > MIN_INPUT_NUM)
367 | Components[choice->type].selection.size--;
368 | }
369 | else
370 | {
371 | if (choice->size < MAX_INPUTS)
372 | choice->size++;
373 | if (Components[choice->type].selection.size < MAX_INPUTS)
374 | Components[choice->type].selection.size++;
375 | }
376 | }
377 |
378 | static void ResetUndoBuffer(int *currentUndoLevel, int *totalUndoLevel)
379 | {
380 | for (int i = *currentUndoLevel; i < *totalUndoLevel; i++)
381 | UndoBuffer[i - *currentUndoLevel] = UndoBuffer[i];
382 | *totalUndoLevel -= *currentUndoLevel;
383 | *currentUndoLevel = 0;
384 | for (int i = *totalUndoLevel; i < MAX_UNDOS; i++)
385 | UndoBuffer[i].act = '\0';
386 | }
387 |
388 | void ShiftUndoBuffer(int *currentUndoLevel, int *totalUndoLevel)
389 | {
390 | if (*totalUndoLevel < MAX_UNDOS - 1)
391 | *totalUndoLevel += 1;
392 | if (*currentUndoLevel > 0)
393 | ResetUndoBuffer(currentUndoLevel, totalUndoLevel);
394 | for (int i = *totalUndoLevel; i > 0; i--)
395 | UndoBuffer[i] = UndoBuffer[i - 1];
396 | }
397 |
398 | void ClearUndoBuffer(int *currentUndoLevel, int *totalUndoLevel)
399 | {
400 | for (int i = 0; i < MAX_UNDOS; i++)
401 | UndoBuffer[i].act = '\0';
402 | *totalUndoLevel = 0;
403 | *currentUndoLevel = 0;
404 | }
405 |
406 | static void UndoDeletion(Delete deleted, int *grid)
407 | {
408 | int toDelete = deleted.index;
409 |
410 | for (int i = 0; i < GRID_COL; i++)
411 | {
412 | for (int j = 0; j < GRID_ROW; j++)
413 | {
414 | if (cell(i, j) >= deleted.index)
415 | cell(i, j)++;
416 | }
417 | }
418 | componentCount++;
419 |
420 | for (int i = 0; i < componentCount; i++)
421 | {
422 | Component *compo = &ComponentList[i];
423 | for (int j = 0; j < compo->inum; j++)
424 | {
425 | if (compo->inpSrc[j].x >= toDelete)
426 | {
427 | compo->inpSrc[j].x++;
428 | compo->inputs[j] = &ComponentList[compo->inpSrc[j].x];
429 | }
430 | }
431 | }
432 | for (int i = componentCount; i > deleted.index; i--)
433 | {
434 | ComponentList[i] = ComponentList[i - 1];
435 | }
436 |
437 | for (int i = 0; i < deleted.conNo; i++)
438 | {
439 | ComponentList[deleted.connections[i].receiver].inpSrc[deleted.connections[i].receiveIndex] = (Pair){deleted.index, deleted.connections[i].sendIndex};
440 | ComponentList[deleted.connections[i].receiver].inputs[deleted.connections[i].receiveIndex] = &ComponentList[deleted.index];
441 | }
442 | ComponentList[deleted.index] = deleted.deletedCompo;
443 | for (int i = deleted.deletedCompo.start.x; i < deleted.deletedCompo.start.x + deleted.deletedCompo.width; i++)
444 | {
445 | for (int j = deleted.deletedCompo.start.y; j < deleted.deletedCompo.start.y + deleted.deletedCompo.size; j++)
446 | {
447 | cell(j, i) = deleted.index;
448 | }
449 | }
450 | }
451 |
452 | static void UndoWiring(Wiring wired)
453 | {
454 | for (int i = 0; i < 256; i++)
455 | AlreadyUpdated[i] = false;
456 | UpdateChildCount(wired.sender, false);
457 | ComponentList[wired.connection.receiver]
458 | .inpSrc[wired.connection.receiveIndex] = (Pair){-1, -1};
459 | ComponentList[wired.connection.receiver].inputs[wired.connection.receiveIndex] = NULL;
460 | }
461 |
462 | static void UndoPlacing(Place placed, int *grid)
463 | {
464 | DeleteComponent(grid, placed.component.start);
465 | }
466 |
467 | static void UndoMoving(Move moved, int *grid)
468 | {
469 | Component compo = ComponentList[moved.index];
470 | for (int i = moved.after.x; i < moved.after.x + compo.width; i++)
471 | for (int j = moved.after.y; j < moved.after.y + compo.size; j++)
472 | cell(j, i) = -1;
473 | for (int i = moved.before.x; i < moved.before.x + compo.width; i++)
474 | for (int j = moved.before.y; j < moved.before.y + compo.size; j++)
475 | cell(j, i) = moved.index;
476 | ComponentList[moved.index].start = moved.before;
477 | SetIOPos(&ComponentList[moved.index]);
478 | }
479 |
480 | void Undo(int *grid, int *currentUndoLevel, int totalUndoLevel)
481 | {
482 | if (*currentUndoLevel >= totalUndoLevel)
483 | return;
484 | Actions toUndo = UndoBuffer[*currentUndoLevel];
485 | switch (toUndo.act)
486 | {
487 | case 'd':
488 | UndoDeletion(toUndo.Action.deleted, grid);
489 | break;
490 | case 'w':
491 | UndoWiring(toUndo.Action.wired);
492 | break;
493 | case 'p':
494 | UndoPlacing(toUndo.Action.placed, grid);
495 | break;
496 | case 'm':
497 | UndoMoving(toUndo.Action.moved, grid);
498 | break;
499 | default:
500 | break;
501 | }
502 | *currentUndoLevel += 1;
503 | }
504 |
505 | static void RedoDeletion(Delete deleted, int *grid)
506 | {
507 | DeleteComponent(grid, deleted.deletedCompo.start);
508 | }
509 |
510 | static void RedoWiring(Wiring wired)
511 | {
512 | for (int i = 0; i < 256; i++)
513 | AlreadyUpdated[i] = false;
514 | UpdateChildCount(wired.sender, true);
515 | ComponentList[wired.connection.receiver].inpSrc[wired.connection.receiveIndex] = (Pair){wired.sender, wired.connection.sendIndex};
516 | ComponentList[wired.connection.receiver].inputs[wired.connection.receiveIndex] = &ComponentList[wired.sender];
517 | }
518 |
519 | static void RedoPlacing(Place placed, int *grid)
520 | {
521 | Selection placing = {.type = placed.component.type, .size = placed.component.inum, .pos = placed.component.start};
522 | InsertComponent(grid, placing, placed.component.width, placed.component.size);
523 | }
524 |
525 | static void RedoMoving(Move moved, int *grid)
526 | {
527 | Component compo = ComponentList[moved.index];
528 | for (int i = moved.before.x; i < moved.before.x + compo.width; i++)
529 | for (int j = moved.before.y; j < moved.before.y + compo.size; j++)
530 | cell(j, i) = -1;
531 | for (int i = moved.after.x; i < moved.after.x + compo.width; i++)
532 | for (int j = moved.after.y; j < moved.after.y + compo.size; j++)
533 | cell(j, i) = moved.index;
534 | ComponentList[moved.index].start = moved.after;
535 | SetIOPos(&ComponentList[moved.index]);
536 | }
537 |
538 | void Redo(int *grid, int *currentUndoLevel, int totalUndoLevel)
539 | {
540 | *currentUndoLevel -= 1;
541 | Actions toRedo = UndoBuffer[*currentUndoLevel];
542 | switch (toRedo.act)
543 | {
544 | case 'd':
545 | RedoDeletion(toRedo.Action.deleted, grid);
546 | break;
547 | case 'w':
548 | RedoWiring(toRedo.Action.wired);
549 | break;
550 | case 'p':
551 | RedoPlacing(toRedo.Action.placed, grid);
552 | break;
553 | case 'm':
554 | RedoMoving(toRedo.Action.moved, grid);
555 | break;
556 | default:
557 | break;
558 | }
559 | }
560 |
--------------------------------------------------------------------------------
/src/interaction.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "program.h"
3 |
4 | typedef struct
5 | {
6 | unsigned char sendIndex, receiver, receiveIndex;
7 | } Connection;
8 |
9 | typedef struct
10 | {
11 | unsigned char index, conNo;
12 | Component deletedCompo;
13 | Connection connections[255];
14 | } Delete;
15 |
16 | typedef struct
17 | {
18 | unsigned char sender;
19 | Connection connection;
20 | } Wiring;
21 | typedef struct
22 | {
23 | Component component;
24 | } Place;
25 |
26 | typedef struct
27 | {
28 | unsigned char index;
29 | Pair before, after;
30 | } Move;
31 |
32 | typedef struct
33 | {
34 | char act;
35 | union
36 | {
37 | Delete deleted;
38 | Wiring wired;
39 | Place placed;
40 | Move moved;
41 | } Action;
42 | } Actions;
43 |
44 | void ToggleSimulation(bool *, unsigned char *);
45 | void ToggleDropDown(bool *, char *);
46 |
47 | void DeleteComponent(int *, Pair);
48 | void InsertComponent(int *, Selection, int, int);
49 | void UpdateWindowTitle(char *);
50 |
51 | char WireIsValid(int *, Pair, int, int, int, int);
52 | void ChangeNumofInputs(bool, Selection *);
53 |
54 | void ChooseFile(int *, bool saving);
55 | void SaveToFile(int *, char *);
56 | void ReadFromFile(int *, char *);
57 | void NewProject(int *grid, bool *updated);
58 | void Undo(int *grid, int *currentUndoLevel, int totalUndoLevel);
59 | void Redo(int *grid, int *currentUndoLevel, int totalUndoLevel);
60 | void ShiftUndoBuffer(int *currentUndoLevel, int *totalUndoLevel);
61 | void ClearUndoBuffer(int *currentUndoLevel, int *totalUndoLevel);
62 | void UpdateChildCount(int, bool);
63 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | #include "program.h"
2 |
3 | int main(int argc, char **argv)
4 | {
5 | int grid[GRID_ROW * GRID_COL];
6 | //Initialize the window, char maps, grid, font, UI etc
7 | InitEverything(grid, argc, argv);
8 | //The main program loop. User input, drawing, output, simulation everything happens here
9 | MainProgramLoop(grid);
10 | //Destroying textures, window, renderer and closing fonts happens here
11 | CloseEverything();
12 | return 0;
13 | }
14 |
--------------------------------------------------------------------------------
/src/program.c:
--------------------------------------------------------------------------------
1 | #include "program.h"
2 | #include "interaction.h"
3 | #include "draw.h"
4 | #include
5 |
6 | // SDL window and renderer
7 | SDL_Window *window = NULL;
8 | SDL_Renderer *renderer = NULL;
9 | // Buttons
10 | Button confirmYes = {.color = {GREEN, 255}};
11 | Button confirmNo = {.color = {RED, 255}};
12 | Button confirmCancel = {.color = {BLUE, 255}};
13 | Button Components[g_total];
14 | Button SideMenu[sm_total];
15 | Button FileMenu[fm_total];
16 | //Fonts and Character Maps
17 | TTF_Font *font = NULL; //Font used in UI
18 | TTF_Font *displayFont = NULL; //Font used in decoders
19 | SDL_Texture *characters[127 - 32]; //Character map for UI
20 | int characterWidth[127 - 32];
21 | SDL_Texture *displayChars[16]; //Character Map for decoders
22 | //Displays no. of inputs for multi-input gates
23 | SDL_Rect InputsCount;
24 | SDL_Rect InputsCountText;
25 | //Array to keep track of changes for undo/redo
26 | extern Actions UndoBuffer[MAX_UNDOS];
27 | //Checks if user is working in a new file or existing file
28 | bool fileExists = false;
29 | //List of components on the grid
30 | extern Component ComponentList[256];
31 | //Total number of components on the grid
32 | extern unsigned char componentCount;
33 | //Array to keep track of components that have already been updated. Prevents stackoverflow while simulating
34 | bool AlreadyUpdated[256];
35 | //To update clocks
36 | int time = 0;
37 | extern char currentFile[256];
38 |
39 | static void InitFont()
40 | {
41 | TTF_Init();
42 | font = TTF_OpenFont("ui_font.ttf", 25);
43 | displayFont = TTF_OpenFont("led_font.otf", 100);
44 | if (font == NULL || displayFont == NULL)
45 | {
46 | SDL_Log("Failed to load the font: %s\n", TTF_GetError());
47 | exit(-3);
48 | }
49 | }
50 |
51 | void InitMenu(int windowWidth, int windowHeight, bool simulating)
52 | {
53 | for (int i = 0; i < sm_total; i++)
54 | {
55 | SideMenu[i].buttonRect.w = MENU_WIDTH - 20;
56 | SideMenu[i].color = (SDL_Color){BLACK};
57 | SideMenu[i].buttonRect.h = MENU_FONT_SIZE;
58 | SideMenu[i].buttonRect.x = 10;
59 | SideMenu[i].buttonRect.y = windowHeight -
60 | (sm_total - i) * (10 + SideMenu[i].buttonRect.h);
61 | }
62 | for (int i = 0; i < fm_total; i++)
63 | {
64 | FileMenu[i].buttonRect.w = MENU_WIDTH - 20;
65 | FileMenu[i].buttonRect.h = MENU_FONT_SIZE;
66 | FileMenu[i].buttonRect.x = windowWidth / 2 - FileMenu[i].buttonRect.w / 2;
67 | FileMenu[i].buttonRect.y = windowHeight / 2 +
68 | FileMenu[i].buttonRect.h / 2 +
69 | (i - fm_total / 2) * (FileMenu[i].buttonRect.h + 10);
70 | }
71 | if (simulating)
72 | SideMenu[sm_run].color = (SDL_Color){RED};
73 | else
74 | SideMenu[sm_run].color = (SDL_Color){GREEN};
75 | SideMenu[sm_run].buttonRect.y = 10;
76 | SideMenu[sm_compo].buttonRect.y = SideMenu[sm_run].buttonRect.y + SideMenu[sm_compo].buttonRect.h + 10;
77 | SideMenu[sm_dec].color = (SDL_Color){RED};
78 | SideMenu[sm_dec].buttonRect.w = 0.15 * MENU_WIDTH - 10;
79 | SideMenu[sm_dec].buttonRect.x = 10;
80 |
81 | InputsCount.x = SideMenu[sm_dec].buttonRect.x + SideMenu[sm_dec].buttonRect.w + 5;
82 | InputsCount.y = SideMenu[sm_dec].buttonRect.y;
83 | InputsCount.w = 0.7 * MENU_WIDTH - 10;
84 | InputsCount.h = MENU_FONT_SIZE;
85 |
86 | SideMenu[sm_inc].color = (SDL_Color){RED};
87 | SideMenu[sm_inc].buttonRect.w = 0.15 * MENU_WIDTH - 10;
88 | SideMenu[sm_inc].buttonRect.x = InputsCount.x + InputsCount.w + 5;
89 | SideMenu[sm_inc].buttonRect.y = SideMenu[sm_dec].buttonRect.y;
90 |
91 | confirmYes.buttonRect.w = 150;
92 | confirmYes.buttonRect.h = MENU_FONT_SIZE;
93 | confirmYes.buttonRect.x = windowWidth / 2 - 200 + 25;
94 | confirmYes.buttonRect.y = windowHeight / 2 - 100 + 200 - confirmYes.buttonRect.h - 15 - MENU_FONT_SIZE;
95 |
96 | confirmNo.buttonRect.w = 150;
97 | confirmNo.buttonRect.h = MENU_FONT_SIZE;
98 | confirmNo.buttonRect.x = windowWidth / 2 - 200 + 400 - 25 - confirmNo.buttonRect.w;
99 | confirmNo.buttonRect.y = windowHeight / 2 - 100 + 200 - confirmNo.buttonRect.h - 15 - MENU_FONT_SIZE;
100 |
101 | confirmCancel.buttonRect.w = 150;
102 | confirmCancel.buttonRect.h = MENU_FONT_SIZE;
103 | confirmCancel.buttonRect.x = windowWidth / 2 - confirmCancel.buttonRect.w / 2;
104 | confirmCancel.buttonRect.y = windowHeight / 2 - 100 + 200 - confirmCancel.buttonRect.h - 10;
105 |
106 | for (int i = 0; i < g_total; i++)
107 | {
108 | Components[i].selection.type = i;
109 | Components[i].selection.size = 2;
110 | Components[i].buttonRect.x = 20;
111 | Components[i].buttonRect.y = SideMenu[sm_compo].buttonRect.y +
112 | SideMenu[sm_compo].buttonRect.h +
113 | i * (CELL_SIZE * SCALE + 2) + 10;
114 | Components[i].buttonRect.w = MENU_WIDTH - 40;
115 | Components[i].buttonRect.h = MENU_FONT_SIZE - 10;
116 | }
117 | }
118 |
119 | void CharacterMap()
120 | {
121 | char *nums[16] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
122 | SDL_Surface *characterSurface;
123 | SDL_Color white = {WHITE, 200};
124 | SDL_Color black = {BLACK, 255};
125 |
126 | for (int i = 32; i < 127; i++)
127 | {
128 | characterSurface = TTF_RenderText_Blended(font, (char *)&i, white);
129 | characters[i - 32] = SDL_CreateTextureFromSurface(renderer, characterSurface);
130 | characterWidth[i - 32] = characterSurface ? characterSurface->w : 0;
131 | }
132 | for (int i = 0; i < 16; i++)
133 | {
134 | characterSurface = TTF_RenderText_Blended(displayFont, nums[i], black);
135 | displayChars[i] = SDL_CreateTextureFromSurface(renderer, characterSurface);
136 | }
137 | SDL_FreeSurface(characterSurface);
138 | }
139 |
140 | void InitGrid(int grid[GRID_ROW * GRID_COL])
141 | {
142 | for (int i = 0; i < GRID_COL * GRID_ROW; i++)
143 | grid[i] = -1;
144 | }
145 |
146 | void InitEverything(int grid[GRID_ROW * GRID_COL], int argc, char **argv)
147 | {
148 | if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
149 | exit(-1);
150 | window =
151 | SDL_CreateWindow("MinimaLogic", 0, 0, WINDOW_WIDTH,
152 | WINDOW_HEIGHT, SDL_WINDOW_MAXIMIZED | SDL_WINDOW_RESIZABLE);
153 | renderer = SDL_CreateRenderer(
154 | window, -1, SDL_RENDERER_SOFTWARE);
155 | if (!(window && renderer))
156 | exit(-2);
157 |
158 | SDL_SetWindowMinimumSize(window, MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
159 |
160 | InitGrid(grid);
161 | if (argc > 1){
162 | ReadFromFile(grid, argv[1]);
163 | fileExists = true;
164 | SDL_strlcpy(currentFile, argv[1], 256);
165 | UpdateWindowTitle(argv[1]);
166 | }
167 |
168 | int w, h;
169 | SDL_GetWindowSize(window, &w, &h);
170 | InitMenu(w, h, false);
171 | //Change directory to location of the executable so that the program can find fonts
172 | char *path = argv[0], i;
173 | for (i = SDL_strlen(path) - 1; path[i] != '\\'; i--);
174 | path[i] = '\0';
175 | _chdir(path);
176 | InitFont();
177 | CharacterMap();
178 | TTF_CloseFont(font);
179 | TTF_CloseFont(displayFont);
180 | TTF_Quit();
181 | }
182 |
183 | static void DestroyTextures()
184 | {
185 | for (int i = 32; i < 127; i++)
186 | SDL_DestroyTexture(characters[i - 32]);
187 | for (int i = 0; i < 16; i++)
188 | SDL_DestroyTexture(displayChars[i]);
189 | }
190 |
191 | void CloseEverything()
192 | {
193 | SDL_DestroyRenderer(renderer);
194 | SDL_DestroyWindow(window);
195 | DestroyTextures();
196 | SDL_Quit();
197 | }
198 |
199 | static void UpdateComponents(unsigned char *updateOrder)
200 | {
201 | for (int i = 0; i < componentCount; i++)
202 | {
203 | unsigned char index = updateOrder[i];
204 | if (!AlreadyUpdated[index])
205 | {
206 | AlreadyUpdated[index] = true;
207 | update(&ComponentList[index]);
208 | }
209 | }
210 | }
211 |
212 | static void AddDeletedToUndo(int *currentUndoLevel, int *totalUndoLevel, int index)
213 | {
214 | ShiftUndoBuffer(currentUndoLevel, totalUndoLevel);
215 | UndoBuffer[0].act = 'd';
216 | UndoBuffer[0].Action.deleted.deletedCompo = ComponentList[index];
217 | UndoBuffer[0].Action.deleted.index = index;
218 | UndoBuffer[0].Action.deleted.conNo = 0;
219 | for (int i = 0; i < componentCount; i++)
220 | {
221 | Component *compo = &ComponentList[i];
222 | for (int j = 0; j < compo->inum; j++)
223 | {
224 | if (compo->inpSrc[j].x == index)
225 | {
226 | UndoBuffer[0].Action.deleted.connections[UndoBuffer[0].Action.deleted.conNo].sendIndex = compo->inpSrc[j].y;
227 | UndoBuffer[0].Action.deleted.connections[UndoBuffer[0].Action.deleted.conNo].receiveIndex = j;
228 | UndoBuffer[0].Action.deleted.connections[UndoBuffer[0].Action.deleted.conNo].receiver = i;
229 | UndoBuffer[0].Action.deleted.conNo++;
230 | }
231 | }
232 | }
233 | }
234 |
235 | void CollisionCheck(int * grid, Component * compo){
236 | bool goUp = true, goLeft = true, goDown = true, goRight = true;
237 | if (!PositionIsValid(grid, compo->width, compo->size, compo->start))
238 | {
239 | for (int i = 1; goUp || goLeft || goRight || goDown; i++)
240 | {
241 | if (goLeft)
242 | {
243 | compo->start.x -= i;
244 | if (compo->start.x < 0)
245 | goLeft = false;
246 | if (PositionIsValid(grid, compo->width, compo->size, compo->start))
247 | break;
248 | compo->start.x += i;
249 | }
250 | if (goUp)
251 | {
252 | compo->start.y -= i;
253 | if (compo->start.y < 0)
254 | goUp = false;
255 | if (PositionIsValid(grid, compo->width, compo->size, compo->start))
256 | break;
257 | compo->start.y += i;
258 | }
259 | if (goRight)
260 | {
261 | compo->start.x += i;
262 | if (compo->start.x >= GRID_ROW)
263 | goRight = false;
264 | if (PositionIsValid(grid, compo->width, compo->size, compo->start))
265 | break;
266 | compo->start.x -= i;
267 | }
268 | if (goDown)
269 | {
270 | compo->start.y += i;
271 | if (compo->start.y >= GRID_COL)
272 | goDown = false;
273 | if (PositionIsValid(grid, compo->width, compo->size, compo->start))
274 | break;
275 | compo->start.y -= i;
276 | }
277 | }
278 | }
279 | }
280 |
281 | void MainProgramLoop(int grid[GRID_ROW * GRID_COL])
282 | {
283 | Selection compoChoice = {.type = 0, .size = 0};
284 | Pair selected = {-1, -1};
285 |
286 | int x, y;
287 | Pair gridPos;
288 | int pad_x, pad_y;
289 | PadGrid(&pad_x, &pad_y);
290 |
291 | int sender, receiver, sendIndex, receiveIndex, compoMoved;
292 | bool simulating = false, menuExpanded = false, drawingWire = false, movingCompo = false, confirmWire = false;
293 | bool snapToGrid = false, snapToggeled = false, cursorInGrid, draw, updated = false, ctrlHeld = false;
294 | char dropDownAnimationFlag = 0, startAt = 0, endAt = 0, animating = 8;
295 | Pair offset, initialPos;
296 | ConfirmationFlags confirmationScreenFlag = none;
297 | unsigned char updateOrder[256];
298 |
299 | int currentUndoLevel = 0, totalUndoLevel = 0;
300 | bool run = true;
301 |
302 | SDL_Event e;
303 | while (run)
304 | {
305 | int begin = SDL_GetTicks();
306 | SDL_GetMouseState(&x, &y);
307 | draw = !simulating;
308 |
309 | if (x - pad_x > 0)
310 | gridPos.x = (x - pad_x) / CELL_SIZE;
311 | else
312 | gridPos.x = -1;
313 | if (y - pad_y > 0)
314 | gridPos.y = (y - pad_y) / CELL_SIZE;
315 | else
316 | gridPos.y = -1;
317 | if (snapToGrid && gridPos.x >= 0 && gridPos.y >= 0)
318 | {
319 | gridPos.x -= gridPos.x % (SCALE / 2);
320 | gridPos.y -= gridPos.y % (SCALE / 2);
321 | }
322 | cursorInGrid = gridPos.x >= 0 && gridPos.x < GRID_ROW && gridPos.y >= 0 &&
323 | gridPos.y < GRID_COL;
324 |
325 | while (SDL_WaitEventTimeout(&e, DELAY / 10))
326 | {
327 | switch (e.type)
328 | {
329 | case SDL_QUIT:
330 | if (fileExists && updated)
331 | confirmationScreenFlag = q_saveChanges;
332 | else if (updated && componentCount > 0)
333 | confirmationScreenFlag = q_saveNewFile;
334 | else
335 | run = false;
336 | case SDL_WINDOWEVENT:
337 | {
338 | int w, h;
339 | SDL_GetWindowSize(window, &w, &h);
340 | InitMenu(w, h, simulating);
341 | PadGrid(&pad_x, &pad_y);
342 | break;
343 | }
344 | case SDL_MOUSEBUTTONDOWN:
345 | if (!confirmationScreenFlag)
346 | {
347 | if (e.button.button == SDL_BUTTON_RIGHT)
348 | {
349 | selected = (Pair){-1, -1};
350 | break;
351 | }
352 | if (cursorInGrid)
353 | {
354 | if (!WireIsValid(grid, gridPos, x, y, pad_x, pad_y) && cell(gridPos.y, gridPos.x) >= 0)
355 | {
356 | if (ComponentList[cell(gridPos.y, gridPos.x)].type == state || (ComponentList[cell(gridPos.y, gridPos.x)].type == clock))
357 | ComponentList[cell(gridPos.y, gridPos.x)].outputs[0] = !ComponentList[cell(gridPos.y, gridPos.x)].outputs[0];
358 | if (!drawingWire && !movingCompo && !simulating)
359 | {
360 | selected = gridPos;
361 | Component compo = ComponentList[cell(gridPos.y, gridPos.x)];
362 | initialPos = compo.start;
363 | offset = (Pair){gridPos.x - initialPos.x, gridPos.y - initialPos.y};
364 | compoMoved = cell(gridPos.y, gridPos.x);
365 | movingCompo = true;
366 | for (int i = initialPos.y; i < initialPos.y + compo.size; i++)
367 | for (int j = initialPos.x; j < initialPos.x + compo.width; j++)
368 | cell(i, j) = -1;
369 | }
370 | }
371 | else
372 | {
373 | selected = (Pair){-1, -1};
374 | }
375 | int w, h;
376 | GetWidthHeight(&w, &h, compoChoice.type, compoChoice.size);
377 | if (componentCount < 255 && !simulating && !drawingWire && PositionIsValid(grid, w, h, compoChoice.pos) && !movingCompo)
378 | {
379 | InsertComponent(grid, compoChoice, w, h);
380 | updated = true;
381 | ShiftUndoBuffer(¤tUndoLevel, &totalUndoLevel);
382 | UndoBuffer[0].act = 'p';
383 | UndoBuffer[0].Action.placed.component = ComponentList[componentCount - 1];
384 | }
385 | else if (!simulating && !drawingWire && !movingCompo)
386 | {
387 | startAt = WireIsValid(grid, gridPos, x, y, pad_x, pad_y);
388 | if (startAt < 0)
389 | {
390 | sender = cell(gridPos.y, gridPos.x);
391 | sendIndex = startAt;
392 | drawingWire = StartWiring((Pair){x, y});
393 | }
394 | else if (startAt > 0)
395 | {
396 | receiver = cell(gridPos.y, gridPos.x);
397 | receiveIndex = startAt;
398 | drawingWire = StartWiring((Pair){x, y});
399 | }
400 | }
401 | }
402 | if (x <= MENU_WIDTH)
403 | {
404 | Pair clickedButton = MouseIsOver(x, y, menuExpanded, compoChoice, confirmationScreenFlag == fileMenuFlag);
405 | if (clickedButton.x == sm)
406 | {
407 | if (clickedButton.y == sm_run)
408 | {
409 | ToggleSimulation(&simulating, updateOrder);
410 | selected = (Pair){-1, -1};
411 | }
412 | else if (!simulating)
413 | {
414 | switch (clickedButton.y)
415 | {
416 | case (sm_clear):
417 | confirmationScreenFlag = clearGrid;
418 | break;
419 | case (sm_compo):
420 | ToggleDropDown(&menuExpanded, &dropDownAnimationFlag);
421 | animating = 0;
422 | break;
423 | case (sm_dec):
424 | ChangeNumofInputs(true, &compoChoice);
425 | break;
426 | case (sm_inc):
427 | ChangeNumofInputs(false, &compoChoice);
428 | break;
429 | case (sm_delete):
430 | AddDeletedToUndo(¤tUndoLevel, &totalUndoLevel, cell(selected.y, selected.x));
431 | DeleteComponent(grid, selected);
432 | selected = (Pair){-1, -1};
433 | updated = true;
434 | break;
435 | case (sm_undo):
436 | if (currentUndoLevel < totalUndoLevel)
437 | Undo(grid, ¤tUndoLevel, totalUndoLevel);
438 | break;
439 | case (sm_redo):
440 | if (currentUndoLevel > 0)
441 | Redo(grid, ¤tUndoLevel, totalUndoLevel);
442 | break;
443 | case (sm_snap):
444 | snapToGrid = !snapToGrid;
445 | snapToggeled = !snapToggeled;
446 | break;
447 | case (sm_fmenu):
448 | confirmationScreenFlag = fileMenuFlag;
449 | break;
450 | default:
451 | break;
452 | }
453 | }
454 | }
455 | else if (clickedButton.x == cm && menuExpanded)
456 | {
457 | UnHighlight(compoChoice.type);
458 | compoChoice = Components[clickedButton.y].selection;
459 | }
460 | }
461 | }
462 | else
463 | {
464 | Pair clickedButton = MouseIsOver(x, y, menuExpanded, compoChoice, confirmationScreenFlag == fileMenuFlag);
465 | char fname[256];
466 | if (confirmationScreenFlag == fileMenuFlag && clickedButton.x == fm)
467 | {
468 | switch (clickedButton.y)
469 | {
470 | case fm_new:
471 | if (fileExists && updated)
472 | confirmationScreenFlag = n_saveChanges;
473 | else if (updated && componentCount > 0)
474 | confirmationScreenFlag = n_saveNewFile;
475 | else
476 | {
477 | NewProject(grid, &updated);
478 | confirmationScreenFlag = none;
479 | }
480 | break;
481 | case fm_open:
482 | SDL_strlcpy(fname, currentFile, 256);
483 | if (fileExists && updated)
484 | confirmationScreenFlag = o_saveChanges;
485 | else if (updated && componentCount > 0)
486 | confirmationScreenFlag = o_saveNewFile;
487 | else
488 | {
489 | ChooseFile(grid, false);
490 | confirmationScreenFlag = none;
491 | }
492 | break;
493 | case fm_save:
494 | if (fileExists)
495 | SaveToFile(grid, currentFile);
496 | else
497 | ChooseFile(grid, true);
498 | updated = false;
499 | confirmationScreenFlag = none;
500 | break;
501 | case fm_saveas:
502 | ChooseFile(grid, true);
503 | updated = false;
504 | confirmationScreenFlag = none;
505 | break;
506 | case fm_exitm:
507 | confirmationScreenFlag = none;
508 | break;
509 | case fm_exitp:
510 | if (fileExists && updated)
511 | confirmationScreenFlag = q_saveChanges;
512 | else if (updated && componentCount > 0)
513 | confirmationScreenFlag = q_saveNewFile;
514 | else
515 | run = false;
516 | break;
517 | default:
518 | break;
519 | }
520 | }
521 | else if (clickedButton.x == con && clickedButton.y > 0)
522 | {
523 | switch (confirmationScreenFlag)
524 | {
525 | case clearGrid:
526 | componentCount = 0;
527 | InitGrid(grid);
528 | updated = true;
529 | break;
530 | case q_saveChanges:
531 | SaveToFile(grid, currentFile);
532 | run = false;
533 | break;
534 | case q_saveNewFile:
535 | ChooseFile(grid, true);
536 | run = false;
537 | break;
538 | case o_saveChanges:
539 | SaveToFile(grid, currentFile);
540 | ChooseFile(grid, false);
541 | if (SDL_strcmp(fname, currentFile))
542 | {
543 | ClearUndoBuffer(¤tUndoLevel, &totalUndoLevel);
544 | updated = false;
545 | }
546 | break;
547 | case o_saveNewFile:
548 | ChooseFile(grid, true);
549 | ChooseFile(grid, false);
550 | if (SDL_strcmp(fname, currentFile))
551 | {
552 | ClearUndoBuffer(¤tUndoLevel, &totalUndoLevel);
553 | updated = false;
554 | }
555 | break;
556 | case n_saveChanges:
557 | SaveToFile(grid, currentFile);
558 | NewProject(grid, &updated);
559 | ClearUndoBuffer(¤tUndoLevel, &totalUndoLevel);
560 | updated = false;
561 | break;
562 | case n_saveNewFile:
563 | ChooseFile(grid, true);
564 | NewProject(grid, &updated);
565 | ClearUndoBuffer(¤tUndoLevel, &totalUndoLevel);
566 | updated = false;
567 | break;
568 | default:
569 | break;
570 | }
571 | confirmationScreenFlag = none;
572 | }
573 | else if (clickedButton.x == con && !clickedButton.y)
574 | {
575 | if (confirmationScreenFlag == q_saveChanges || confirmationScreenFlag == q_saveNewFile)
576 | run = false;
577 | else if (confirmationScreenFlag == o_saveChanges || confirmationScreenFlag == o_saveNewFile)
578 | {
579 | ChooseFile(grid, false);
580 | if (SDL_strcmp(fname, currentFile))
581 | {
582 | updated = false;
583 | ClearUndoBuffer(¤tUndoLevel, &totalUndoLevel);
584 | }
585 | }
586 | else if (confirmationScreenFlag == n_saveChanges || confirmationScreenFlag == n_saveNewFile)
587 | {
588 | NewProject(grid, &updated);
589 | updated = false;
590 | ClearUndoBuffer(¤tUndoLevel, &totalUndoLevel);
591 | }
592 | confirmationScreenFlag = none;
593 | }
594 | else if (clickedButton.x == con)
595 | confirmationScreenFlag = none;
596 | }
597 | break;
598 | case SDL_MOUSEBUTTONUP:
599 | if (drawingWire)
600 | {
601 | endAt = WireIsValid(grid, gridPos, x, y, pad_x, pad_y);
602 | if (endAt && startAt != endAt)
603 | {
604 | if (endAt < 0 && startAt > 0)
605 | {
606 | sender = cell(gridPos.y, gridPos.x);
607 | sendIndex = endAt;
608 | confirmWire = true;
609 | }
610 | else if (endAt > 0 && startAt < 0)
611 | {
612 | receiver = cell(gridPos.y, gridPos.x);
613 | receiveIndex = endAt;
614 | confirmWire = true;
615 | }
616 | if (sender != receiver && confirmWire)
617 | {
618 | if (ComponentList[receiver].inpSrc[receiveIndex - 1].x != -1)
619 | UpdateChildCount(sender, false);
620 | ComponentList[receiver].inpSrc[receiveIndex - 1] = (Pair){sender, sendIndex * -1 - 1};
621 | ComponentList[receiver].inputs[receiveIndex - 1] = &ComponentList[sender];
622 | updated = true;
623 | for (int i = 0; i < 256; i++)
624 | AlreadyUpdated[i] = false;
625 | UpdateChildCount(sender, true);
626 | ShiftUndoBuffer(¤tUndoLevel, &totalUndoLevel);
627 | UndoBuffer[0].act = 'w';
628 | UndoBuffer[0].Action.wired.sender = sender;
629 | UndoBuffer[0].Action.wired.connection.receiver = receiver;
630 | UndoBuffer[0].Action.wired.connection.receiveIndex = receiveIndex - 1;
631 | UndoBuffer[0].Action.wired.connection.sendIndex = sendIndex * -1 - 1;
632 | }
633 | }
634 | drawingWire = false;
635 | }
636 | if (movingCompo)
637 | {
638 | Component compo = ComponentList[compoMoved];
639 | Pair finalPos = initialPos;
640 | if (compo.start.x < 0 || compo.start.y < 0)
641 | {
642 | ComponentList[compoMoved].start = initialPos;
643 | SetIOPos(&ComponentList[compoMoved]);
644 | }
645 | if (!PositionIsValid(grid, compo.width, compo.size, compo.start))
646 | {
647 | ComponentList[compoMoved].start = initialPos;
648 | SetIOPos(&ComponentList[compoMoved]);
649 | }
650 | else
651 | finalPos = compo.start;
652 | for (int i = finalPos.y; i < finalPos.y + compo.size; i++)
653 | for (int j = finalPos.x; j < finalPos.x + compo.width; j++)
654 | cell(i, j) = compoMoved;
655 | movingCompo = false;
656 | selected = finalPos;
657 | if (initialPos.x != compo.start.x || initialPos.y != compo.start.y)
658 | {
659 | updated = true;
660 | ShiftUndoBuffer(¤tUndoLevel, &totalUndoLevel);
661 | UndoBuffer[0].act = 'm';
662 | UndoBuffer[0].Action.moved.before = initialPos;
663 | UndoBuffer[0].Action.moved.after = finalPos;
664 | UndoBuffer[0].Action.moved.index = compoMoved;
665 | }
666 | }
667 | case SDL_MOUSEMOTION:
668 | {
669 | int w, h;
670 | GetWidthHeight(&w, &h, compoChoice.type, compoChoice.size);
671 | compoChoice.pos = gridPos;
672 | if (compoChoice.pos.x + w >= GRID_ROW)
673 | compoChoice.pos.x = GRID_ROW - w;
674 | if (compoChoice.pos.y + h >= GRID_COL)
675 | compoChoice.pos.y = GRID_COL - h;
676 | }
677 | if (drawingWire)
678 | {
679 | WireEndPos(x, y);
680 | }
681 | if (movingCompo)
682 | {
683 | Component compo = ComponentList[compoMoved];
684 | Pair newPos = {gridPos.x - offset.x, gridPos.y - offset.y};
685 | if (gridPos.x - offset.x + compo.width >= GRID_ROW)
686 | newPos.x = GRID_ROW - compo.width;
687 | if (gridPos.y - offset.y + compo.size >= GRID_COL)
688 | newPos.y = GRID_COL - compo.size;
689 | if (snapToGrid)
690 | {
691 | newPos.x -= newPos.x % (SCALE / 2);
692 | newPos.y -= newPos.y % (SCALE / 2);
693 | }
694 | newPos.x = newPos.x < 0 ? 0 : newPos.x;
695 | newPos.y = newPos.y < 0 ? 0 : newPos.y;
696 |
697 | compo.start = newPos;
698 | CollisionCheck(grid, &compo);
699 | ComponentList[compoMoved].start = compo.start;
700 | SetIOPos(&ComponentList[compoMoved]);
701 | }
702 | break;
703 | case SDL_KEYDOWN:
704 | switch (e.key.keysym.scancode)
705 | {
706 | case SDL_SCANCODE_MINUS:
707 | ChangeNumofInputs(true, &compoChoice);
708 | break;
709 | case SDL_SCANCODE_EQUALS:
710 | ChangeNumofInputs(false, &compoChoice);
711 | break;
712 | case SDL_SCANCODE_DELETE:
713 | if (!simulating)
714 | {
715 | if (cursorInGrid && cell(gridPos.y, gridPos.x) >= 0)
716 | {
717 | AddDeletedToUndo(¤tUndoLevel, &totalUndoLevel, cell(gridPos.y, gridPos.x));
718 | DeleteComponent(grid, gridPos);
719 | updated = true;
720 | }
721 | else if (selected.x >= 0)
722 | {
723 | AddDeletedToUndo(¤tUndoLevel, &totalUndoLevel, cell(selected.y, selected.x));
724 | DeleteComponent(grid, selected);
725 | updated = true;
726 | }
727 | selected = (Pair){-1, -1};
728 | }
729 | break;
730 | case SDL_SCANCODE_LSHIFT:
731 | if (!snapToggeled)
732 | snapToGrid = true;
733 | break;
734 | case SDL_SCANCODE_RSHIFT:
735 | if (!snapToggeled)
736 | snapToGrid = true;
737 | break;
738 | case SDL_SCANCODE_LCTRL:
739 | ctrlHeld = !simulating;
740 | break;
741 | case SDL_SCANCODE_RCTRL:
742 | ctrlHeld = !simulating;
743 | break;
744 | case SDL_SCANCODE_Z:
745 | if (ctrlHeld && currentUndoLevel < totalUndoLevel)
746 | Undo(grid, ¤tUndoLevel, totalUndoLevel);
747 | break;
748 | case SDL_SCANCODE_R:
749 | if (ctrlHeld && currentUndoLevel > 0)
750 | Redo(grid, ¤tUndoLevel, totalUndoLevel);
751 | break;
752 | case SDL_SCANCODE_S:
753 | if (ctrlHeld)
754 | {
755 | if (fileExists)
756 | SaveToFile(grid, currentFile);
757 | else
758 | ChooseFile(grid, true);
759 | updated = false;
760 | }
761 | break;
762 | case SDL_SCANCODE_O:
763 | if (ctrlHeld && !simulating)
764 | {
765 | if (fileExists && updated)
766 | confirmationScreenFlag = o_saveChanges;
767 | else if (updated && componentCount > 0)
768 | confirmationScreenFlag = o_saveNewFile;
769 | else
770 | ChooseFile(grid, false);
771 | ClearUndoBuffer(¤tUndoLevel, &totalUndoLevel);
772 | updated = false;
773 | }
774 | break;
775 | default:
776 | break;
777 | }
778 | break;
779 | case SDL_KEYUP:
780 | switch (e.key.keysym.scancode)
781 | {
782 | case SDL_SCANCODE_LSHIFT:
783 | snapToGrid = snapToggeled;
784 | break;
785 | case SDL_SCANCODE_RSHIFT:
786 | snapToGrid = snapToggeled;
787 | break;
788 | case SDL_SCANCODE_LCTRL:
789 | ctrlHeld = false;
790 | break;
791 | case SDL_SCANCODE_RCTRL:
792 | ctrlHeld = false;
793 | break;
794 | default:
795 | break;
796 | }
797 | break;
798 | default:
799 | break;
800 | }
801 | if (draw)
802 | {
803 | DrawCall(menuExpanded, drawingWire, x, y, compoChoice, pad_x, pad_y,
804 | simulating, &dropDownAnimationFlag, gridPos, grid, movingCompo, selected, snapToGrid, confirmationScreenFlag);
805 | draw = false;
806 | }
807 | }
808 |
809 | if (simulating)
810 | {
811 | for (int i = 0; i < 256; i++)
812 | AlreadyUpdated[i] = false;
813 | UpdateComponents(updateOrder);
814 | time += DELAY;
815 | if (time >= DELAY * 20)
816 | time = 0;
817 | selected = (Pair){-1, -1};
818 | }
819 |
820 | if (simulating || animating < 8)
821 | DrawCall(menuExpanded, drawingWire, x, y, compoChoice, pad_x, pad_y,
822 | simulating, &dropDownAnimationFlag, gridPos, grid, movingCompo, selected, snapToGrid, confirmationScreenFlag);
823 |
824 | animating += 1;
825 | animating = (animating > 8)? 8 : animating;
826 |
827 | if ((SDL_GetTicks() - begin) < DELAY)
828 | SDL_Delay(DELAY - (SDL_GetTicks() - begin));
829 | else
830 | SDL_Delay(DELAY);
831 | }
832 | }
833 |
--------------------------------------------------------------------------------
/src/program.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include "component.h"
4 | #define SDL_MAIN_HANDELED
5 | #include "SDL2/SDL.h"
6 | #include "SDL2/SDL_ttf.h"
7 |
8 | #define cell(y, x) grid[y * GRID_ROW + x]
9 | #define WINDOW_WIDTH 500
10 | #define WINDOW_HEIGHT 500
11 |
12 | #define GRID_WIDTH 1100
13 | #define GRID_HEIGHT 750
14 | #define MENU_WIDTH 200
15 |
16 | #define SCALE 12
17 | #define CELL_SIZE 2
18 | #define MENU_FONT_SIZE 30
19 | #define GRID_ROW (GRID_WIDTH / CELL_SIZE - GRID_WIDTH / CELL_SIZE % SCALE)
20 | #define GRID_COL (GRID_HEIGHT / CELL_SIZE - GRID_HEIGHT / CELL_SIZE % SCALE)
21 |
22 | #define MIN_WINDOW_WIDTH GRID_WIDTH + MENU_WIDTH
23 | #define MIN_WINDOW_HEIGHT GRID_HEIGHT
24 | #define DELAY 20
25 |
26 | #define TERMINAL_SIZE 12
27 | #define MAX_UNDOS 200
28 |
29 | #define RED 181, 60, 54
30 | #define ORANGE 228, 135, 112
31 | #define YELLOW 250, 189, 46
32 | #define GREEN 94, 168, 98
33 | #define VOMIT_GREEN 93, 142, 38
34 | #define PURPLE 180, 102, 173
35 | #define BLUE 40, 56, 118
36 | #define WHITE 255, 255, 255
37 | #define GRAY 100, 103, 109
38 | #define BLACK 0, 0, 0
39 |
40 | typedef enum
41 | {
42 | sm,
43 | fm,
44 | cm,
45 | con
46 | } ButtonClan;
47 | typedef enum
48 | {
49 | sm_run,
50 | sm_compo,
51 | sm_inc,
52 | sm_dec,
53 | sm_undo,
54 | sm_redo,
55 | sm_snap,
56 | sm_delete,
57 | sm_clear,
58 | sm_fmenu,
59 | sm_total
60 | } SidemenuButtons;
61 | typedef enum
62 | {
63 | fm_new,
64 | fm_open,
65 | fm_save,
66 | fm_saveas,
67 | fm_exitm,
68 | fm_exitp,
69 | fm_total
70 | } FileMenuButtons;
71 |
72 | typedef struct
73 | {
74 | Type type;
75 | char size;
76 | Pair pos;
77 | } Selection;
78 |
79 | typedef struct Button
80 | {
81 | SDL_Rect buttonRect;
82 | Selection selection;
83 | SDL_Color color;
84 | } Button;
85 |
86 | bool PositionIsValid(int *, int, int, Pair);
87 | void InitEverything(int *, int, char **);
88 | void InitMenu(int, int, bool);
89 | void CloseEverything();
90 | void MainProgramLoop(int *);
91 | Pair MouseIsOver(int, int, bool, Selection, bool);
92 | void InitGrid(int *);
93 |
--------------------------------------------------------------------------------