├── .gitattributes ├── .gitignore ├── README.md ├── hypercube.sln └── hypercube ├── hypercube.vcxproj ├── hypercube.vcxproj.filters └── main.c /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hypercube 2 | 3 | This project generates an ASCII projection of a rotating 4D hypercube in the Windows console. It is based on the paper [Four-Space Visualization of 4D Objects](https://hollasch.github.io/ray4/Four-Space_Visualization_of_4D_Objects.html#chapter4). 4 | 5 | There is a [Linux port](https://github.com/dominicmkennedy/hypercube) by [dominicmkennedy](https://github.com/dominicmkennedy/). 6 | 7 | ``` 8 | ___@______________ 9 | ____-- |\ -------------__@ 10 | ____-- || ___-- | 11 | @___________________@__________ ___- -- | 12 | | ---_ ____-------------------@----_@- | 13 | | --@_____ | | /| __- | | 14 | | | -------_____ /_|- | | 15 | | | | | -----@- | | | 16 | \ | \| | | | | 17 | | | | \ | | / / 18 | | | | | | | | | 19 | | | | | | | | | 20 | | \ | | | | | | 21 | | | | | / | | | 22 | | | |_@_________| | | | 23 | | | ___@______ |---|---_@_ | 24 | \ | _____- -----|--_|____ -___ | 25 | | _@_____ | |_ ------_@ 26 | | __-_- ------____ | _-| __ 27 | | ___- -----@- | __ 28 | @-____ \ | __ 29 | ------_____ \ | __ 30 | ------_____ \|__ 31 | -----@ 32 | ``` 33 | -------------------------------------------------------------------------------- /hypercube.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31729.503 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hypercube", "hypercube\hypercube.vcxproj", "{0781FA37-E169-46B4-B4F8-017057958492}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {0781FA37-E169-46B4-B4F8-017057958492}.Debug|x64.ActiveCfg = Debug|x64 17 | {0781FA37-E169-46B4-B4F8-017057958492}.Debug|x64.Build.0 = Debug|x64 18 | {0781FA37-E169-46B4-B4F8-017057958492}.Debug|x86.ActiveCfg = Debug|Win32 19 | {0781FA37-E169-46B4-B4F8-017057958492}.Debug|x86.Build.0 = Debug|Win32 20 | {0781FA37-E169-46B4-B4F8-017057958492}.Release|x64.ActiveCfg = Release|x64 21 | {0781FA37-E169-46B4-B4F8-017057958492}.Release|x64.Build.0 = Release|x64 22 | {0781FA37-E169-46B4-B4F8-017057958492}.Release|x86.ActiveCfg = Release|Win32 23 | {0781FA37-E169-46B4-B4F8-017057958492}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {36D6D34D-B3E6-47F6-B44B-E3CC36656EAA} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /hypercube/hypercube.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {0781fa37-e169-46b4-b4f8-017057958492} 25 | hypercube 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /hypercube/hypercube.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /hypercube/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #define _USE_MATH_DEFINES 3 | #include 4 | #include 5 | 6 | // width and height of screen 7 | #define ww 100 8 | #define wh 50 9 | 10 | void clr(CHAR_INFO* d) 11 | { 12 | for (int i = 0; i < ww * wh; ++i) 13 | { 14 | d[i].Attributes = FOREGROUND_GREEN; 15 | d[i].Char.UnicodeChar = ' '; 16 | } 17 | } 18 | 19 | void set(CHAR_INFO* d, COORD pt, char c) 20 | { 21 | d[pt.Y * ww + pt.X].Char.UnicodeChar = c; 22 | } 23 | 24 | char getp(CHAR_INFO* d, COORD* pts, double err) 25 | { 26 | if (abs(pts[0].Y - pts[2].Y) < 2) 27 | { 28 | if (err > 0.5) 29 | { 30 | return '-'; 31 | } 32 | return '_'; 33 | } 34 | 35 | if (abs(pts[0].X - pts[2].X) < 2 && 36 | (pts[0].X >= pts[2].X || pts[1].X != pts[2].X) && 37 | (pts[0].X <= pts[2].X || pts[1].X != pts[0].X)) 38 | { 39 | return '|'; 40 | } 41 | 42 | int mX = pts[0].Y < pts[2].Y ? pts[0].X : pts[2].X; 43 | return mX < pts[1].X ? '\\' : '/';\ 44 | } 45 | 46 | void ln(CHAR_INFO* d, COORD a, COORD b) 47 | { 48 | set(d, a, '@'); 49 | set(d, b, '@'); 50 | 51 | int dx = abs(b.X - a.X), sx = a.X < b.X ? 1 : -1; 52 | int dy = abs(b.Y - a.Y), sy = a.Y < b.Y ? 1 : -1; 53 | int err = (dx > dy ? dx : -dy) / 2, e2; 54 | 55 | COORD pts[3]; 56 | double ers[3]; 57 | 58 | for (int i = 0; i < 3; ++i) 59 | { 60 | pts[i] = a; 61 | ers[i] = ((double)err - dx) / ((double)dy - dx); 62 | ers[i] = sy == 1 ? 1.0f - ers[i] : ers[i]; 63 | 64 | if (a.X == b.X && a.Y == b.Y) { 65 | return; 66 | } 67 | 68 | e2 = err; 69 | if (e2 > -dx) { err -= dy; a.X += sx; } 70 | if (e2 < dy) { err += dx; a.Y += sy; } 71 | } 72 | 73 | for (;;) 74 | { 75 | set(d, pts[1], getp(d, pts, ers[1])); 76 | 77 | pts[0] = pts[1]; 78 | pts[1] = pts[2]; 79 | pts[2] = a; 80 | 81 | ers[0] = ers[1]; 82 | ers[1] = ers[2]; 83 | ers[2] = ((double)err - dx) / ((double)dy - dx); 84 | ers[2] = sy == 1 ? 1.0f - ers[2] : ers[2]; 85 | 86 | if (a.X == b.X && a.Y == b.Y) { 87 | break; 88 | } 89 | 90 | e2 = err; 91 | if (e2 > -dx) { err -= dy; a.X += sx; } 92 | if (e2 < dy) { err += dx; a.Y += sy; } 93 | } 94 | 95 | // add the final point 96 | set(d, pts[1], getp(d, pts, ers[1])); 97 | } 98 | 99 | // hypercube vertices in 4D 100 | double V4[16][4] = 101 | { 102 | {-1, -1, -1, -1}, 103 | { 1, -1, -1, -1}, 104 | {-1, 1, -1, -1}, 105 | { 1, 1, -1, -1}, 106 | {-1, -1, 1, -1}, 107 | { 1, -1, 1, -1}, 108 | {-1, 1, 1, -1}, 109 | { 1, 1, 1, -1}, 110 | {-1, -1, -1, 1}, 111 | { 1, -1, -1, 1}, 112 | {-1, 1, -1, 1}, 113 | { 1, 1, -1, 1}, 114 | {-1, -1, 1, 1}, 115 | { 1, -1, 1, 1}, 116 | {-1, 1, 1, 1}, 117 | { 1, 1, 1, 1}, 118 | }; 119 | 120 | // store the vertices once they have been projected to 3D 121 | double V3[16][3]; 122 | 123 | // final 2D projection 124 | double V2[16][2]; 125 | 126 | // the indices for each line 127 | int indices[32][2] = 128 | { 129 | // cube #1 130 | {0, 1}, 131 | {0, 2}, 132 | {0, 4}, 133 | {1, 3}, 134 | {1, 5}, 135 | {2, 3}, 136 | {2, 6}, 137 | {3, 7}, 138 | {4, 5}, 139 | {4, 6}, 140 | {5, 7}, 141 | {6, 7}, 142 | 143 | // in-between lines 144 | {0, 8}, 145 | {1, 9}, 146 | {2, 10}, 147 | {3, 11}, 148 | {4, 12}, 149 | {5, 13}, 150 | {6, 14}, 151 | {7, 15}, 152 | 153 | // cube #2 154 | {8, 9}, 155 | {8, 10}, 156 | {8, 12}, 157 | {9, 11}, 158 | {9, 13}, 159 | {10, 11}, 160 | {10, 14}, 161 | {11, 15}, 162 | {12, 13}, 163 | {12, 14}, 164 | {13, 15}, 165 | {14, 15}, 166 | }; 167 | 168 | double dot4(const double* V, const double* U) 169 | { 170 | return (V[0] * U[0]) + (V[1] * U[1]) + (V[2] * U[2]) + (V[3] * U[3]); 171 | } 172 | 173 | double norm4(const double* V) 174 | { 175 | return sqrt(dot4(V, V)); 176 | } 177 | 178 | // cross4 computes the four-dimensional cross product of the three vectors 179 | // U, V and W, in that order. 180 | // returns the resulting four-vector. 181 | void cross4(double* result, const double* U, const double* V, const double* W) 182 | { 183 | // intermediate values 184 | double A, B, C, D, E, F; 185 | 186 | // calculate intermediate values 187 | A = (V[0] * W[1]) - (V[1] * W[0]); 188 | B = (V[0] * W[2]) - (V[2] * W[0]); 189 | C = (V[0] * W[3]) - (V[3] * W[0]); 190 | D = (V[1] * W[2]) - (V[2] * W[1]); 191 | E = (V[1] * W[3]) - (V[3] * W[1]); 192 | F = (V[2] * W[3]) - (V[3] * W[2]); 193 | 194 | // calculate the result-vector components 195 | result[0] = (U[1] * F) - (U[2] * E) + (U[3] * D); 196 | result[1] = -(U[0] * F) + (U[2] * C) - (U[3] * B); 197 | result[2] = (U[0] * E) - (U[1] * C) + (U[3] * A); 198 | result[3] = -(U[0] * D) + (U[1] * B) - (U[2] * A); 199 | } 200 | 201 | void vecSub4(double* result, const double* a, const double* b) 202 | { 203 | result[0] = a[0] - b[0]; 204 | result[1] = a[1] - b[1]; 205 | result[2] = a[2] - b[2]; 206 | result[3] = a[3] - b[3]; 207 | } 208 | 209 | void vecScale4(double* vec, double m) 210 | { 211 | vec[0] *= m; 212 | vec[1] *= m; 213 | vec[2] *= m; 214 | vec[3] *= m; 215 | } 216 | 217 | void matVecMul4(double* result, const double* mat, const double* vec) 218 | { 219 | for (int row = 0; row < 4; ++row) 220 | { 221 | result[row] = 0; 222 | for (int col = 0; col < 4; ++col) 223 | { 224 | result[row] += mat[col * 4 + row] * vec[col]; 225 | } 226 | } 227 | } 228 | 229 | // creates a rotation matrix for the XW plane 230 | // T is the angle in radians 231 | void rotXW4(double* result, double T) 232 | { 233 | // column vectors 234 | double* Wa = result + 4 * 0; 235 | double* Wb = result + 4 * 1; 236 | double* Wc = result + 4 * 2; 237 | double* Wd = result + 4 * 3; 238 | 239 | Wa[0] = cos(T); 240 | Wa[1] = 0; 241 | Wa[2] = 0; 242 | Wa[3] = -sin(T); 243 | 244 | Wb[0] = 0; 245 | Wb[1] = 1; 246 | Wb[2] = 0; 247 | Wb[3] = 0; 248 | 249 | Wc[0] = 0; 250 | Wc[1] = 0; 251 | Wc[2] = 1; 252 | Wc[3] = 0; 253 | 254 | Wd[0] = sin(T); 255 | Wd[1] = 0; 256 | Wd[2] = 0; 257 | Wd[3] = cos(T); 258 | } 259 | 260 | double from4[4] = { 5, 0, 0, 0 }; 261 | double to4[4] = { 0, 0, 0, 0 }; 262 | double up4[4] = { 0, 1, 0, 0 }; 263 | double over4[4] = { 0, 0, 1, 0 }; 264 | 265 | // generate a 4D view matrix 266 | void view4(double* result) 267 | { 268 | // column vectors 269 | double* Wa = result + 4 * 0; 270 | double* Wb = result + 4 * 1; 271 | double* Wc = result + 4 * 2; 272 | double* Wd = result + 4 * 3; 273 | 274 | // vector norm 275 | double norm; 276 | 277 | // get the normalized Wd column-vector. 278 | vecSub4(Wd, to4, from4); 279 | norm = norm4(Wd); 280 | vecScale4(Wd, 1 / norm); 281 | 282 | // calculate the normalized Wa column-vector. 283 | cross4(Wa, up4, over4, Wd); 284 | norm = norm4(Wa); 285 | vecScale4(Wa, 1 / norm); 286 | 287 | // calculate the normalized Wb column-vector. 288 | cross4(Wb, over4, Wd, Wa); 289 | norm = norm4(Wb); 290 | vecScale4(Wb, 1 / norm); 291 | 292 | // calculate the Wc column-vector. 293 | cross4(Wc, Wd, Wa, Wb); 294 | } 295 | 296 | void projectTo3D(double vAngle, const double* matView, const double* matRotation) 297 | { 298 | // column vectors 299 | const double* Wa = matView + 4 * 0; 300 | const double* Wb = matView + 4 * 1; 301 | const double* Wc = matView + 4 * 2; 302 | const double* Wd = matView + 4 * 3; 303 | 304 | // divisor Values 305 | double S, T; 306 | 307 | T = 1 / tan(vAngle / 2); 308 | 309 | for (int i = 0; i < 16; ++i) 310 | { 311 | double V[4]; 312 | matVecMul4(V, matRotation, V4[i]); 313 | 314 | double Vf[4]; 315 | vecSub4(Vf, V, from4); 316 | 317 | S = T / dot4(Vf, Wd); 318 | 319 | V3[i][0] = S * dot4(Vf, Wa); 320 | V3[i][1] = S * dot4(Vf, Wb); 321 | V3[i][2] = S * dot4(Vf, Wc); 322 | } 323 | } 324 | 325 | double dot3(const double* V, const double* U) 326 | { 327 | return (V[0] * U[0]) + (V[1] * U[1]) + (V[2] * U[2]); 328 | } 329 | 330 | double norm3(const double* V) 331 | { 332 | return sqrt(dot3(V, V)); 333 | } 334 | 335 | void cross3(double* result, const double* U, const double* V) 336 | { 337 | result[0] = (U[1] * V[2]) - (U[2] * V[1]); 338 | result[1] = (U[2] * V[0]) - (U[0] * V[2]); 339 | result[2] = (U[0] * V[1]) - (U[1] * V[0]); 340 | } 341 | void vecSub3(double* result, const double* a, const double* b) 342 | { 343 | result[0] = a[0] - b[0]; 344 | result[1] = a[1] - b[1]; 345 | result[2] = a[2] - b[2]; 346 | } 347 | 348 | void vecScale3(double* vec, double m) 349 | { 350 | vec[0] *= m; 351 | vec[1] *= m; 352 | vec[2] *= m; 353 | } 354 | 355 | void matVecMul3(double* result, const double* mat, const double* vec) 356 | { 357 | for (int row = 0; row < 3; ++row) 358 | { 359 | result[row] = 0; 360 | for (int col = 0; col < 3; ++col) 361 | { 362 | result[row] += mat[col * 3 + row] * vec[col]; 363 | } 364 | } 365 | } 366 | 367 | void rotXZ3(double* result, double T) 368 | { 369 | // column vectors 370 | double* Va = result + 3 * 0; 371 | double* Vb = result + 3 * 1; 372 | double* Vc = result + 3 * 2; 373 | 374 | Va[0] = cos(T); 375 | Va[1] = 0; 376 | Va[2] = -sin(T); 377 | 378 | Vb[0] = 0; 379 | Vb[1] = 1; 380 | Vb[2] = 0; 381 | 382 | Vc[0] = sin(T); 383 | Vc[1] = 0; 384 | Vc[2] = cos(T); 385 | } 386 | 387 | double from3[3] = { 3.00f, 0.99f, 1.82f }; 388 | double to3[3] = { 0, 0, 0 }; 389 | double up3[3] = { 0, -1, 0 }; 390 | 391 | // generate a 3D view matrix 392 | void view3(double* result) 393 | { 394 | double* Va = result + 3 * 0; 395 | double* Vb = result + 3 * 1; 396 | double* Vc = result + 3 * 2; 397 | 398 | double norm; 399 | 400 | // Get the normalized Vc column-vector. 401 | vecSub3(Vc, to3, from3); 402 | norm = norm3(Vc); 403 | vecScale3(Vc, 1 / norm); 404 | 405 | // Calculate the normalized Va column-vector. 406 | cross3(Va, Vc, up3); 407 | norm = norm3(Va); 408 | vecScale3(Va, 1 / norm); 409 | 410 | // Calculate the Vb column-vector. 411 | cross3(Vb, Va, Vc); 412 | } 413 | 414 | void projectTo2D(double vAngle, const double* matView, const double* matRotation) 415 | { 416 | // column vectors 417 | const double* Va = matView + 3 * 0; 418 | const double* Vb = matView + 3 * 1; 419 | const double* Vc = matView + 3 * 2; 420 | 421 | // divisor values 422 | double S, T; 423 | 424 | T = 1 / tan(vAngle / 2); 425 | 426 | for (int i = 0; i < 16; ++i) 427 | { 428 | double V[3]; 429 | matVecMul3(V, matRotation, V3[i]); 430 | 431 | double Vf[3]; 432 | vecSub3(Vf, V, from3); 433 | 434 | S = T / dot3(Vf, Vc); 435 | 436 | V2[i][0] = (ww / 2) + (ww * S * dot3(Vf, Va)); 437 | V2[i][1] = (wh / 2) + (wh * S * dot3(Vf, Vb)); 438 | } 439 | } 440 | 441 | int main(int argc, const char* argv[]) 442 | { 443 | // get the console handle 444 | HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); 445 | 446 | // set console dimensions 447 | COORD s = { ww, wh }; 448 | SMALL_RECT r = { 0, 0, ww, wh }; 449 | COORD z = { 0, 0 }; 450 | SetConsoleScreenBufferSize(h, s); 451 | SetConsoleWindowInfo(h, TRUE, &r); 452 | 453 | CHAR_INFO d[wh * ww]; 454 | 455 | double viewMat4[4 * 4]; 456 | view4(viewMat4); 457 | double rot4[4 * 4]; 458 | 459 | double viewMat3[3 * 3]; 460 | view3(viewMat3); 461 | double rot3[3 * 3]; 462 | 463 | double rotation = 0; 464 | 465 | for (;;) 466 | { 467 | rotation += 0.01f; 468 | 469 | rotXW4(rot4, rotation); 470 | projectTo3D(M_PI / 3, viewMat4, rot4); 471 | 472 | rotXZ3(rot3, rotation * 0.3); 473 | projectTo2D(M_PI / 4, viewMat3, rot3); 474 | 475 | clr(d); 476 | 477 | for (int i = 0; i < 32; ++i) 478 | { 479 | int a = indices[i][0]; 480 | int b = indices[i][1]; 481 | COORD c1 = { (SHORT)V2[a][0], (SHORT)V2[a][1] }; 482 | COORD c2 = { (SHORT)V2[b][0], (SHORT)V2[b][1] }; 483 | ln(d, c1, c2); 484 | } 485 | 486 | WriteConsoleOutput(h, d, s, z, &r); 487 | 488 | Sleep(1); 489 | } 490 | } --------------------------------------------------------------------------------