├── .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 | --------------------------------------------------------------------------------