├── DDD.png ├── Win32-OpenGL-DLL ├── Win32-OpenGL-DLL.h ├── framework.h ├── pch.cpp ├── pch.h ├── Win32-OpenGL-DLL.vcxproj.filters ├── Win32-OpenGL-DLL.vcxproj └── dllmain.cpp ├── DDD ├── ddd.code-workspace ├── UI.cs ├── demos │ ├── coil.ps1 │ ├── sphere.ps1 │ ├── heightmap.ps1 │ ├── 3dtext.ps1 │ └── teapot.ps1 ├── DDD.csproj ├── assets │ ├── aliases.ps1 │ └── functions.ps1 ├── Point.cs ├── make.ps1 ├── Out-3d.cs ├── Vector.cs ├── Matrix.cs └── UIWindows.cs ├── README.md ├── CPP2CSBindings ├── App.config ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ └── AssemblyInfo.cs ├── packages.config ├── Program.cs └── CPP2CSBindings.csproj ├── DotNetCore-OpenGL ├── DotNetCore-OpenGL.csproj └── Program.cs ├── .editorconfig ├── DDD.code-workspace ├── Prototype-CSWindow-Windows ├── Prototype-CSWindow-Windows.csproj ├── .vscode │ ├── launch.json │ └── tasks.json └── doPublish.ps1 ├── UnitTest-Point ├── UnitTest.csproj ├── UnitTest_Point.cs ├── UnitTest_Vector.cs └── UnitTest_Matrix.cs ├── Win32-OpenGL ├── Win32-OpenGL.vcxproj.filters ├── Win32-OpenGL.vcxproj └── Win32-OpenGL.cpp ├── LICENSE ├── .gitignore └── DDD.sln /DDD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lenihan/DDD/HEAD/DDD.png -------------------------------------------------------------------------------- /Win32-OpenGL-DLL/Win32-OpenGL-DLL.h: -------------------------------------------------------------------------------- 1 | __declspec(dllimport) int main(); -------------------------------------------------------------------------------- /DDD/ddd.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DDD 2 | Cross-platform (Windows, Linux, Mac) 3D tools for PowerShell. 3 | 4 | ![alt text](DDD.png "DDD") 5 | 6 | [DDD Blog Posts](http://www.davidlenihan.com/category/ddd/) 7 | -------------------------------------------------------------------------------- /Win32-OpenGL-DLL/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 4 | // Windows Header Files 5 | #include 6 | -------------------------------------------------------------------------------- /CPP2CSBindings/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Win32-OpenGL-DLL/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /DDD/UI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DDD 5 | { 6 | interface UI 7 | { 8 | void ShowWindow(List Objects, Point BoundingBoxMin, Point BoundingBoxMax, string Title); 9 | } 10 | } -------------------------------------------------------------------------------- /CPP2CSBindings/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /CPP2CSBindings/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /DotNetCore-OpenGL/DotNetCore-OpenGL.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | DotNetCore_OpenGL 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # To learn more about .editorconfig see https://aka.ms/editorconfigdocs 2 | 3 | # All files 4 | [*] 5 | indent_style = space 6 | 7 | # Xml files 8 | 9 | # CA1051: Do not declare visible instance fields 10 | dotnet_diagnostic.CA1051.severity = none 11 | 12 | [*.xml] 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /DDD/demos/coil.ps1: -------------------------------------------------------------------------------- 1 | # create a coil 2 | Install-Module DDD -Repository PSGallery 3 | Import-Module DDD 4 | 0..3600 | ForEach-Object { 5 | $deg = $_ 6 | $rad = $deg*[Math]::PI/180 7 | $x = [Math]::Sin($rad)*360 8 | $y = $deg 9 | $z = [Math]::Cos($rad)*360 10 | New-Point $x $y $z 11 | } | Out-3d -------------------------------------------------------------------------------- /DDD.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "name": "DDD", 5 | "path": "DDD" 6 | }, 7 | { 8 | "name": "GraphicalTools", 9 | "path": "C:\\Users\\David\\src\\GraphicalTools" 10 | }, 11 | { 12 | "name": "Avalonia", 13 | "path": "C:\\Users\\David\\src\\Avalonia" 14 | } 15 | ], 16 | "settings": {} 17 | } -------------------------------------------------------------------------------- /DDD/demos/sphere.ps1: -------------------------------------------------------------------------------- 1 | # create a sphere 2 | Install-Module DDD -Repository PSGallery 3 | Import-Module DDD 4 | $points = for($degY = 0; $degY -lt 360; $degY += 10) { 5 | for($degZ = 0; $degZ -lt 360; $degZ += 10) { 6 | $p = New-Point 1 0 0 7 | $rotZ = New-RotateZMatrix $degZ 8 | $rotY = New-RotateYMatrix $degY 9 | $rotZ * $rotY * $p 10 | } 11 | } 12 | $points | Out-3d -------------------------------------------------------------------------------- /DotNetCore-OpenGL/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace DotNetCore_OpenGL 5 | { 6 | class Program 7 | { 8 | //[DllImport("Win32-OpenGL-DLL.dll")] 9 | [DllImport("C:/Users/David/src/DDD/x64/Debug/Win32-OpenGL-DLL.dll")] 10 | public static extern int main(); 11 | 12 | static void Main(string[] args) 13 | { 14 | main(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DDD/demos/heightmap.ps1: -------------------------------------------------------------------------------- 1 | Add-Type -Assembly 'System.Drawing' 2 | Import-Module 'DDD' 3 | 4 | Invoke-WebRequest 'https://upload.wikimedia.org/wikipedia/commons/5/57/Heightmap.png' -OutFile 'heightmap.png' 5 | $bmp = [System.Drawing.Bitmap]::FromFile('./heightmap.png') 6 | 7 | $scale = 50 8 | 9 | $points = for ($y = 0; $y -lt $bmp.Height; $y++) { 10 | for ($x = 0; $x -lt $bmp.Width; $x++) { 11 | New-Point $x $y ($bmp.GetPixel($x,$y).GetBrightness() * $scale) 12 | } 13 | } 14 | 15 | $points | Out-3d -------------------------------------------------------------------------------- /DDD/demos/3dtext.ps1: -------------------------------------------------------------------------------- 1 | $text = "PowerShell!" 2 | 3 | 4 | import-module DDD 5 | 6 | Add-Type -AssemblyName System.Drawing 7 | 8 | $bmp = [Drawing.Bitmap]::new(($w = 10*$text.Length), ($h=20)) 9 | $g = [Drawing.Graphics]::FromImage($bmp) 10 | $g.DrawString($text, [Drawing.Font]::new("Fixedsys", 10), [Drawing.Brushes]::Black, 0, 0) 11 | 12 | $allPoints = foreach ($x in 0..($w-1)) 13 | { 14 | foreach ($y in 0..($h-1)) 15 | { 16 | if ($bmp.GetPixel($x, $y).A -gt 0) { 17 | new-point $x ($h-$y) 0 18 | } 19 | } 20 | } 21 | $allPoints | out-3d -------------------------------------------------------------------------------- /Win32-OpenGL-DLL/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /Prototype-CSWindow-Windows/Prototype-CSWindow-Windows.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | Prototype_CSWindow_Windows 7 | 8 | 9 | 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /UnitTest-Point/UnitTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | UnitTest_Point 6 | 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /DDD/DDD.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | false 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | 13 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /DDD/demos/teapot.ps1: -------------------------------------------------------------------------------- 1 | # load a teapot in OBJ file format 2 | Install-Module DDD -Repository PSGallery 3 | Import-Module DDD 4 | 5 | $url = "https://graphics.cs.utah.edu/courses/cs6620/fall2019/prj05/teapot.obj" 6 | $obj = (Invoke-WebRequest $url).ToString() 7 | $windowsLineEnding = "`r`n" 8 | $lines = $obj -split $windowsLineEnding 9 | 10 | # regular expression to match a vertex: "v   " 11 | $V = "^v"  # https://en.wikipedia.org/wiki/Wavefront_.obj_file#Geometric_vertex 12 | $WS = "\s+" # whitespace 13 | $FLOAT = "[\d\.\-]+" 14 | # use "Named Captures" to get x, y, and z: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7#named-captures 15 | $X_FLOAT = "(?$FLOAT)" 16 | $Y_FLOAT = "(?$FLOAT)" 17 | $Z_FLOAT = "(?$FLOAT)" 18 | $vertRegex = "$V$WS$X_FLOAT$WS$Y_FLOAT$WS$Z_FLOAT" 19 | 20 | # parse .obj and output verts 21 | $teapot = $lines | ForEach-Object { 22 |     if ($_ -match $vertRegex) {New-Point $Matches.x $Matches.y $Matches.z} 23 | }  24 | $teapot | Out-3d -------------------------------------------------------------------------------- /Win32-OpenGL/Win32-OpenGL.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 David Lenihan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CPP2CSBindings/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace CPP2CSBindings.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Prototype-CSWindow-Windows/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/Prototype-CSWindow-Windows.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /DDD/assets/aliases.ps1: -------------------------------------------------------------------------------- 1 | # VERBS - from https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands?view=powershell-7 2 | # New = n 3 | # Get = g 4 | # Out = o 5 | 6 | # NOUNS 7 | # Point = pt 8 | # Vector = vec 9 | # Matrix = mat 10 | # Array = arr 11 | # Identity = i 12 | # Zero = z 13 | # Rotation = rot 14 | # Scale = sc 15 | # Translate = tr 16 | 17 | Set-Alias -Name npt -Value New-Point 18 | Set-Alias -Name nptarr -Value New-PointArray 19 | 20 | Set-Alias -Name nvec -Value New-Vector 21 | Set-Alias -Name nvecarr -Value New-VectorArray 22 | 23 | Set-Alias -Name nmat -Value New-Matrix 24 | Set-Alias -Name nmatarr -Value New-MatrixArray 25 | 26 | Set-Alias -Name nimat -Value New-IdentityMatrix 27 | Set-Alias -Name nzmat -Value New-ZeroMatrix 28 | 29 | Set-Alias -Name nrotxmat -Value New-RotateXMatrix 30 | Set-Alias -Name nrotymat -Value New-RotateYMatrix 31 | Set-Alias -Name nrotzmat -Value New-RotateZMatrix 32 | 33 | Set-Alias -Name nscmat -Value New-ScaleMatrix 34 | 35 | Set-Alias -Name ntrmatxyz -Value New-TranslateMatrixXYZ 36 | Set-Alias -Name ntrmatvec -Value New-TranslateMatrixVector 37 | 38 | Set-Alias -Name gcp -Value Get-CrossProduct 39 | Set-Alias -Name cross -Value Get-CrossProduct 40 | 41 | Set-Alias -Name gdp -Value Get-DotProduct 42 | Set-Alias -Name dot -Value Get-DotProduct -------------------------------------------------------------------------------- /Win32-OpenGL-DLL/Win32-OpenGL-DLL.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;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 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | -------------------------------------------------------------------------------- /Prototype-CSWindow-Windows/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Prototype-CSWindow-Windows.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/Prototype-CSWindow-Windows.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/Prototype-CSWindow-Windows.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /CPP2CSBindings/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("CPP2CSBindings")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CPP2CSBindings")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2c4c3d4d-88c8-4b29-9103-ae4806b7801a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /CPP2CSBindings/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using CppSharp.Generators.CSharp; 7 | 8 | using System.IO; 9 | using CppSharp; 10 | using CppSharp.AST; 11 | using CppSharp.AST.Extensions; 12 | using CppSharp.Generators; 13 | 14 | namespace CPP2CSBindings 15 | { 16 | class Program 17 | { 18 | static void Main(string[] args) 19 | { 20 | ConsoleDriver.Run(new DDDCppSharp()); 21 | } 22 | } 23 | 24 | public class DDDCppSharp : ILibrary 25 | { 26 | 27 | 28 | // Setup the driver options here. 29 | public void Setup(Driver driver) 30 | { 31 | var options = driver.Options; 32 | options.GeneratorKind = GeneratorKind.CSharp; 33 | var module = options.AddModule("Sample"); 34 | //module.IncludeDirs.Add(@"C:\Users\David\src\DDD\Win32-OpenGL-DLL"); 35 | module.Headers.Add(@"C:\Users\David\src\DDD\Win32-OpenGL-DLL\Win32-OpenGL-DLL.h"); 36 | //module.LibraryDirs.Add(@"C:\Users\David\src\DDD\x64\Debug"); 37 | //module.Libraries.Add("Win32-OpenGL-DLL.lib"); 38 | 39 | //module.Headers.Add(@"C:\Program Files(x86)\Windows Kits\10\Include\10.0.18362.0\um\WinUser.h"); 40 | module.IncludeDirs.Add(@"C:\Program Files(x86)\Windows Kits\10\Include\10.0.18362.0\um"); 41 | module.Headers.Add("WinUser.h"); 42 | } 43 | // Setup your passes here. 44 | public void SetupPasses(Driver driver) { } 45 | // Do transformations that should happen before passes are processed. 46 | public void Preprocess(Driver driver, ASTContext ctx) { } 47 | // Do transformations that should happen after passes are processed. 48 | public void Postprocess(Driver driver, ASTContext ctx) { } 49 | } 50 | } -------------------------------------------------------------------------------- /DDD/assets/functions.ps1: -------------------------------------------------------------------------------- 1 | # function global:wrappers around constructors 2 | function global:New-Point { 3 | New-Object -TypeName 'DDD.Point' -ArgumentList $args 4 | } 5 | function global:New-PointArray ([Parameter(Mandatory=$true)][ulong]$Size) { 6 | New-Object -TypeName 'DDD.Point[]' -ArgumentList $Size 7 | } 8 | function global:New-Vector { 9 | New-Object -TypeName 'DDD.Vector' -ArgumentList $args 10 | } 11 | function global:New-VectorArray ([Parameter(Mandatory=$true)][ulong]$Size) { 12 | New-Object -TypeName 'DDD.Vector[]' -ArgumentList $Size 13 | } 14 | function global:New-Matrix { 15 | New-Object -TypeName 'DDD.Matrix' -ArgumentList $args 16 | } 17 | function global:New-MatrixArray ([Parameter(Mandatory=$true)][ulong]$Size) { 18 | New-Object -TypeName 'DDD.Matrix[]' -ArgumentList $Size 19 | } 20 | function global:New-IdentityMatrix { 21 | return [DDD.Matrix]::Identity() 22 | } 23 | function global:New-ZeroMatrix { 24 | return [DDD.Matrix]::Zero() 25 | } 26 | function global:New-RotateXMatrix ([Parameter(Mandatory=$true)][double]$DegreesCCW) { 27 | return [DDD.Matrix]::RotateX($DegreesCCW) 28 | } 29 | function global:New-RotateYMatrix ([Parameter(Mandatory=$true)][double]$DegreesCCW) { 30 | return [DDD.Matrix]::RotateY($DegreesCCW) 31 | } 32 | function global:New-RotateZMatrix ([Parameter(Mandatory=$true)][double]$DegreesCCW) { 33 | return [DDD.Matrix]::RotateZ($DegreesCCW) 34 | } 35 | function global:New-ScaleMatrix ( [Parameter(Mandatory=$true)][double]$X, 36 | [Parameter(Mandatory=$true)][double]$Y, 37 | [Parameter(Mandatory=$true)][double]$Z) { 38 | return [DDD.Matrix]::Scale($X, $Y, $Z) 39 | } 40 | function global:New-TranslateMatrixXYZ ([Parameter(Mandatory=$true)][double]$X, 41 | [Parameter(Mandatory=$true)][double]$Y, 42 | [Parameter(Mandatory=$true)][double]$Z) { 43 | return [DDD.Matrix]::Translate($X, $Y, $Z) 44 | } 45 | function global:New-TranslateMatrixVector ([Parameter(Mandatory=$true)][DDD.Vector]$Vector) { 46 | return [DDD.Matrix]::Translate($Vector) 47 | } 48 | 49 | 50 | # static functions 51 | function global:Get-CrossProduct ( [Parameter(Mandatory=$true)][DDD.Vector]$VectorA, 52 | [Parameter(Mandatory=$true)][DDD.Vector]$VectorB) { 53 | return [DDD.Vector]::Cross($VectorA, $VectorB) 54 | } 55 | function global:Get-DotProduct ([Parameter(Mandatory=$true)][DDD.Vector]$VectorA, 56 | [Parameter(Mandatory=$true)][DDD.Vector]$VectorB) { 57 | return [DDD.Vector]::Dot($vectorA, $vectorB) 58 | } 59 | -------------------------------------------------------------------------------- /Prototype-CSWindow-Windows/doPublish.ps1: -------------------------------------------------------------------------------- 1 | Param([Switch]$PowerShellGallery) 2 | 3 | # How to write a PowerShell module manifest 4 | # https://docs.microsoft.com/en-us/powershell/scripting/developer/module/how-to-write-a-powershell-module-manifest?view=powershell-7 5 | 6 | $root = $PSScriptRoot 7 | $moduleName = split-path $PSScriptRoot -Leaf 8 | $version = '0.0.1' 9 | $name = 'David Lenihan' 10 | $description = 'Cross-platform (Windows, Linux, Mac) 3D tools for PowerShell.' 11 | $reqAssemblies = @("$moduleName.exe") 12 | $tags = @() 13 | $scripts = @() 14 | $cmdlets = @('Out-3d') 15 | $aliases = @('o3d') 16 | 17 | $cmd = "dotnet build '$root\$moduleName.csproj' --configuration Release" 18 | Write-Host "# $cmd" -ForegroundColor Green 19 | Invoke-Expression $cmd 20 | if ($LASTEXITCODE -ne 0) { 21 | Write-Host "# Fix errors and re-run." -ForegroundColor Green 22 | return 23 | } 24 | 25 | $cmd = "mkdir '$root\publish\$moduleName' -Force | Out-Null" 26 | Write-Host "# $cmd" -ForegroundColor Green 27 | Invoke-Expression $cmd 28 | 29 | try { 30 | $cmd = "Copy-Item '$root\bin\Release\netcoreapp3.1\*' '$root\publish\$moduleName' -Recurse -Force -ErrorAction Stop" 31 | Write-Host "# $cmd" -ForegroundColor Green 32 | Invoke-Expression $cmd 33 | } 34 | catch { 35 | Write-Host $_.Exception.Message -ForegroundColor Red 36 | Write-Host "Kill process to free file." -ForegroundColor Yellow 37 | return 38 | } 39 | 40 | # $cmd = "Copy-Item '$root\assets\functions.ps1' '$root\publish\DDD'" 41 | # Write-Host "# $cmd" -ForegroundColor Green 42 | # Invoke-Expression $cmd 43 | 44 | # $cmd = "Copy-Item '$root\assets\aliases.ps1' '$root\publish\DDD'" 45 | # Write-Host "# $cmd" -ForegroundColor Green 46 | # Invoke-Expression $cmd 47 | 48 | # $manifestArgs = [ordered]@{ 49 | # Path = "$root\publish\DDD\DDD.psd1" 50 | # RootModule = $moduleName 51 | # ModuleVersion = $version 52 | # Author = $name 53 | # CompanyName = $name 54 | # Copyright = "Copyright = '(c) $name. All rights reserved.'" 55 | # Description = $description 56 | # RequiredAssemblies = $reqAssemblies 57 | # ScriptsToProcess = $scripts 58 | # CmdletsToExport = $cmdlets 59 | # Tags = $tags 60 | # AliasesToExport = $aliases 61 | # } 62 | # Write-Host '# $manifestArgs contents: ' -ForegroundColor Green 63 | # $manifestArgs 64 | 65 | # $cmd = "New-ModuleManifest @manifestArgs" 66 | # Write-Host "# $cmd" -ForegroundColor Green 67 | # Invoke-Expression $cmd 68 | 69 | # if ($PowerShellGallery) { 70 | # $cmd = "Publish-Module -Name '$root\publish\DDD' -NuGetApiKey oy2ig7ftcymwygzfh7oaeychbiaumxyuld27f2zetouyca" 71 | # Write-Host "# $cmd" -ForegroundColor Green 72 | # Invoke-Expression $cmd 73 | # } 74 | # else { 75 | # $cmd = "Start-Process -FilePath 'pwsh' -ArgumentList '-NoExit -Command Import-Module -Force -Verbose $root\publish\DDD' -PassThru" 76 | # Write-Host "# $cmd" -ForegroundColor Green 77 | # $process = Invoke-Expression $cmd 78 | # $global:pwshId = $process.Id 79 | # Write-Host "# To run again..." -ForegroundColor Green 80 | # Write-Host "# kill `$pwshId; $($MyInvocation.InvocationName)" -ForegroundColor Green 81 | # } -------------------------------------------------------------------------------- /DDD/Point.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography.X509Certificates; 3 | 4 | namespace DDD 5 | { 6 | [Serializable()] 7 | public struct Point : IEquatable 8 | { 9 | public double X; 10 | public double Y; 11 | public double Z; 12 | 13 | public Point(double x, double y, double z) 14 | { 15 | X = x; 16 | Y = y; 17 | Z = z; 18 | } 19 | public Point(Point p) 20 | { 21 | X = p.X; 22 | Y = p.Y; 23 | Z = p.Z; 24 | } 25 | public Point(double[] arr) 26 | { 27 | X = 0; 28 | Y = 0; 29 | Z = 0; 30 | if (arr == null) return; 31 | if (arr.Length > 0) X = arr[0]; 32 | if (arr.Length > 1) Y = arr[1]; 33 | if (arr.Length > 2) Z = arr[2]; 34 | } 35 | public Point(string str) 36 | { 37 | char[] delimiterChars = { ' ', ',', '\t' }; 38 | char[] trimChars = { ' ', '(', ')', '[', ']', '{', '}', '<', '>', }; 39 | X = 0; 40 | Y = 0; 41 | Z = 0; 42 | if (str == null) return; 43 | string[] values = str.Trim(trimChars).Split(delimiterChars, StringSplitOptions.RemoveEmptyEntries); 44 | if (values.Length > 0) X = double.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture); 45 | if (values.Length > 1) Y = double.Parse(values[1], System.Globalization.CultureInfo.InvariantCulture); 46 | if (values.Length > 2) Z = double.Parse(values[2], System.Globalization.CultureInfo.InvariantCulture); 47 | } 48 | public bool Equals(Point p) => p == null ? false : (X == p.X) && (Y == p.Y) && (Z == p.Z); 49 | public override bool Equals(object obj) 50 | { 51 | if ((obj is null) || !GetType().Equals(obj.GetType())) 52 | { 53 | return false; 54 | } 55 | else 56 | { 57 | return Equals((Point)obj); 58 | } 59 | } 60 | public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); 61 | public override string ToString() 62 | { 63 | // https://ss64.com/ps/syntax-f-operator.html 64 | // {0:#,0.##} 65 | // 0: Index 66 | // #,0 Group integers in 3's with commas, do not hide zero 67 | // .## Show at most 2 decimals, or nothing if no decimal point 68 | return String.Format(System.Globalization.CultureInfo.InvariantCulture, "({0:#,0.##} {1:#,0.##} {2:#,0.##})\n", X, Y, Z); 69 | } 70 | public static bool operator ==(Point? a, Point? b) => a is null ? b is null : a.Equals(b); 71 | public static bool operator !=(Point? a, Point? b) => !(a == b); 72 | public static Point operator +(Point a, Point b) => new Point(a.X + b.X, 73 | a.Y + b.Y, 74 | a.Z + b.Z); 75 | public static Point Add(Point a, Point b) => a + b; 76 | public static Vector operator -(Point a, Point b) => new Vector(a.X - b.X, 77 | a.Y - b.Y, 78 | a.Z - b.Z); 79 | public static Vector Subtract(Point a, Point b) => a - b; 80 | public static Point operator *(double s, Point p) => new Point(s * p.X, 81 | s * p.Y, 82 | s * p.Z); 83 | public static Point Multiply(double s, Point p) => s * p; 84 | public static Point operator *(Point p, double s) => s * p; 85 | public static Point Multiply(Point p, double s) => p * s; 86 | public static Point operator /(Point p, double s) => new Point(p.X / s, 87 | p.Y / s, 88 | p.Z / s); 89 | public static Point Divide(Point p, double s) => p / s; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /DDD/make.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [Switch]$PowerShellGallery, 3 | [Switch]$Release, 4 | [Switch]$KillPrev, 5 | [String]$ApiKey 6 | ) 7 | 8 | if ($KillPrev -and $pwshId) { 9 | Stop-Process $pwshId -ErrorAction SilentlyContinue 10 | } 11 | # How to write a PowerShell module manifest 12 | # https://docs.microsoft.com/en-us/powershell/scripting/developer/module/how-to-write-a-powershell-module-manifest?view=powershell-7 13 | 14 | $root = $PSScriptRoot 15 | $version = '0.0.7' 16 | $name = 'David Lenihan' 17 | $moduleName = 'DDD' 18 | $description = "Cross-platform (Windows, Linux, Mac) 3D tools for PowerShell.`nhttp://www.davidlenihan.com/category/ddd/" 19 | $reqAssemblies = @('DDD.dll') 20 | $tags = @('3D', 'ply', 'obj', 'Point', 'Matrix', 'Vector') 21 | $scripts = @('functions.ps1', 'aliases.ps1') 22 | $cmdlets = @('Out-3d') 23 | $aliases = @('o3d') 24 | $config = $Release ? "Release" : "Debug" 25 | 26 | # Clean 27 | $cmd = "dotnet clean '$root\DDD.csproj' --configuration $config" 28 | Write-Host "# $cmd" -ForegroundColor Green 29 | Invoke-Expression $cmd 30 | if ($LASTEXITCODE -ne 0) { 31 | Write-Host "# Fix errors and re-run." -ForegroundColor Green 32 | return 33 | } 34 | 35 | # Build 36 | # Creates $root\bin\$config\netcoreapp3.1\DDD.dll 37 | # $root\obj\$config\netcoreapp3.1\DDD.dll 38 | $cmd = "dotnet build '$root\DDD.csproj' --configuration $config" 39 | Write-Host "# $cmd" -ForegroundColor Green 40 | Invoke-Expression $cmd 41 | if ($LASTEXITCODE -ne 0) { 42 | Write-Host "# Fix errors and re-run." -ForegroundColor Green 43 | return 44 | } 45 | 46 | $cmd = "mkdir '$root\publish\DDD' -Force | Out-Null" 47 | Write-Host "# $cmd" -ForegroundColor Green 48 | Invoke-Expression $cmd 49 | 50 | try { 51 | $cmd = "Copy-Item '$root\bin\$config\netcoreapp3.1\DDD.dll' '$root\publish\DDD' -ErrorAction Stop" 52 | Write-Host "# $cmd" -ForegroundColor Green 53 | Invoke-Expression $cmd 54 | } 55 | catch { 56 | Write-Host $_.Exception.Message -ForegroundColor Red 57 | Write-Host "Kill process to free file." -ForegroundColor Yellow 58 | return 59 | } 60 | 61 | $cmd = "Copy-Item '$root\assets\functions.ps1' '$root\publish\DDD'" 62 | Write-Host "# $cmd" -ForegroundColor Green 63 | Invoke-Expression $cmd 64 | 65 | $cmd = "Copy-Item '$root\assets\aliases.ps1' '$root\publish\DDD'" 66 | Write-Host "# $cmd" -ForegroundColor Green 67 | Invoke-Expression $cmd 68 | 69 | $manifestArgs = [ordered]@{ 70 | Path = "$root\publish\DDD\DDD.psd1" 71 | RootModule = $moduleName 72 | ModuleVersion = $version 73 | Author = $name 74 | CompanyName = $name 75 | Copyright = "Copyright = '(c) $name. All rights reserved.'" 76 | Description = $description 77 | RequiredAssemblies = $reqAssemblies 78 | ScriptsToProcess = $scripts 79 | CmdletsToExport = $cmdlets 80 | Tags = $tags 81 | AliasesToExport = $aliases 82 | } 83 | Write-Host '# $manifestArgs contents: ' -ForegroundColor Green 84 | $manifestArgs 85 | 86 | $cmd = "New-ModuleManifest @manifestArgs" 87 | Write-Host "# $cmd" -ForegroundColor Green 88 | Invoke-Expression $cmd 89 | 90 | if ($PowerShellGallery) { 91 | if (!$ApiKey) { 92 | Write-Host "No API Key provided. To publish, pass the API Key via -ApiKey option.`nTo generate an API Key: https://www.powershellgallery.com/account/apikeys" -ForegroundColor Yellow 93 | return 94 | } 95 | $cmd = "Publish-Module -Name '$root\publish\DDD' -NuGetApiKey $ApiKey" 96 | Write-Host "# $cmd" -ForegroundColor Green 97 | Invoke-Expression $cmd 98 | } 99 | else { 100 | $cmd = "Start-Process -FilePath 'pwsh' -ArgumentList '-NoExit -Command Import-Module -Force -Verbose $root\publish\DDD' -PassThru" 101 | Write-Host "# $cmd" -ForegroundColor Green 102 | $process = Invoke-Expression $cmd 103 | $global:pwshId = $process.Id 104 | Write-Host "# To run again..." -ForegroundColor Green 105 | Write-Host "# $($MyInvocation.InvocationName) -KillPrev" -ForegroundColor Green 106 | } -------------------------------------------------------------------------------- /DDD/Out-3d.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Management.Automation; // Windows PowerShell namespace 4 | using System.Runtime.InteropServices; 5 | // Defining input from the pipeline 6 | // https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/adding-parameters-that-process-pipeline-input?view=powershell-7 7 | 8 | // Samples here: 9 | // C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0\Samples\sysmgmt\WindowsPowerShell\ 10 | 11 | namespace DDD 12 | { 13 | #pragma warning disable CA1303 // Do not pass literals as localized parameters 14 | [Cmdlet(VerbsData.Out, "3d")] 15 | [Alias("o3d")] 16 | public class Out3dCommand : Cmdlet 17 | { 18 | List _objects = new List(); 19 | string _title = ""; 20 | Point _bboxMin = new Point(0.0, 0.0, 0.0); 21 | Point _bboxMax = new Point(0.0, 0.0, 0.0); 22 | 23 | [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] 24 | public object[] InputObject; 25 | 26 | [Parameter()] 27 | public string Title 28 | { 29 | get {return _title;} 30 | set {_title = value;} 31 | } 32 | 33 | protected override void BeginProcessing() 34 | { 35 | // Console.WriteLine("BeginProcessing"); 36 | } 37 | void UpdateBBox(Point p) 38 | { 39 | // bboxMin 40 | if (p.X < _bboxMin.X) _bboxMin.X = p.X; 41 | if (p.Y < _bboxMin.Y) _bboxMin.Y = p.Y; 42 | if (p.Z < _bboxMin.Z) _bboxMin.Z = p.Z; 43 | 44 | // bboxMax 45 | if (p.X > _bboxMax.X) _bboxMax.X = p.X; 46 | if (p.Y > _bboxMax.Y) _bboxMax.Y = p.Y; 47 | if (p.Z > _bboxMax.Z) _bboxMax.Z = p.Z; 48 | } 49 | void ProcessObject(object input) 50 | { 51 | if (input is Point p) 52 | { 53 | _objects.Add(input); 54 | UpdateBBox(p); 55 | // Console.WriteLine($"Got point: {p}"); 56 | } 57 | else if (input is Matrix m) 58 | { 59 | _objects.Add(input); 60 | Point origin = m * new Point(0.0, 0.0, 0.0); 61 | Point xaxis = m * new Point(1.0, 0.0, 0.0); 62 | Point yaxis = m * new Point(0.0, 1.0, 0.0); 63 | Point zaxis = m * new Point(0.0, 0.0, 1.0); 64 | UpdateBBox(origin); 65 | UpdateBBox(xaxis); 66 | UpdateBBox(yaxis); 67 | UpdateBBox(zaxis); 68 | // Console.WriteLine($"Got matrix: {origin}"); 69 | } 70 | else if (input is Vector v) 71 | { 72 | _objects.Add(input); 73 | UpdateBBox(new Point(v.X, v.Y, v.Z)); 74 | // Console.WriteLine($"Got vector: {v}"); 75 | } 76 | else 77 | { 78 | // Throw a terminating error for types that are not supported. 79 | ErrorRecord error = new ErrorRecord( 80 | new FormatException("Invalid data type for Out-3d"), 81 | "DataNotQualifiedFor3d", 82 | ErrorCategory.InvalidType, 83 | null); 84 | this.ThrowTerminatingError(error); 85 | } 86 | } 87 | protected override void ProcessRecord() 88 | { 89 | if (InputObject == null) 90 | { 91 | // Console.WriteLine("Got null"); 92 | } 93 | else if (InputObject.Length == 0) 94 | { 95 | // Console.WriteLine("Got empty array of type {0}", InputObject[0].GetType().ToString()); 96 | } 97 | else if (InputObject.Length == 1) 98 | { 99 | // Console.WriteLine("Got array length 1 of type {0}", InputObject[0].GetType().ToString()); 100 | ProcessObject(InputObject[0]); 101 | } 102 | else 103 | { 104 | // Console.WriteLine("Got array length {0} containing...", InputObject.Length); 105 | foreach(object obj in InputObject) 106 | { 107 | // Console.WriteLine(" Type: {0}", obj.GetType().ToString()); 108 | ProcessObject(obj); 109 | } 110 | } 111 | } 112 | protected override void EndProcessing() 113 | { 114 | if (_objects.Count == 0) return; 115 | 116 | // TODO: VERIFY EXCEPTIONS WORK 117 | try 118 | { 119 | // var ui = new UI(); 120 | // if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ui = new UIWindows(); 121 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 122 | { 123 | new UIWindows().ShowWindow(_objects, _bboxMin, _bboxMax, _title); 124 | } 125 | } 126 | catch (System.InvalidOperationException ioex) 127 | { 128 | ErrorRecord er = new ErrorRecord( 129 | new FormatException(ioex.Message), 130 | "LastError", 131 | ErrorCategory.NotSpecified, 132 | null); 133 | ThrowTerminatingError(er); 134 | } 135 | 136 | // Console.WriteLine("EndProcessing - DONE"); 137 | } 138 | } 139 | #pragma warning restore CA1303 // Do not pass literals as localized parameters 140 | } -------------------------------------------------------------------------------- /CPP2CSBindings/CPP2CSBindings.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A} 8 | Exe 9 | CPP2CSBindings 10 | CPP2CSBindings 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | publish\ 18 | true 19 | Disk 20 | false 21 | Foreground 22 | 7 23 | Days 24 | false 25 | false 26 | true 27 | 0 28 | 1.0.0.%2a 29 | false 30 | false 31 | true 32 | 33 | 34 | x64 35 | true 36 | full 37 | false 38 | bin\Debug\ 39 | DEBUG;TRACE 40 | prompt 41 | 4 42 | 43 | 44 | AnyCPU 45 | pdbonly 46 | true 47 | bin\Release\ 48 | TRACE 49 | prompt 50 | 4 51 | 52 | 53 | 54 | ..\packages\CppSharp.0.10.2\lib\CppSharp.dll 55 | 56 | 57 | ..\packages\CppSharp.0.10.2\lib\CppSharp.AST.dll 58 | 59 | 60 | ..\packages\CppSharp.0.10.2\lib\CppSharp.Generator.dll 61 | 62 | 63 | ..\packages\CppSharp.0.10.2\lib\CppSharp.Parser.dll 64 | 65 | 66 | ..\packages\CppSharp.0.10.2\lib\CppSharp.Parser.CLI.dll 67 | 68 | 69 | ..\packages\CppSharp.0.10.2\lib\CppSharp.Runtime.dll 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | False 91 | Microsoft .NET Framework 4.7.2 %28x86 and x64%29 92 | true 93 | 94 | 95 | False 96 | .NET Framework 3.5 SP1 97 | false 98 | 99 | 100 | 101 | 102 | 103 | 104 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /DDD/Vector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DDD 4 | { 5 | [Serializable()] 6 | public struct Vector : IEquatable 7 | { 8 | public double X; 9 | public double Y; 10 | public double Z; 11 | 12 | public Vector(double x, double y, double z) 13 | { 14 | X = x; 15 | Y = y; 16 | Z = z; 17 | } 18 | public Vector(Vector v) 19 | { 20 | X = v.X; 21 | Y = v.Y; 22 | Z = v.Z; 23 | } 24 | public Vector(double[] arr) 25 | { 26 | X = 0; 27 | Y = 0; 28 | Z = 0; 29 | if (arr == null) return; 30 | if (arr.Length > 0) X = arr[0]; 31 | if (arr.Length > 1) Y = arr[1]; 32 | if (arr.Length > 2) Z = arr[2]; 33 | } 34 | public Vector(string str) 35 | { 36 | char[] delimiterChars = { ' ', ',', '\t' }; 37 | char[] trimChars = { ' ', '(', ')', '[', ']', '{', '}', '<', '>', }; 38 | 39 | X = 0; 40 | Y = 0; 41 | Z = 0; 42 | if (str == null) return; 43 | string[] values = str.Trim(trimChars).Split(delimiterChars, StringSplitOptions.RemoveEmptyEntries); 44 | if (values.Length > 0) X = double.Parse(values[0], System.Globalization.CultureInfo.InvariantCulture); 45 | if (values.Length > 1) Y = double.Parse(values[1], System.Globalization.CultureInfo.InvariantCulture); 46 | if (values.Length > 2) Z = double.Parse(values[2], System.Globalization.CultureInfo.InvariantCulture); 47 | } 48 | public bool Equals(Vector v) => v == null ? false : (X == v.X) && (Y == v.Y) && (Z == v.Z); 49 | public override bool Equals(object obj) 50 | { 51 | if ((obj is null) || !GetType().Equals(obj.GetType())) 52 | { 53 | return false; 54 | } 55 | else 56 | { 57 | return Equals((Vector)obj); 58 | } 59 | } 60 | public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); 61 | public override string ToString() 62 | { 63 | // https://ss64.com/ps/syntax-f-operator.html 64 | // {0:#,0.##} 65 | // 0: Index 66 | // #,0 Group integers in 3's with commas, do not hide zero 67 | // .## Show at most 2 decimals, or nothing if no decimal point 68 | return String.Format(System.Globalization.CultureInfo.InvariantCulture, "[{0:#,0.##} {1:#,0.##} {2:#,0.##}]\n", X, Y, Z); 69 | } 70 | public static bool operator ==(Vector? a, Vector? b) => a is null ? b is null : a.Equals(b); 71 | public static bool operator !=(Vector? a, Vector? b) => !(a == b); 72 | public double Length() => Math.Sqrt(X * X + 73 | Y * Y + 74 | Z * Z); 75 | public Vector Normalize() 76 | { 77 | double length = Length(); 78 | X /= length; 79 | Y /= length; 80 | Z /= length; 81 | return this; 82 | } 83 | public static Vector operator +(Vector a, Vector b) => new Vector(a.X + b.X, 84 | a.Y + b.Y, 85 | a.Z + b.Z); 86 | public static Vector Add(Vector a, Vector b) => a + b; 87 | public static Point operator +(Point p, Vector v) => v + p; 88 | public static Point Add(Point p, Vector v) => p + v; 89 | public static Point operator +(Vector v, Point p) => new Point(v.X + p.X, 90 | v.Y + p.Y, 91 | v.Z + p.Z); 92 | public static Point Add(Vector v, Point p) => v + p; 93 | public static Vector operator -(Vector a, Vector b) => new Vector(a.X - b.X, 94 | a.Y - b.Y, 95 | a.Z - b.Z); 96 | 97 | public static Vector Subtract(Vector a, Vector b) => a - b; 98 | public static Vector operator *(Vector v, double s) => new Vector(v.X * s, 99 | v.Y * s, 100 | v.Z * s); 101 | public static Vector Multiply(Vector v, double s) => v * s; 102 | public static Vector operator *(double s, Vector v) => v * s; 103 | public static Vector Multiply(double s, Vector v) => s * v; 104 | public static Vector operator /(Vector v, double s) => new Vector(v.X / s, 105 | v.Y / s, 106 | v.Z / s); 107 | public static Vector Divide(Vector v, double s) => v / s; 108 | 109 | public static Vector operator -(Vector v) => new Vector(-v.X, 110 | -v.Y, 111 | -v.Z); 112 | public static Vector Negate(Vector v) => -v; 113 | 114 | public static double Dot(Vector a, Vector b) 115 | { 116 | return a.X * b.X + 117 | a.Y * b.Y + 118 | a.Z * b.Z; 119 | } 120 | public static Vector Cross(Vector a, Vector b) 121 | { 122 | return new Vector(a.Y * b.Z - a.Z * b.Y, 123 | a.Z * b.X - a.X * b.Z, 124 | a.X * b.Y - a.Y * b.X); 125 | } 126 | public static Vector Normalize(Vector v) 127 | { 128 | Vector u = new Vector(v); 129 | return u.Normalize(); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## LENIHAN START 2 | .vscode/ 3 | ## LENIHAN END 4 | 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 9 | 10 | # User-specific files 11 | *.rsuser 12 | *.suo 13 | *.user 14 | *.userosscache 15 | *.sln.docstates 16 | 17 | # User-specific files (MonoDevelop/Xamarin Studio) 18 | *.userprefs 19 | 20 | # Mono auto generated files 21 | mono_crash.* 22 | 23 | # Build results 24 | [Dd]ebug/ 25 | [Dd]ebugPublic/ 26 | [Rr]elease/ 27 | [Rr]eleases/ 28 | x64/ 29 | x86/ 30 | [Aa][Rr][Mm]/ 31 | [Aa][Rr][Mm]64/ 32 | bld/ 33 | [Bb]in/ 34 | [Oo]bj/ 35 | [Ll]og/ 36 | [Ll]ogs/ 37 | 38 | # Visual Studio 2015/2017 cache/options directory 39 | .vs/ 40 | # Uncomment if you have tasks that create the project's static files in wwwroot 41 | #wwwroot/ 42 | 43 | # Visual Studio 2017 auto generated files 44 | Generated\ Files/ 45 | 46 | # MSTest test Results 47 | [Tt]est[Rr]esult*/ 48 | [Bb]uild[Ll]og.* 49 | 50 | # NUnit 51 | *.VisualState.xml 52 | TestResult.xml 53 | nunit-*.xml 54 | 55 | # Build Results of an ATL Project 56 | [Dd]ebugPS/ 57 | [Rr]eleasePS/ 58 | dlldata.c 59 | 60 | # Benchmark Results 61 | BenchmarkDotNet.Artifacts/ 62 | 63 | # .NET Core 64 | project.lock.json 65 | project.fragment.lock.json 66 | artifacts/ 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.svclog 98 | *.scc 99 | 100 | # Chutzpah Test files 101 | _Chutzpah* 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opendb 108 | *.opensdf 109 | *.sdf 110 | *.cachefile 111 | *.VC.db 112 | *.VC.VC.opendb 113 | 114 | # Visual Studio profiler 115 | *.psess 116 | *.vsp 117 | *.vspx 118 | *.sap 119 | 120 | # Visual Studio Trace Files 121 | *.e2e 122 | 123 | # TFS 2012 Local Workspace 124 | $tf/ 125 | 126 | # Guidance Automation Toolkit 127 | *.gpState 128 | 129 | # ReSharper is a .NET coding add-in 130 | _ReSharper*/ 131 | *.[Rr]e[Ss]harper 132 | *.DotSettings.user 133 | 134 | # TeamCity is a build add-in 135 | _TeamCity* 136 | 137 | # DotCover is a Code Coverage Tool 138 | *.dotCover 139 | 140 | # AxoCover is a Code Coverage Tool 141 | .axoCover/* 142 | !.axoCover/settings.json 143 | 144 | # Visual Studio code coverage results 145 | *.coverage 146 | *.coveragexml 147 | 148 | # NCrunch 149 | _NCrunch_* 150 | .*crunch*.local.xml 151 | nCrunchTemp_* 152 | 153 | # MightyMoose 154 | *.mm.* 155 | AutoTest.Net/ 156 | 157 | # Web workbench (sass) 158 | .sass-cache/ 159 | 160 | # Installshield output folder 161 | [Ee]xpress/ 162 | 163 | # DocProject is a documentation generator add-in 164 | DocProject/buildhelp/ 165 | DocProject/Help/*.HxT 166 | DocProject/Help/*.HxC 167 | DocProject/Help/*.hhc 168 | DocProject/Help/*.hhk 169 | DocProject/Help/*.hhp 170 | DocProject/Help/Html2 171 | DocProject/Help/html 172 | 173 | # Click-Once directory 174 | publish/ 175 | 176 | # Publish Web Output 177 | *.[Pp]ublish.xml 178 | *.azurePubxml 179 | # Note: Comment the next line if you want to checkin your web deploy settings, 180 | # but database connection strings (with potential passwords) will be unencrypted 181 | *.pubxml 182 | *.publishproj 183 | 184 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 185 | # checkin your Azure Web App publish settings, but sensitive information contained 186 | # in these scripts will be unencrypted 187 | PublishScripts/ 188 | 189 | # NuGet Packages 190 | *.nupkg 191 | # NuGet Symbol Packages 192 | *.snupkg 193 | # The packages folder can be ignored because of Package Restore 194 | **/[Pp]ackages/* 195 | # except build/, which is used as an MSBuild target. 196 | !**/[Pp]ackages/build/ 197 | # Uncomment if necessary however generally it will be regenerated when needed 198 | #!**/[Pp]ackages/repositories.config 199 | # NuGet v3's project.json files produces more ignorable files 200 | *.nuget.props 201 | *.nuget.targets 202 | 203 | # Microsoft Azure Build Output 204 | csx/ 205 | *.build.csdef 206 | 207 | # Microsoft Azure Emulator 208 | ecf/ 209 | rcf/ 210 | 211 | # Windows Store app package directories and files 212 | AppPackages/ 213 | BundleArtifacts/ 214 | Package.StoreAssociation.xml 215 | _pkginfo.txt 216 | *.appx 217 | *.appxbundle 218 | *.appxupload 219 | 220 | # Visual Studio cache files 221 | # files ending in .cache can be ignored 222 | *.[Cc]ache 223 | # but keep track of directories ending in .cache 224 | !?*.[Cc]ache/ 225 | 226 | # Others 227 | ClientBin/ 228 | ~$* 229 | *~ 230 | *.dbmdl 231 | *.dbproj.schemaview 232 | *.jfm 233 | *.pfx 234 | *.publishsettings 235 | orleans.codegen.cs 236 | 237 | # Including strong name files can present a security risk 238 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 239 | #*.snk 240 | 241 | # Since there are multiple workflows, uncomment next line to ignore bower_components 242 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 243 | #bower_components/ 244 | 245 | # RIA/Silverlight projects 246 | Generated_Code/ 247 | 248 | # Backup & report files from converting an old project file 249 | # to a newer Visual Studio version. Backup files are not needed, 250 | # because we have git ;-) 251 | _UpgradeReport_Files/ 252 | Backup*/ 253 | UpgradeLog*.XML 254 | UpgradeLog*.htm 255 | ServiceFabricBackup/ 256 | *.rptproj.bak 257 | 258 | # SQL Server files 259 | *.mdf 260 | *.ldf 261 | *.ndf 262 | 263 | # Business Intelligence projects 264 | *.rdl.data 265 | *.bim.layout 266 | *.bim_*.settings 267 | *.rptproj.rsuser 268 | *- [Bb]ackup.rdl 269 | *- [Bb]ackup ([0-9]).rdl 270 | *- [Bb]ackup ([0-9][0-9]).rdl 271 | 272 | # Microsoft Fakes 273 | FakesAssemblies/ 274 | 275 | # GhostDoc plugin setting file 276 | *.GhostDoc.xml 277 | 278 | # Node.js Tools for Visual Studio 279 | .ntvs_analysis.dat 280 | node_modules/ 281 | 282 | # Visual Studio 6 build log 283 | *.plg 284 | 285 | # Visual Studio 6 workspace options file 286 | *.opt 287 | 288 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 289 | *.vbw 290 | 291 | # Visual Studio LightSwitch build output 292 | **/*.HTMLClient/GeneratedArtifacts 293 | **/*.DesktopClient/GeneratedArtifacts 294 | **/*.DesktopClient/ModelManifest.xml 295 | **/*.Server/GeneratedArtifacts 296 | **/*.Server/ModelManifest.xml 297 | _Pvt_Extensions 298 | 299 | # Paket dependency manager 300 | .paket/paket.exe 301 | paket-files/ 302 | 303 | # FAKE - F# Make 304 | .fake/ 305 | 306 | # CodeRush personal settings 307 | .cr/personal 308 | 309 | # Python Tools for Visual Studio (PTVS) 310 | __pycache__/ 311 | *.pyc 312 | 313 | # Cake - Uncomment if you are using it 314 | # tools/** 315 | # !tools/packages.config 316 | 317 | # Tabs Studio 318 | *.tss 319 | 320 | # Telerik's JustMock configuration file 321 | *.jmconfig 322 | 323 | # BizTalk build output 324 | *.btp.cs 325 | *.btm.cs 326 | *.odx.cs 327 | *.xsd.cs 328 | 329 | # OpenCover UI analysis results 330 | OpenCover/ 331 | 332 | # Azure Stream Analytics local run output 333 | ASALocalRun/ 334 | 335 | # MSBuild Binary and Structured Log 336 | *.binlog 337 | 338 | # NVidia Nsight GPU debugger configuration file 339 | *.nvuser 340 | 341 | # MFractors (Xamarin productivity tool) working folder 342 | .mfractor/ 343 | 344 | # Local History for Visual Studio 345 | .localhistory/ 346 | 347 | # BeatPulse healthcheck temp database 348 | healthchecksdb 349 | 350 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 351 | MigrationBackup/ 352 | 353 | # Ionide (cross platform F# VS Code tools) working folder 354 | .ionide/ 355 | -------------------------------------------------------------------------------- /Win32-OpenGL/Win32-OpenGL.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 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75} 24 | Win32Proj 25 | Win32OpenGL 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 | NotSet 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | NotSet 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 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | C:\Users\David\src\vcpkg\packages\opengl_x64-windows\debug\lib\OpenGL32.Lib;%(AdditionalDependencies) 112 | 113 | 114 | 115 | 116 | 117 | 118 | Level3 119 | true 120 | true 121 | true 122 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 123 | true 124 | 125 | 126 | Console 127 | true 128 | true 129 | true 130 | 131 | 132 | 133 | 134 | 135 | 136 | Level3 137 | true 138 | true 139 | true 140 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | true 142 | 143 | 144 | Console 145 | true 146 | true 147 | true 148 | C:\Users\David\src\vcpkg\packages\opengl_x64-windows\lib\OpenGL32.Lib;%(AdditionalDependencies) 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /UnitTest-Point/UnitTest_Point.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | namespace DDD_UnitTest 4 | { 5 | [TestClass] 6 | public class Point 7 | { 8 | [TestMethod] 9 | public void ConstructorNoArgs() 10 | { 11 | DDD.Point p = new DDD.Point(); 12 | Assert.IsTrue(p.ToString() == "(0 0 0)\n"); 13 | } 14 | [TestMethod] 15 | public void ConstructorWithXYZ() 16 | { 17 | DDD.Point p = new DDD.Point(4, 2, 1); 18 | Assert.IsTrue(p.ToString() == "(4 2 1)\n"); 19 | } 20 | [TestMethod] 21 | public void ConstructorWithPoint() 22 | { 23 | DDD.Point p = new DDD.Point(new DDD.Point(1, 2, 3)); 24 | Assert.IsTrue(p.ToString() == "(1 2 3)\n"); 25 | } 26 | [TestMethod] 27 | public void ConstructorWithNullArray() 28 | { 29 | double[] arr = null; 30 | DDD.Point p = new DDD.Point(arr); 31 | Assert.IsTrue(p.ToString() == "(0 0 0)\n"); 32 | } 33 | [TestMethod] 34 | public void ConstructorWithEmptyArray() 35 | { 36 | double[] arr = System.Array.Empty(); 37 | DDD.Point p = new DDD.Point(arr); 38 | Assert.IsTrue(p.ToString() == "(0 0 0)\n"); 39 | } 40 | [TestMethod] 41 | public void ConstructorWith1ElementArray() 42 | { 43 | double[] arr = { 10 }; 44 | DDD.Point p = new DDD.Point(arr); 45 | Assert.IsTrue(p.ToString() == "(10 0 0)\n"); 46 | } 47 | [TestMethod] 48 | public void ConstructorWith2ElementArray() 49 | { 50 | double[] arr = { 10, 20 }; 51 | DDD.Point p = new DDD.Point(arr); 52 | Assert.IsTrue(p.ToString() == "(10 20 0)\n"); 53 | } 54 | [TestMethod] 55 | public void ConstructorWith3ElementArray() 56 | { 57 | double[] arr = { 30, 20, 10 }; 58 | DDD.Point p = new DDD.Point(arr); 59 | Assert.IsTrue(p.ToString() == "(30 20 10)\n"); 60 | } 61 | [TestMethod] 62 | public void ConstructorWith4ElementArray() 63 | { 64 | double[] arr = { 40, 30, 20, 10 }; 65 | DDD.Point p = new DDD.Point(arr); 66 | Assert.IsTrue(p.ToString() == "(40 30 20)\n"); 67 | } 68 | [TestMethod] 69 | public void ConstructorWithNullString() 70 | { 71 | string str = null; 72 | DDD.Point p = new DDD.Point(str); 73 | Assert.IsTrue(p.ToString() == "(0 0 0)\n"); 74 | } 75 | [TestMethod] 76 | public void ConstructorWithEmptyString() 77 | { 78 | string str = ""; 79 | DDD.Point p = new DDD.Point(str); 80 | Assert.IsTrue(p.ToString() == "(0 0 0)\n"); 81 | } 82 | [TestMethod] 83 | public void ConstructorWith1ElementString() 84 | { 85 | string str = "1"; 86 | DDD.Point p = new DDD.Point(str); 87 | Assert.IsTrue(p.ToString() == "(1 0 0)\n"); 88 | } 89 | [TestMethod] 90 | public void ConstructorWith2ElementString() 91 | { 92 | string str = "1 10"; 93 | DDD.Point p = new DDD.Point(str); 94 | Assert.IsTrue(p.ToString() == "(1 10 0)\n"); 95 | } 96 | [TestMethod] 97 | public void ConstructorWith3ElementString() 98 | { 99 | string str = "1 2 3"; 100 | DDD.Point p = new DDD.Point(str); 101 | Assert.IsTrue(p.ToString() == "(1 2 3)\n"); 102 | } 103 | [TestMethod] 104 | public void ConstructorWith4ElementString() 105 | { 106 | string str = "3 2 1 9"; 107 | DDD.Point p = new DDD.Point(str); 108 | Assert.IsTrue(p.ToString() == "(3 2 1)\n"); 109 | } 110 | [TestMethod] 111 | public void ConstructorWithtStringSpaceDelimiters() 112 | { 113 | string str = "11 22 33"; 114 | DDD.Point p = new DDD.Point(str); 115 | Assert.IsTrue(p.ToString() == "(11 22 33)\n"); 116 | } 117 | [TestMethod] 118 | public void ConstructorWithtStringCommaDelimiters() 119 | { 120 | string str = "11,22,33"; 121 | DDD.Point p = new DDD.Point(str); 122 | Assert.IsTrue(p.ToString() == "(11 22 33)\n"); 123 | } 124 | [TestMethod] 125 | public void ConstructorWithtStringTabDelimiters() 126 | { 127 | string str = "11\t22\t33"; 128 | DDD.Point p = new DDD.Point(str); 129 | Assert.IsTrue(p.ToString() == "(11 22 33)\n"); 130 | } 131 | [TestMethod] 132 | public void ConstructorWithtStringMultipleDelimiters() 133 | { 134 | string str = "11\t\t ,22,, \t33"; 135 | DDD.Point p = new DDD.Point(str); 136 | Assert.IsTrue(p.ToString() == "(11 22 33)\n"); 137 | } 138 | [TestMethod] 139 | public void ConstructorWithtStringTrimCharacters() 140 | { 141 | string str = " <{([ 11 22 33 ])}> "; 142 | DDD.Point p = new DDD.Point(str); 143 | Assert.IsTrue(p.ToString() == "(11 22 33)\n"); 144 | } 145 | 146 | [TestMethod] 147 | public void TestEquals() 148 | { 149 | DDD.Point? p1 = new DDD.Point(1, 2, 3); 150 | DDD.Point? p2 = null; 151 | DDD.Point? p3 = new DDD.Point(1, 2, 3); 152 | DDD.Point? p4 = new DDD.Point(4, 5, 6); 153 | DDD.Point? p5 = null; 154 | 155 | Assert.IsTrue(p1.Equals(p3)); 156 | Assert.IsTrue(!p1.Equals(p4)); 157 | 158 | Assert.IsTrue(!p1.Equals((object)p2)); 159 | Assert.IsTrue(!p1.Equals((object)123)); 160 | Assert.IsTrue(p1.Equals((object)p3)); 161 | Assert.IsTrue(!p1.Equals((object)p4)); 162 | 163 | Assert.IsTrue(p2 == p5); 164 | Assert.IsTrue(p1 == p3); 165 | 166 | Assert.IsTrue(p1 != p2); 167 | Assert.IsTrue(p1 != p4); 168 | } 169 | [TestMethod] 170 | public void TestGetHashCode() 171 | { 172 | DDD.Point p = new DDD.Point(2.3, -3.112, 4.123); 173 | int ans = 1370492436; // from previous run 174 | Assert.IsTrue(p.GetHashCode() == ans); 175 | } 176 | [TestMethod] 177 | public void TestToString() 178 | { 179 | DDD.Point p = new DDD.Point(123456789, 123456789.12345, 1000000000000); 180 | Assert.IsTrue(p.ToString() == "(123,456,789 123,456,789.12 1,000,000,000,000)\n"); 181 | } 182 | [TestMethod] 183 | public void TestAdd() 184 | { 185 | DDD.Point p1 = new DDD.Point(1, 2, 3); 186 | DDD.Point p2 = new DDD.Point(3, 2, 1); 187 | Assert.IsTrue((p1 + p2).ToString() == "(4 4 4)\n"); 188 | Assert.IsTrue(DDD.Point.Add(p1,p2).ToString() == "(4 4 4)\n"); 189 | } 190 | [TestMethod] 191 | public void TestSubtract() 192 | { 193 | DDD.Point p1 = new DDD.Point(1, 2, 3); 194 | DDD.Point p2 = new DDD.Point(3, 2, 1); 195 | Assert.IsTrue((p1 - p2).ToString() == "[-2 0 2]\n"); 196 | Assert.IsTrue(DDD.Point.Subtract(p1, p2).ToString() == "[-2 0 2]\n"); 197 | } 198 | [TestMethod] 199 | public void TestMultiplyPre() 200 | { 201 | DDD.Point p = new DDD.Point(1, 2, 3); 202 | Assert.IsTrue((p * 2).ToString() == "(2 4 6)\n"); 203 | Assert.IsTrue(DDD.Point.Multiply(p, 2).ToString() == "(2 4 6)\n"); 204 | } 205 | [TestMethod] 206 | public void TestMultiplyPost() 207 | { 208 | DDD.Point p = new DDD.Point(1, 2, 3); 209 | Assert.IsTrue((2 * p).ToString() == "(2 4 6)\n"); 210 | Assert.IsTrue(DDD.Point.Multiply(2, p).ToString() == "(2 4 6)\n"); 211 | } 212 | [TestMethod] 213 | public void TestDivide() 214 | { 215 | DDD.Point p = new DDD.Point(10, 20, 30); 216 | Assert.IsTrue((p / 10).ToString() == "(1 2 3)\n"); 217 | Assert.IsTrue(DDD.Point.Divide(p, 10).ToString() == "(1 2 3)\n"); 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /DDD.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29806.167 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DDD", "DDD\DDD.csproj", "{F8F9847C-3B88-4540-AC27-D63368ADA462}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "UnitTest-Point\UnitTest.csproj", "{5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{13005639-F3A0-4695-98E0-F363FF669B97}" 11 | ProjectSection(SolutionItems) = preProject 12 | .editorconfig = .editorconfig 13 | EndProjectSection 14 | EndProject 15 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Win32-OpenGL", "Win32-OpenGL\Win32-OpenGL.vcxproj", "{2D69347A-B1F1-4087-8F9E-9B0596F32E75}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore-OpenGL", "DotNetCore-OpenGL\DotNetCore-OpenGL.csproj", "{E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}" 18 | EndProject 19 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Win32-OpenGL-DLL", "Win32-OpenGL-DLL\Win32-OpenGL-DLL.vcxproj", "{9CCE90DA-C194-479F-8026-7D3CF7C13396}" 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CPP2CSBindings", "CPP2CSBindings\CPP2CSBindings.csproj", "{2C4C3D4D-88C8-4B29-9103-AE4806B7801A}" 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prototype-CSWindow-Windows", "Prototype-CSWindow-Windows\Prototype-CSWindow-Windows.csproj", "{099F3F91-46B0-4CDE-98E3-5801BB6CC678}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Debug|x64 = Debug|x64 29 | Debug|x86 = Debug|x86 30 | Release|Any CPU = Release|Any CPU 31 | Release|x64 = Release|x64 32 | Release|x86 = Release|x86 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Debug|x64.ActiveCfg = Debug|Any CPU 38 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Debug|x64.Build.0 = Debug|Any CPU 39 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Debug|x86.ActiveCfg = Debug|Any CPU 40 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Debug|x86.Build.0 = Debug|Any CPU 41 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Release|x64.ActiveCfg = Release|Any CPU 44 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Release|x64.Build.0 = Release|Any CPU 45 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Release|x86.ActiveCfg = Release|Any CPU 46 | {F8F9847C-3B88-4540-AC27-D63368ADA462}.Release|x86.Build.0 = Release|Any CPU 47 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Debug|x64.ActiveCfg = Debug|Any CPU 50 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Debug|x64.Build.0 = Debug|Any CPU 51 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Debug|x86.ActiveCfg = Debug|Any CPU 52 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Debug|x86.Build.0 = Debug|Any CPU 53 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Release|x64.ActiveCfg = Release|Any CPU 56 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Release|x64.Build.0 = Release|Any CPU 57 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Release|x86.ActiveCfg = Release|Any CPU 58 | {5D2F0086-F964-4212-BBF3-E3DBCF28A8FD}.Release|x86.Build.0 = Release|Any CPU 59 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Debug|Any CPU.ActiveCfg = Debug|Win32 60 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Debug|x64.ActiveCfg = Debug|x64 61 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Debug|x64.Build.0 = Debug|x64 62 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Debug|x86.ActiveCfg = Debug|Win32 63 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Debug|x86.Build.0 = Debug|Win32 64 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Release|Any CPU.ActiveCfg = Release|Win32 65 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Release|x64.ActiveCfg = Release|x64 66 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Release|x64.Build.0 = Release|x64 67 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Release|x86.ActiveCfg = Release|Win32 68 | {2D69347A-B1F1-4087-8F9E-9B0596F32E75}.Release|x86.Build.0 = Release|Win32 69 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 70 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Debug|Any CPU.Build.0 = Debug|Any CPU 71 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Debug|x64.ActiveCfg = Debug|Any CPU 72 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Debug|x64.Build.0 = Debug|Any CPU 73 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Debug|x86.ActiveCfg = Debug|Any CPU 74 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Debug|x86.Build.0 = Debug|Any CPU 75 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Release|Any CPU.ActiveCfg = Release|Any CPU 76 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Release|Any CPU.Build.0 = Release|Any CPU 77 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Release|x64.ActiveCfg = Release|Any CPU 78 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Release|x64.Build.0 = Release|Any CPU 79 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Release|x86.ActiveCfg = Release|Any CPU 80 | {E7FE1D1B-4FA1-41F0-870E-B8DCD76EA666}.Release|x86.Build.0 = Release|Any CPU 81 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Debug|Any CPU.ActiveCfg = Debug|Win32 82 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Debug|x64.ActiveCfg = Debug|x64 83 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Debug|x64.Build.0 = Debug|x64 84 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Debug|x86.ActiveCfg = Debug|Win32 85 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Debug|x86.Build.0 = Debug|Win32 86 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Release|Any CPU.ActiveCfg = Release|Win32 87 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Release|x64.ActiveCfg = Release|x64 88 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Release|x64.Build.0 = Release|x64 89 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Release|x86.ActiveCfg = Release|Win32 90 | {9CCE90DA-C194-479F-8026-7D3CF7C13396}.Release|x86.Build.0 = Release|Win32 91 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 92 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Debug|x64.ActiveCfg = Debug|Any CPU 93 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Debug|x64.Build.0 = Debug|Any CPU 94 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Debug|x86.ActiveCfg = Debug|Any CPU 95 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Release|Any CPU.ActiveCfg = Release|Any CPU 96 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Release|Any CPU.Build.0 = Release|Any CPU 97 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Release|x64.ActiveCfg = Release|Any CPU 98 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Release|x64.Build.0 = Release|Any CPU 99 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Release|x86.ActiveCfg = Release|Any CPU 100 | {2C4C3D4D-88C8-4B29-9103-AE4806B7801A}.Release|x86.Build.0 = Release|Any CPU 101 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 102 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Debug|Any CPU.Build.0 = Debug|Any CPU 103 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Debug|x64.ActiveCfg = Debug|Any CPU 104 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Debug|x64.Build.0 = Debug|Any CPU 105 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Debug|x86.ActiveCfg = Debug|Any CPU 106 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Debug|x86.Build.0 = Debug|Any CPU 107 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Release|Any CPU.ActiveCfg = Release|Any CPU 108 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Release|Any CPU.Build.0 = Release|Any CPU 109 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Release|x64.ActiveCfg = Release|Any CPU 110 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Release|x64.Build.0 = Release|Any CPU 111 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Release|x86.ActiveCfg = Release|Any CPU 112 | {099F3F91-46B0-4CDE-98E3-5801BB6CC678}.Release|x86.Build.0 = Release|Any CPU 113 | EndGlobalSection 114 | GlobalSection(SolutionProperties) = preSolution 115 | HideSolutionNode = FALSE 116 | EndGlobalSection 117 | GlobalSection(ExtensibilityGlobals) = postSolution 118 | SolutionGuid = {A57B008A-62EC-4B0D-BD4F-574DC1C59A8C} 119 | EndGlobalSection 120 | EndGlobal 121 | -------------------------------------------------------------------------------- /Win32-OpenGL-DLL/Win32-OpenGL-DLL.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 | {9CCE90DA-C194-479F-8026-7D3CF7C13396} 24 | Win32Proj 25 | Win32OpenGLDLL 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v142 46 | NotSet 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | NotSet 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 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Use 88 | Level3 89 | true 90 | _DEBUG;WIN32OPENGLDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 91 | true 92 | pch.h 93 | 94 | 95 | Windows 96 | true 97 | false 98 | C:\Users\David\src\vcpkg\packages\opengl_x64-windows\debug\lib\OpenGL32.Lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 99 | 100 | 101 | 102 | 103 | Use 104 | Level3 105 | true 106 | WIN32;_DEBUG;WIN32OPENGLDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 107 | true 108 | pch.h 109 | 110 | 111 | Windows 112 | true 113 | false 114 | 115 | 116 | 117 | 118 | Use 119 | Level3 120 | true 121 | true 122 | true 123 | WIN32;NDEBUG;WIN32OPENGLDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 124 | true 125 | pch.h 126 | 127 | 128 | Windows 129 | true 130 | true 131 | true 132 | false 133 | 134 | 135 | 136 | 137 | Use 138 | Level3 139 | true 140 | true 141 | true 142 | NDEBUG;WIN32OPENGLDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 143 | true 144 | pch.h 145 | 146 | 147 | Windows 148 | true 149 | true 150 | true 151 | false 152 | C:\Users\David\src\vcpkg\packages\opengl_x64-windows\lib\OpenGL32.Lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | Create 163 | Create 164 | Create 165 | Create 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /Win32-OpenGL/Win32-OpenGL.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* An example of an OpenGL animation loop using the Win32 API. Also 3 | demonstrates palette management for RGB and color index modes and 4 | general strategies for message handling. */ 5 | 6 | 7 | #include /* must include this before GL/gl.h */ 8 | #include /* OpenGL header file */ 9 | #include 10 | 11 | 12 | HDC hDC; /* device context */ 13 | HPALETTE hPalette = 0; /* custom palette (if needed) */ 14 | GLboolean animate = GL_TRUE; /* animation flag */ 15 | 16 | 17 | void 18 | display() 19 | { 20 | /* rotate a triangle around */ 21 | 22 | //glClearColor(1.0f, 0.0f, 0.0f, 1.0f); 23 | glClear(GL_COLOR_BUFFER_BIT); 24 | if (animate) 25 | glRotatef(1.0f, 0.0f, 0.0f, 1.0f); 26 | glBegin(GL_TRIANGLES); 27 | glIndexi(1); 28 | glColor3f(1.0f, 0.0f, 0.0f); 29 | glVertex2i(0, 1); 30 | glIndexi(2); 31 | glColor3f(0.0f, 1.0f, 0.0f); 32 | glVertex2i(-1, -1); 33 | glIndexi(3); 34 | glColor3f(0.0f, 0.0f, 1.0f); 35 | glVertex2i(1, -1); 36 | glEnd(); 37 | glFlush(); 38 | SwapBuffers(hDC); /* nop if singlebuffered */ 39 | } 40 | 41 | LRESULT WINAPI 42 | WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 43 | { 44 | static PAINTSTRUCT ps; 45 | 46 | switch (uMsg) { 47 | case WM_PAINT: 48 | display(); 49 | //BeginPaint(hWnd, &ps); 50 | //EndPaint(hWnd, &ps); 51 | return 0; 52 | 53 | case WM_SIZE: 54 | glViewport(0, 0, LOWORD(lParam), HIWORD(lParam)); 55 | //PostMessage(hWnd, WM_PAINT, 0, 0); 56 | return 0; 57 | 58 | //case WM_CHAR: 59 | // switch (wParam) { 60 | // case 27: /* ESC key */ 61 | // PostQuitMessage(0); 62 | // break; 63 | // case ' ': 64 | // animate = !animate; 65 | // break; 66 | // } 67 | // return 0; 68 | 69 | //case WM_ACTIVATE: 70 | // if (IsIconic(hWnd)) 71 | // animate = GL_FALSE; 72 | // else 73 | // animate = GL_TRUE; 74 | // return 0; 75 | 76 | //case WM_PALETTECHANGED: 77 | // if (hWnd == (HWND)wParam) 78 | // break; 79 | // /* fall through to WM_QUERYNEWPALETTE */ 80 | 81 | //case WM_QUERYNEWPALETTE: 82 | // if (hPalette) { 83 | // UnrealizeObject(hPalette); 84 | // SelectPalette(hDC, hPalette, FALSE); 85 | // RealizePalette(hDC); 86 | // return TRUE; 87 | // } 88 | // return FALSE; 89 | 90 | //case WM_CLOSE: 91 | // PostQuitMessage(0); 92 | // return 0; 93 | } 94 | 95 | return DefWindowProc(hWnd, uMsg, wParam, lParam); 96 | } 97 | 98 | 99 | HWND 100 | CreateOpenGLWindow(char* title, int x, int y, int width, int height, 101 | BYTE type, DWORD flags) 102 | { 103 | //int n; 104 | int pf; 105 | HWND hWnd; 106 | WNDCLASS wc; 107 | //LOGPALETTE* lpPal; 108 | PIXELFORMATDESCRIPTOR pfd; 109 | static HINSTANCE hInstance = 0; 110 | 111 | /* only register the window class once - use hInstance as a flag. */ 112 | if (!hInstance) { 113 | hInstance = GetModuleHandle(NULL); 114 | wc.style = CS_OWNDC; 115 | wc.lpfnWndProc = (WNDPROC)WindowProc; 116 | wc.cbClsExtra = 0; 117 | wc.cbWndExtra = 0; 118 | wc.hInstance = hInstance; 119 | wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); 120 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 121 | wc.hbrBackground = NULL; 122 | wc.lpszMenuName = NULL; 123 | wc.lpszClassName = "OpenGL"; 124 | 125 | if (!RegisterClass(&wc)) { 126 | MessageBox(NULL, "RegisterClass() failed: " 127 | "Cannot register window class.", "Error", MB_OK); 128 | return NULL; 129 | } 130 | } 131 | 132 | hWnd = CreateWindow("OpenGL", title, WS_OVERLAPPEDWINDOW | 133 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 134 | x, y, width, height, NULL, NULL, hInstance, NULL); 135 | 136 | if (hWnd == NULL) { 137 | MessageBox(NULL, "CreateWindow() failed: Cannot create a window.", 138 | "Error", MB_OK); 139 | return NULL; 140 | } 141 | 142 | hDC = GetDC(hWnd); 143 | 144 | /* there is no guarantee that the contents of the stack that become 145 | the pfd are zeroed, therefore _make sure_ to clear these bits. */ 146 | memset(&pfd, 0, sizeof(pfd)); 147 | pfd.nSize = sizeof(pfd); 148 | pfd.nVersion = 1; 149 | pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | flags; 150 | pfd.iPixelType = type; 151 | pfd.cColorBits = 32; 152 | 153 | // LENIHAN - track down issue in sandbox 154 | 155 | int maxIndex = DescribePixelFormat(hDC, 1, sizeof(PIXELFORMATDESCRIPTOR), &pfd); 156 | printf("maxIndex = %d\n", maxIndex); 157 | printf("size of PIXELFORMATDESCRIPTOR = %d\n", (int)sizeof(PIXELFORMATDESCRIPTOR)); 158 | 159 | pf = ChoosePixelFormat(hDC, &pfd); 160 | printf("ChoosePixelFormat returns %d\n", pf); 161 | 162 | int beforePf = GetPixelFormat(hDC); 163 | printf("beforePf = %d\n", beforePf); 164 | 165 | // LENIHAN - track down issue in sandbox 166 | 167 | 168 | 169 | 170 | 171 | pf = ChoosePixelFormat(hDC, &pfd); 172 | if (pf == 0) { 173 | MessageBox(NULL, "ChoosePixelFormat() failed: " 174 | "Cannot find a suitable pixel format.", "Error", MB_OK); 175 | return 0; 176 | } 177 | 178 | if (SetPixelFormat(hDC, pf, &pfd) == FALSE) { 179 | MessageBox(NULL, "SetPixelFormat() failed: " 180 | "Cannot set format specified.", "Error", MB_OK); 181 | return 0; 182 | } 183 | 184 | 185 | int afterPf = GetPixelFormat(hDC); 186 | printf("afterPf = %d\n", afterPf); 187 | 188 | 189 | //DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd); 190 | 191 | //if (pfd.dwFlags & PFD_NEED_PALETTE || 192 | // pfd.iPixelType == PFD_TYPE_COLORINDEX) { 193 | 194 | // n = 1 << pfd.cColorBits; 195 | // if (n > 256) n = 256; 196 | 197 | // lpPal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + 198 | // sizeof(PALETTEENTRY) * n); 199 | // memset(lpPal, 0, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n); 200 | // lpPal->palVersion = 0x300; 201 | // lpPal->palNumEntries = n; 202 | 203 | // GetSystemPaletteEntries(hDC, 0, n, &lpPal->palPalEntry[0]); 204 | 205 | // /* if the pixel type is RGBA, then we want to make an RGB ramp, 206 | // otherwise (color index) set individual colors. */ 207 | // if (pfd.iPixelType == PFD_TYPE_RGBA) { 208 | // int redMask = (1 << pfd.cRedBits) - 1; 209 | // int greenMask = (1 << pfd.cGreenBits) - 1; 210 | // int blueMask = (1 << pfd.cBlueBits) - 1; 211 | // int i; 212 | 213 | // /* fill in the entries with an RGB color ramp. */ 214 | // for (i = 0; i < n; ++i) { 215 | // lpPal->palPalEntry[i].peRed = 216 | // (((i >> pfd.cRedShift) & redMask) * 255) / redMask; 217 | // lpPal->palPalEntry[i].peGreen = 218 | // (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask; 219 | // lpPal->palPalEntry[i].peBlue = 220 | // (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask; 221 | // lpPal->palPalEntry[i].peFlags = 0; 222 | // } 223 | // } 224 | // else { 225 | // lpPal->palPalEntry[0].peRed = 0; 226 | // lpPal->palPalEntry[0].peGreen = 0; 227 | // lpPal->palPalEntry[0].peBlue = 0; 228 | // lpPal->palPalEntry[0].peFlags = PC_NOCOLLAPSE; 229 | // lpPal->palPalEntry[1].peRed = 255; 230 | // lpPal->palPalEntry[1].peGreen = 0; 231 | // lpPal->palPalEntry[1].peBlue = 0; 232 | // lpPal->palPalEntry[1].peFlags = PC_NOCOLLAPSE; 233 | // lpPal->palPalEntry[2].peRed = 0; 234 | // lpPal->palPalEntry[2].peGreen = 255; 235 | // lpPal->palPalEntry[2].peBlue = 0; 236 | // lpPal->palPalEntry[2].peFlags = PC_NOCOLLAPSE; 237 | // lpPal->palPalEntry[3].peRed = 0; 238 | // lpPal->palPalEntry[3].peGreen = 0; 239 | // lpPal->palPalEntry[3].peBlue = 255; 240 | // lpPal->palPalEntry[3].peFlags = PC_NOCOLLAPSE; 241 | // } 242 | 243 | // hPalette = CreatePalette(lpPal); 244 | // if (hPalette) { 245 | // SelectPalette(hDC, hPalette, FALSE); 246 | // RealizePalette(hDC); 247 | // } 248 | 249 | // free(lpPal); 250 | //} 251 | 252 | //ReleaseDC(hWnd, hDC); 253 | 254 | return hWnd; 255 | } 256 | 257 | int APIENTRY 258 | WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, 259 | LPSTR lpszCmdLine, int nCmdShow) 260 | { 261 | HGLRC hRC; /* opengl context */ 262 | HWND hWnd; /* window */ 263 | MSG msg; /* message */ 264 | DWORD buffer = PFD_DOUBLEBUFFER; /* buffering type */ 265 | BYTE color = PFD_TYPE_RGBA; /* color type */ 266 | 267 | int size = sizeof(TOUCHINPUT); 268 | 269 | //if (strstr(lpszCmdLine, "-sb")) { 270 | // buffer = 0; 271 | //} 272 | //if (strstr(lpszCmdLine, "-ci")) { 273 | // color = PFD_TYPE_COLORINDEX; 274 | //} 275 | //if (strstr(lpszCmdLine, "-h")) { 276 | // MessageBox(NULL, "animate [-ci] [-sb]\n" 277 | // " -sb single buffered\n" 278 | // " -ci color index\n", 279 | // "Usage help", MB_ICONINFORMATION); 280 | // exit(0); 281 | //} 282 | 283 | hWnd = CreateOpenGLWindow((char *)"animate", 0, 0, 256, 256, color, buffer); 284 | if (hWnd == NULL) 285 | exit(1); 286 | 287 | hDC = GetDC(hWnd); 288 | hRC = wglCreateContext(hDC); 289 | wglMakeCurrent(hDC, hRC); 290 | 291 | ShowWindow(hWnd, SW_SHOW); 292 | //UpdateWindow(hWnd); 293 | 294 | while (1) { 295 | while (PeekMessage(&msg, hWnd, 0, 0, PM_NOREMOVE)) { 296 | if (GetMessage(&msg, hWnd, 0, 0)) { 297 | TranslateMessage(&msg); 298 | DispatchMessage(&msg); 299 | } 300 | else { 301 | goto quit; 302 | } 303 | } 304 | display(); 305 | } 306 | 307 | quit: 308 | 309 | wglMakeCurrent(NULL, NULL); 310 | ReleaseDC(hWnd, hDC); 311 | wglDeleteContext(hRC); 312 | DestroyWindow(hWnd); 313 | if (hPalette) 314 | DeleteObject(hPalette); 315 | 316 | return 0; 317 | } 318 | 319 | 320 | int main() 321 | { 322 | return WinMain(GetModuleHandle(NULL), NULL, (char *)"", 0); 323 | } -------------------------------------------------------------------------------- /UnitTest-Point/UnitTest_Vector.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | namespace DDD_UnitTest 4 | { 5 | [TestClass] 6 | public class Vector 7 | { 8 | [TestMethod] 9 | public void ConstructorNoArgs() 10 | { 11 | DDD.Vector v = new DDD.Vector(); 12 | Assert.IsTrue(v.ToString() == "[0 0 0]\n"); 13 | } 14 | [TestMethod] 15 | public void ConstructorWithXYZ() 16 | { 17 | DDD.Vector v = new DDD.Vector(4, 2, 1); 18 | Assert.IsTrue(v.ToString() == "[4 2 1]\n"); 19 | } 20 | [TestMethod] 21 | public void ConstructorWithVector() 22 | { 23 | DDD.Vector v = new DDD.Vector(new DDD.Vector(1, 2, 3)); 24 | Assert.IsTrue(v.ToString() == "[1 2 3]\n"); 25 | } 26 | [TestMethod] 27 | public void ConstructorWithNullArray() 28 | { 29 | double[] arr = null; 30 | DDD.Vector v = new DDD.Vector(arr); 31 | Assert.IsTrue(v.ToString() == "[0 0 0]\n"); 32 | } 33 | static public void ConstructorWithEmptyArray() 34 | { 35 | double[] arr = System.Array.Empty(); 36 | DDD.Vector v = new DDD.Vector(arr); 37 | Assert.IsTrue(v.ToString() == "[0 0 0]\n"); 38 | } 39 | [TestMethod] 40 | public void ConstructorWith1ElementArray() 41 | { 42 | double[] arr = { 10 }; 43 | DDD.Vector v = new DDD.Vector(arr); 44 | Assert.IsTrue(v.ToString() == "[10 0 0]\n"); 45 | } 46 | [TestMethod] 47 | public void ConstructorWith2ElementArray() 48 | { 49 | double[] arr = { 10, 20 }; 50 | DDD.Vector v = new DDD.Vector(arr); 51 | Assert.IsTrue(v.ToString() == "[10 20 0]\n"); 52 | } 53 | [TestMethod] 54 | public void ConstructorWith3ElementArray() 55 | { 56 | double[] arr = { 30, 20, 10 }; 57 | DDD.Vector v = new DDD.Vector(arr); 58 | Assert.IsTrue(v.ToString() == "[30 20 10]\n"); 59 | } 60 | [TestMethod] 61 | public void ConstructorWith4ElementArray() 62 | { 63 | double[] arr = { 40, 30, 20, 10 }; 64 | DDD.Vector v = new DDD.Vector(arr); 65 | Assert.IsTrue(v.ToString() == "[40 30 20]\n"); 66 | } 67 | [TestMethod] 68 | public void ConstructorWithNullString() 69 | { 70 | string str = null; 71 | DDD.Vector v = new DDD.Vector(str); 72 | Assert.IsTrue(v.ToString() == "[0 0 0]\n"); 73 | } 74 | [TestMethod] 75 | public void ConstructorWithEmptyString() 76 | { 77 | string str = ""; 78 | DDD.Vector v = new DDD.Vector(str); 79 | Assert.IsTrue(v.ToString() == "[0 0 0]\n"); 80 | } 81 | [TestMethod] 82 | public void ConstructorWith1ElementString() 83 | { 84 | string str = "1"; 85 | DDD.Vector v = new DDD.Vector(str); 86 | Assert.IsTrue(v.ToString() == "[1 0 0]\n"); 87 | } 88 | [TestMethod] 89 | public void ConstructorWith2ElementString() 90 | { 91 | string str = "1 10"; 92 | DDD.Vector v = new DDD.Vector(str); 93 | Assert.IsTrue(v.ToString() == "[1 10 0]\n"); 94 | } 95 | [TestMethod] 96 | public void ConstructorWith3ElementString() 97 | { 98 | string str = "1 2 3"; 99 | DDD.Vector v = new DDD.Vector(str); 100 | Assert.IsTrue(v.ToString() == "[1 2 3]\n"); 101 | } 102 | [TestMethod] 103 | public void ConstructorWith4ElementString() 104 | { 105 | string str = "3 2 1 9"; 106 | DDD.Vector v = new DDD.Vector(str); 107 | Assert.IsTrue(v.ToString() == "[3 2 1]\n"); 108 | } 109 | [TestMethod] 110 | public void ConstructorWithtStringSpaceDelimiters() 111 | { 112 | string str = "11 22 33"; 113 | DDD.Vector v = new DDD.Vector(str); 114 | Assert.IsTrue(v.ToString() == "[11 22 33]\n"); 115 | } 116 | [TestMethod] 117 | public void ConstructorWithtStringCommaDelimiters() 118 | { 119 | string str = "11,22,33"; 120 | DDD.Vector v = new DDD.Vector(str); 121 | Assert.IsTrue(v.ToString() == "[11 22 33]\n"); 122 | } 123 | [TestMethod] 124 | public void ConstructorWithtStringTabDelimiters() 125 | { 126 | string str = "11\t22\t33"; 127 | DDD.Vector v = new DDD.Vector(str); 128 | Assert.IsTrue(v.ToString() == "[11 22 33]\n"); 129 | } 130 | [TestMethod] 131 | public void ConstructorWithtStringMultipleDelimiters() 132 | { 133 | string str = "11\t\t ,22,, \t33"; 134 | DDD.Vector v = new DDD.Vector(str); 135 | Assert.IsTrue(v.ToString() == "[11 22 33]\n"); 136 | } 137 | [TestMethod] 138 | public void ConstructorWithtStringTrimCharacters() 139 | { 140 | string str = " <{([ 11 22 33 ])}> "; 141 | DDD.Vector v = new DDD.Vector(str); 142 | Assert.IsTrue(v.ToString() == "[11 22 33]\n"); 143 | } 144 | [TestMethod] 145 | public void TestEquals() 146 | { 147 | DDD.Vector? v1 = new DDD.Vector(1, 2, 3); 148 | DDD.Vector? v2 = null; 149 | DDD.Vector? v3 = new DDD.Vector(1, 2, 3); 150 | DDD.Vector? v4 = new DDD.Vector(4, 5, 6); 151 | DDD.Vector? v5 = null; 152 | 153 | Assert.IsTrue(v1.Equals(v3)); 154 | Assert.IsTrue(!v1.Equals(v4)); 155 | 156 | Assert.IsTrue(!v1.Equals((object)v2)); 157 | Assert.IsTrue(!v1.Equals((object)123)); 158 | Assert.IsTrue(v1.Equals((object)v3)); 159 | Assert.IsTrue(!v1.Equals((object)v4)); 160 | 161 | Assert.IsTrue(v2 == v5); 162 | Assert.IsTrue(v1 == v3); 163 | 164 | Assert.IsTrue(v1 != v2); 165 | Assert.IsTrue(v1 != v4); 166 | } 167 | [TestMethod] 168 | public void TestGetHashCode() 169 | { 170 | DDD.Vector p = new DDD.Vector(2.3, -3.112, 4.123); 171 | int ans = 1370492436; // from previous run 172 | Assert.IsTrue(p.GetHashCode() == ans); 173 | } 174 | [TestMethod] 175 | public void TestToString() 176 | { 177 | DDD.Vector v = new DDD.Vector(123456789, 123456789.12345, 1000000000000); 178 | Assert.IsTrue(v.ToString() == "[123,456,789 123,456,789.12 1,000,000,000,000]\n"); 179 | } 180 | [TestMethod] 181 | public void TestLength() 182 | { 183 | DDD.Vector v = new DDD.Vector(1, 2, 2); 184 | Assert.IsTrue(v.Length() == 3); 185 | } 186 | [TestMethod] 187 | public void TestNormalize() 188 | { 189 | DDD.Vector v = new DDD.Vector(1, 2, 3); 190 | var vn = v.Normalize(); 191 | var str = vn.ToString(); 192 | Assert.IsTrue(str == "[0.27 0.53 0.8]\n"); 193 | Assert.IsTrue(v.ToString() == "[0.27 0.53 0.8]\n"); 194 | Assert.IsTrue(v.X == 0.2672612419124244); 195 | Assert.IsTrue(v.Y == 0.53452248382484879); 196 | Assert.IsTrue(v.Z == 0.80178372573727319); 197 | } 198 | [TestMethod] 199 | public void TestNormalizeStatic() 200 | { 201 | 202 | DDD.Vector v = new DDD.Vector(1, 2, 3); 203 | Assert.IsTrue(DDD.Vector.Normalize(v).ToString() == "[0.27 0.53 0.8]\n"); 204 | } 205 | [TestMethod] 206 | public void TestAddVectorVector() 207 | { 208 | DDD.Vector v1 = new DDD.Vector(1, 2, 3); 209 | DDD.Vector v2 = new DDD.Vector(6, -2, 8); 210 | Assert.IsTrue((v1 + v2).ToString() == "[7 0 11]\n"); 211 | Assert.IsTrue(DDD.Vector.Add(v1, v2).ToString() == "[7 0 11]\n"); 212 | } 213 | [TestMethod] 214 | public void TestAddVectorPoint() 215 | { 216 | DDD.Vector v = new DDD.Vector(1, 2, 3); 217 | DDD.Point p = new DDD.Point(1, 2, 3); 218 | Assert.IsTrue((v + p).ToString() == "(2 4 6)\n"); 219 | Assert.IsTrue(DDD.Vector.Add(v, p).ToString() == "(2 4 6)\n"); 220 | } 221 | [TestMethod] 222 | public void TestAddPointVector() 223 | { 224 | DDD.Vector v = new DDD.Vector(1, 2, 3); 225 | DDD.Point p = new DDD.Point(1, 2, 3); 226 | Assert.IsTrue((p + v).ToString() == "(2 4 6)\n"); 227 | Assert.IsTrue(DDD.Vector.Add(p, v).ToString() == "(2 4 6)\n"); 228 | } 229 | [TestMethod] 230 | public void TestSubtractVectorVector() 231 | { 232 | DDD.Vector v1 = new DDD.Vector(4, 5, 6); 233 | DDD.Vector v2 = new DDD.Vector(3, 2, 1); 234 | Assert.IsTrue((v1 - v2).ToString() == "[1 3 5]\n"); 235 | Assert.IsTrue(DDD.Vector.Subtract(v1, v2).ToString() == "[1 3 5]\n"); 236 | } 237 | [TestMethod] 238 | public void TestMultiplyVectorScaler() 239 | { 240 | DDD.Vector v = new DDD.Vector(1, 2, 3); 241 | Assert.IsTrue((v * 2).ToString() == "[2 4 6]\n"); 242 | Assert.IsTrue(DDD.Vector.Multiply(v, 2).ToString() == "[2 4 6]\n"); 243 | } 244 | [TestMethod] 245 | public void TestMultiplyScalerVector() 246 | { 247 | DDD.Vector v = new DDD.Vector(1, 2, 3); 248 | Assert.IsTrue((2 * v).ToString() == "[2 4 6]\n"); 249 | Assert.IsTrue(DDD.Vector.Multiply(2, v).ToString() == "[2 4 6]\n"); 250 | } 251 | [TestMethod] 252 | public void TestDivideVectorScaler() 253 | { 254 | DDD.Vector v = new DDD.Vector(1, 2, 3); 255 | Assert.IsTrue((v / 2).ToString() == "[0.5 1 1.5]\n"); 256 | Assert.IsTrue(DDD.Vector.Divide(v, 2).ToString() == "[0.5 1 1.5]\n"); 257 | } 258 | [TestMethod] 259 | public void TestNegate() 260 | { 261 | DDD.Vector v = new DDD.Vector(1, 2, 3); 262 | Assert.IsTrue((-v).ToString() == "[-1 -2 -3]\n"); 263 | Assert.IsTrue(DDD.Vector.Negate(v).ToString() == "[-1 -2 -3]\n"); 264 | } 265 | [TestMethod] 266 | public void TestDot() 267 | { 268 | DDD.Vector v1 = new DDD.Vector(1, 2, 3); 269 | DDD.Vector v2 = new DDD.Vector(10, 11, 12); 270 | // Verified by http://onlinemschool.com/math/assistance/vector/multiply/ 271 | Assert.IsTrue(DDD.Vector.Dot(v1, v2) == 68); 272 | } 273 | [TestMethod] 274 | public void TestCross() 275 | { 276 | DDD.Vector v1 = new DDD.Vector(1, 2, 3); 277 | DDD.Vector v2 = new DDD.Vector(10, 11, 12); 278 | // Verified by https://www.symbolab.com/solver/Vector-cross-product-calculator 279 | Assert.IsTrue(DDD.Vector.Cross(v1, v2).ToString() == "[-9 18 -9]\n"); 280 | } 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /Win32-OpenGL-DLL/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "pch.h" 3 | 4 | BOOL APIENTRY DllMain( HMODULE hModule, 5 | DWORD ul_reason_for_call, 6 | LPVOID lpReserved 7 | ) 8 | { 9 | switch (ul_reason_for_call) 10 | { 11 | case DLL_PROCESS_ATTACH: 12 | case DLL_THREAD_ATTACH: 13 | case DLL_THREAD_DETACH: 14 | case DLL_PROCESS_DETACH: 15 | break; 16 | } 17 | return TRUE; 18 | } 19 | 20 | 21 | /* An example of an OpenGL animation loop using the Win32 API. Also 22 | demonstrates palette management for RGB and color index modes and 23 | general strategies for message handling. */ 24 | 25 | 26 | #include /* must include this before GL/gl.h */ 27 | #include /* OpenGL header file */ 28 | #include 29 | #include 30 | 31 | 32 | HDC hDC; /* device context */ 33 | HPALETTE hPalette = 0; /* custom palette (if needed) */ 34 | GLboolean animate = GL_TRUE; /* animation flag */ 35 | 36 | 37 | //void 38 | //display() 39 | //{ 40 | // /* rotate a triangle around */ 41 | // glClear(GL_COLOR_BUFFER_BIT); 42 | // if (animate) 43 | // glRotatef(1.0f, 0.0f, 0.0f, 1.0f); 44 | // glBegin(GL_TRIANGLES); 45 | // glIndexi(1); 46 | // glColor3f(1.0f, 0.0f, 0.0f); 47 | // glVertex2i(0, 1); 48 | // glIndexi(2); 49 | // glColor3f(0.0f, 1.0f, 0.0f); 50 | // glVertex2i(-1, -1); 51 | // glIndexi(3); 52 | // glColor3f(0.0f, 0.0f, 1.0f); 53 | // glVertex2i(1, -1); 54 | // glEnd(); 55 | // glFlush(); 56 | // SwapBuffers(hDC); /* nop if singlebuffered */ 57 | //} 58 | 59 | void 60 | display() 61 | { 62 | 63 | GLfloat sizes[2]; // Store supported point size range 64 | GLfloat step; // Store supported point size increments 65 | glGetFloatv(GL_POINT_SIZE_RANGE, sizes); 66 | glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step); 67 | glPointSize(sizes[1]); 68 | glEnable(GL_POINT_SMOOTH); 69 | 70 | 71 | /* rotate a triangle around */ 72 | glClear(GL_COLOR_BUFFER_BIT); 73 | if (animate) 74 | glRotatef(1.0f, 0.0f, 0.0f, 1.0f); 75 | glBegin(GL_POINTS); 76 | //glIndexi(1); 77 | glColor3ub(255, 0, 0); // red 78 | glVertex3d(0.0, 1.0, 0.0); 79 | //glIndexi(2); 80 | glColor3ub(0, 255, 0); // green 81 | glVertex3d(0.1, 0.1, 0.1); 82 | //glIndexi(3); 83 | glColor3ub(0, 0, 255); // blue 84 | glVertex3d(0.5, 0.5, 0.0); 85 | glEnd(); 86 | glFlush(); 87 | SwapBuffers(hDC); /* nop if singlebuffered */ 88 | } 89 | 90 | 91 | LRESULT WINAPI 92 | WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 93 | { 94 | static PAINTSTRUCT ps; 95 | 96 | switch (uMsg) { 97 | case WM_PAINT: 98 | display(); 99 | BeginPaint(hWnd, &ps); 100 | EndPaint(hWnd, &ps); 101 | return 0; 102 | 103 | case WM_SIZE: 104 | glViewport(0, 0, LOWORD(lParam), HIWORD(lParam)); 105 | PostMessage(hWnd, WM_PAINT, 0, 0); 106 | return 0; 107 | 108 | case WM_CHAR: 109 | switch (wParam) { 110 | case 27: /* ESC key */ 111 | PostQuitMessage(0); 112 | break; 113 | case ' ': 114 | animate = !animate; 115 | break; 116 | } 117 | return 0; 118 | 119 | case WM_ACTIVATE: 120 | if (IsIconic(hWnd)) 121 | animate = GL_FALSE; 122 | else 123 | animate = GL_TRUE; 124 | return 0; 125 | 126 | case WM_PALETTECHANGED: 127 | if (hWnd == (HWND)wParam) 128 | break; 129 | /* fall through to WM_QUERYNEWPALETTE */ 130 | 131 | case WM_QUERYNEWPALETTE: 132 | if (hPalette) { 133 | UnrealizeObject(hPalette); 134 | SelectPalette(hDC, hPalette, FALSE); 135 | RealizePalette(hDC); 136 | return TRUE; 137 | } 138 | return FALSE; 139 | 140 | case WM_CLOSE: 141 | PostQuitMessage(0); 142 | return 0; 143 | } 144 | 145 | return DefWindowProc(hWnd, uMsg, wParam, lParam); 146 | } 147 | 148 | HWND 149 | CreateOpenGLWindow(char* title, int x, int y, int width, int height, 150 | BYTE type, DWORD flags) 151 | { 152 | int n, pf; 153 | HWND hWnd; 154 | WNDCLASS wc; 155 | LOGPALETTE* lpPal; 156 | PIXELFORMATDESCRIPTOR pfd; 157 | static HINSTANCE hInstance = 0; 158 | 159 | /* only register the window class once - use hInstance as a flag. */ 160 | if (!hInstance) { 161 | hInstance = GetModuleHandle(NULL); 162 | wc.style = CS_OWNDC; 163 | wc.lpfnWndProc = (WNDPROC)WindowProc; 164 | wc.cbClsExtra = 0; 165 | wc.cbWndExtra = 0; 166 | wc.hInstance = hInstance; 167 | wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); 168 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 169 | wc.hbrBackground = NULL; 170 | wc.lpszMenuName = NULL; 171 | wc.lpszClassName = "OpenGL"; 172 | 173 | if (!RegisterClass(&wc)) { 174 | MessageBox(NULL, "RegisterClass() failed: " 175 | "Cannot register window class.", "Error", MB_OK); 176 | return NULL; 177 | } 178 | } 179 | 180 | hWnd = CreateWindow("OpenGL", title, WS_OVERLAPPEDWINDOW | 181 | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 182 | x, y, width, height, NULL, NULL, hInstance, NULL); 183 | 184 | if (hWnd == NULL) { 185 | MessageBox(NULL, "CreateWindow() failed: Cannot create a window.", 186 | "Error", MB_OK); 187 | return NULL; 188 | } 189 | 190 | hDC = GetDC(hWnd); 191 | 192 | /* there is no guarantee that the contents of the stack that become 193 | the pfd are zeroed, therefore _make sure_ to clear these bits. */ 194 | memset(&pfd, 0, sizeof(pfd)); 195 | pfd.nSize = sizeof(pfd); 196 | pfd.nVersion = 1; 197 | pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | flags; 198 | pfd.iPixelType = type; 199 | pfd.cColorBits = 32; 200 | 201 | pf = ChoosePixelFormat(hDC, &pfd); 202 | if (pf == 0) { 203 | MessageBox(NULL, "ChoosePixelFormat() failed: " 204 | "Cannot find a suitable pixel format.", "Error", MB_OK); 205 | return 0; 206 | } 207 | 208 | if (SetPixelFormat(hDC, pf, &pfd) == FALSE) { 209 | MessageBox(NULL, "SetPixelFormat() failed: " 210 | "Cannot set format specified.", "Error", MB_OK); 211 | return 0; 212 | } 213 | 214 | DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd); 215 | 216 | if (pfd.dwFlags & PFD_NEED_PALETTE || 217 | pfd.iPixelType == PFD_TYPE_COLORINDEX) { 218 | 219 | n = 1 << pfd.cColorBits; 220 | if (n > 256) n = 256; 221 | 222 | lpPal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + 223 | sizeof(PALETTEENTRY) * n); 224 | memset(lpPal, 0, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n); 225 | lpPal->palVersion = 0x300; 226 | lpPal->palNumEntries = n; 227 | 228 | GetSystemPaletteEntries(hDC, 0, n, &lpPal->palPalEntry[0]); 229 | 230 | /* if the pixel type is RGBA, then we want to make an RGB ramp, 231 | otherwise (color index) set individual colors. */ 232 | if (pfd.iPixelType == PFD_TYPE_RGBA) { 233 | int redMask = (1 << pfd.cRedBits) - 1; 234 | int greenMask = (1 << pfd.cGreenBits) - 1; 235 | int blueMask = (1 << pfd.cBlueBits) - 1; 236 | int i; 237 | 238 | /* fill in the entries with an RGB color ramp. */ 239 | for (i = 0; i < n; ++i) { 240 | lpPal->palPalEntry[i].peRed = 241 | (((i >> pfd.cRedShift) & redMask) * 255) / redMask; 242 | lpPal->palPalEntry[i].peGreen = 243 | (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask; 244 | lpPal->palPalEntry[i].peBlue = 245 | (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask; 246 | lpPal->palPalEntry[i].peFlags = 0; 247 | } 248 | } 249 | else { 250 | lpPal->palPalEntry[0].peRed = 0; 251 | lpPal->palPalEntry[0].peGreen = 0; 252 | lpPal->palPalEntry[0].peBlue = 0; 253 | lpPal->palPalEntry[0].peFlags = PC_NOCOLLAPSE; 254 | lpPal->palPalEntry[1].peRed = 255; 255 | lpPal->palPalEntry[1].peGreen = 0; 256 | lpPal->palPalEntry[1].peBlue = 0; 257 | lpPal->palPalEntry[1].peFlags = PC_NOCOLLAPSE; 258 | lpPal->palPalEntry[2].peRed = 0; 259 | lpPal->palPalEntry[2].peGreen = 255; 260 | lpPal->palPalEntry[2].peBlue = 0; 261 | lpPal->palPalEntry[2].peFlags = PC_NOCOLLAPSE; 262 | lpPal->palPalEntry[3].peRed = 0; 263 | lpPal->palPalEntry[3].peGreen = 0; 264 | lpPal->palPalEntry[3].peBlue = 255; 265 | lpPal->palPalEntry[3].peFlags = PC_NOCOLLAPSE; 266 | } 267 | 268 | hPalette = CreatePalette(lpPal); 269 | if (hPalette) { 270 | SelectPalette(hDC, hPalette, FALSE); 271 | RealizePalette(hDC); 272 | } 273 | 274 | free(lpPal); 275 | } 276 | 277 | ReleaseDC(hWnd, hDC); 278 | 279 | return hWnd; 280 | } 281 | 282 | int APIENTRY 283 | WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, 284 | LPSTR lpszCmdLine, int nCmdShow) 285 | { 286 | HGLRC hRC; /* opengl context */ 287 | HWND hWnd; /* window */ 288 | MSG msg; /* message */ 289 | DWORD buffer = PFD_DOUBLEBUFFER; /* buffering type */ 290 | BYTE color = PFD_TYPE_RGBA; /* color type */ 291 | 292 | //if (strstr(lpszCmdLine, "-sb")) { 293 | // buffer = 0; 294 | //} 295 | //if (strstr(lpszCmdLine, "-ci")) { 296 | // color = PFD_TYPE_COLORINDEX; 297 | //} 298 | //if (strstr(lpszCmdLine, "-h")) { 299 | // MessageBox(NULL, "animate [-ci] [-sb]\n" 300 | // " -sb single buffered\n" 301 | // " -ci color index\n", 302 | // "Usage help", MB_ICONINFORMATION); 303 | // exit(0); 304 | //} 305 | 306 | hWnd = CreateOpenGLWindow((char*)"animate", 0, 0, 256, 256, color, buffer); 307 | if (hWnd == NULL) 308 | return 1; // exit(1); 309 | 310 | hDC = GetDC(hWnd); 311 | hRC = wglCreateContext(hDC); 312 | wglMakeCurrent(hDC, hRC); 313 | 314 | ShowWindow(hWnd, SW_SHOW); 315 | UpdateWindow(hWnd); 316 | 317 | while (1) { 318 | while (PeekMessage(&msg, hWnd, 0, 0, PM_NOREMOVE)) { 319 | if (GetMessage(&msg, hWnd, 0, 0)) { 320 | TranslateMessage(&msg); 321 | DispatchMessage(&msg); 322 | } 323 | else { 324 | goto quit; 325 | } 326 | } 327 | display(); 328 | } 329 | 330 | quit: 331 | 332 | wglMakeCurrent(NULL, NULL); 333 | ReleaseDC(hWnd, hDC); 334 | wglDeleteContext(hRC); 335 | DestroyWindow(hWnd); 336 | if (hPalette) 337 | DeleteObject(hPalette); 338 | 339 | return 0; 340 | } 341 | 342 | 343 | __declspec(dllexport) int main() 344 | { 345 | return WinMain(GetModuleHandle(NULL), NULL, (char*)"", 0); 346 | } 347 | -------------------------------------------------------------------------------- /DDD/Matrix.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DDD 4 | { 5 | [Serializable()] 6 | public struct Matrix : IEquatable 7 | { 8 | // Storing Matrix in row major: mXY. For example, m23 is the second row, third column 9 | public double M11, M12, M13, M14; 10 | public double M21, M22, M23, M24; 11 | public double M31, M32, M33, M34; 12 | public double M41, M42, M43, M44; 13 | 14 | // API inspired by DirectX 9.0: https://msdn.microsoft.com/en-us/library/windows/desktop/bb281696(v=vs.85).aspx 15 | public Matrix(double m11, double m12, double m13, double m14, 16 | double m21, double m22, double m23, double m24, 17 | double m31, double m32, double m33, double m34, 18 | double m41, double m42, double m43, double m44) 19 | { 20 | M11 = m11; M12 = m12; M13 = m13; M14 = m14; 21 | M21 = m21; M22 = m22; M23 = m23; M24 = m24; 22 | M31 = m31; M32 = m32; M33 = m33; M34 = m34; 23 | M41 = m41; M42 = m42; M43 = m43; M44 = m44; 24 | } 25 | public Matrix(double m11, double m12, double m13, 26 | double m21, double m22, double m23, 27 | double m31, double m32, double m33) 28 | { 29 | M11 = m11; M12 = m12; M13 = m13; M14 = 0; 30 | M21 = m21; M22 = m22; M23 = m23; M24 = 0; 31 | M31 = m31; M32 = m32; M33 = m33; M34 = 0; 32 | M41 = 0; M42 = 0; M43 = 0; M44 = 1; 33 | } 34 | public Matrix(Matrix mat) 35 | { 36 | M11 = mat.M11; M12 = mat.M12; M13 = mat.M13; M14 = mat.M14; 37 | M21 = mat.M21; M22 = mat.M22; M23 = mat.M23; M24 = mat.M24; 38 | M31 = mat.M31; M32 = mat.M32; M33 = mat.M33; M34 = mat.M34; 39 | M41 = mat.M41; M42 = mat.M42; M43 = mat.M43; M44 = mat.M44; 40 | } 41 | public Matrix(double[] arr) 42 | { 43 | M11 = 0; M12 = 0; M13 = 0; M14 = 0; 44 | M21 = 0; M22 = 0; M23 = 0; M24 = 0; 45 | M31 = 0; M32 = 0; M33 = 0; M34 = 0; 46 | M41 = 0; M42 = 0; M43 = 0; M44 = 0; 47 | if (arr == null) 48 | { 49 | return; 50 | } 51 | else if (arr.Length == 16) 52 | { 53 | M11 = arr[0]; M12 = arr[1]; M13 = arr[2]; M14 = arr[3]; 54 | M21 = arr[4]; M22 = arr[5]; M23 = arr[6]; M24 = arr[7]; 55 | M31 = arr[8]; M32 = arr[9]; M33 = arr[10]; M34 = arr[11]; 56 | M41 = arr[12]; M42 = arr[13]; M43 = arr[14]; M44 = arr[15]; 57 | } 58 | else if (arr.Length == 9) 59 | { 60 | M11 = arr[0]; M12 = arr[1]; M13 = arr[2]; M14 = 0; 61 | M21 = arr[3]; M22 = arr[4]; M23 = arr[5]; M24 = 0; 62 | M31 = arr[6]; M32 = arr[7]; M33 = arr[8]; M34 = 0; 63 | M41 = 0; M42 = 0; M43 = 0; M44 = 1; 64 | } 65 | } 66 | public Matrix(Vector xAxis, Vector yAxis, Vector zAxis) 67 | { 68 | M11 = xAxis.X; M12 = yAxis.X; M13 = zAxis.X; M14 = 0; 69 | M21 = xAxis.Y; M22 = yAxis.Y; M23 = zAxis.Y; M24 = 0; 70 | M31 = xAxis.Z; M32 = yAxis.Z; M33 = zAxis.Z; M34 = 0; 71 | M41 = 0; M42 = 0; M43 = 0; M44 = 1; 72 | } 73 | public bool Equals(Matrix m) => m == null ? false : (M11 == m.M11) && (M12 == m.M12) && (M13 == m.M13) && (M14 == m.M14) && 74 | (M21 == m.M21) && (M22 == m.M22) && (M23 == m.M23) && (M24 == m.M24) && 75 | (M31 == m.M31) && (M32 == m.M32) && (M33 == m.M33) && (M34 == m.M34) && 76 | (M41 == m.M41) && (M42 == m.M42) && (M43 == m.M43) && (M44 == m.M44); 77 | public override bool Equals(object obj) 78 | { 79 | if ((obj is null) || !GetType().Equals(obj.GetType())) 80 | { 81 | return false; 82 | } 83 | else 84 | { 85 | return Equals((Matrix)obj); 86 | } 87 | } 88 | public override int GetHashCode() 89 | { 90 | int row1 = M11.GetHashCode() ^ M12.GetHashCode() ^ M13.GetHashCode() ^ M14.GetHashCode(); 91 | int row2 = M21.GetHashCode() ^ M22.GetHashCode() ^ M23.GetHashCode() ^ M24.GetHashCode(); 92 | int row3 = M31.GetHashCode() ^ M32.GetHashCode() ^ M33.GetHashCode() ^ M34.GetHashCode(); 93 | int row4 = M41.GetHashCode() ^ M42.GetHashCode() ^ M43.GetHashCode() ^ M44.GetHashCode(); 94 | return row1 ^ row2 ^ row3 ^ row4; 95 | } 96 | 97 | public override string ToString() 98 | { 99 | // https://ss64.com/ps/syntax-f-operator.html 100 | // {0,16:#,0.##} 101 | // 0: Index 102 | // ,16 Use at least 16 characters, right justified. Chose 16 to fit 1,234,567,890.12 103 | // #,0 Group integers in 3's with commas, do not hide zero 104 | // .## Show at most 2 decimals, or nothing if no decimal point 105 | string row1 = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0,16:#,0.##} {1,16:#,0.##} {2,16:#,0.##} {3,16:#,0.##}\n", M11, M12, M13, M14); 106 | string row2 = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0,16:#,0.##} {1,16:#,0.##} {2,16:#,0.##} {3,16:#,0.##}\n", M21, M22, M23, M24); 107 | string row3 = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0,16:#,0.##} {1,16:#,0.##} {2,16:#,0.##} {3,16:#,0.##}\n", M31, M32, M33, M34); 108 | string row4 = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0,16:#,0.##} {1,16:#,0.##} {2,16:#,0.##} {3,16:#,0.##}\n", M41, M42, M43, M44); 109 | return row1 + row2 + row3 + row4; 110 | } 111 | public static bool operator ==(Matrix? a, Matrix? b) => a is null ? b is null : a.Equals(b); 112 | public static bool operator !=(Matrix? a, Matrix? b) => !(a == b); 113 | public double Determinate() => Determinate4x4(M11, M12, M13, M14, 114 | M21, M22, M23, M24, 115 | M31, M32, M33, M34, 116 | M41, M42, M43, M44); 117 | public Matrix Invert() 118 | { 119 | // https://en.wikipedia.org/wiki/Invertible_matrix 120 | double det = Determinate(); 121 | Matrix adj = Adjugate(); 122 | Matrix inv = (1 / det) * adj; 123 | M11 = inv.M11; M12 = inv.M12; M13 = inv.M13; M14 = inv.M14; 124 | M21 = inv.M21; M22 = inv.M22; M23 = inv.M23; M24 = inv.M24; 125 | M31 = inv.M31; M32 = inv.M32; M33 = inv.M33; M34 = inv.M34; 126 | M41 = inv.M41; M42 = inv.M42; M43 = inv.M43; M44 = inv.M44; 127 | return this; 128 | } 129 | public Matrix Transpose() 130 | { 131 | double a = M11; double b = M12; double c = M13; double d = M14; 132 | double e = M21; double f = M22; double g = M23; double h = M24; 133 | double i = M31; double j = M32; double k = M33; double l = M34; 134 | double m = M41; double n = M42; double o = M43; double p = M44; 135 | 136 | M11 = a; M12 = e; M13 = i; M14 = m; 137 | M21 = b; M22 = f; M23 = j; M24 = n; 138 | M31 = c; M32 = g; M33 = k; M34 = o; 139 | M41 = d; M42 = h; M43 = l; M44 = p; 140 | return this; 141 | } 142 | public Matrix Adjugate() 143 | { 144 | // https://en.wikipedia.org/wiki/Adjugate_matrix 145 | double a = M11; double b = M12; double c = M13; double d = M14; 146 | double e = M21; double f = M22; double g = M23; double h = M24; 147 | double i = M31; double j = M32; double k = M33; double l = M34; 148 | double m = M41; double n = M42; double o = M43; double p = M44; 149 | 150 | double detA = Determinant3x3(f, g, h, 151 | j, k, l, 152 | n, o, p); 153 | double detB = Determinant3x3(e, g, h, 154 | i, k, l, 155 | m, o, p); 156 | double detC = Determinant3x3(e, f, h, 157 | i, j, l, 158 | m, n, p); 159 | double detD = Determinant3x3(e, f, g, 160 | i, j, k, 161 | m, n, o); 162 | double detE = Determinant3x3(b, c, d, 163 | j, k, l, 164 | n, o, p); 165 | double detF = Determinant3x3(a, c, d, 166 | i, k, l, 167 | m, o, p); 168 | double detG = Determinant3x3(a, b, d, 169 | i, j, l, 170 | m, n, p); 171 | double detH = Determinant3x3(a, b, c, 172 | i, j, k, 173 | m, n, o); 174 | double detI = Determinant3x3(b, c, d, 175 | f, g, h, 176 | n, o, p); 177 | double detJ = Determinant3x3(a, c, d, 178 | e, g, h, 179 | m, o, p); 180 | double detK = Determinant3x3(a, b, d, 181 | e, f, h, 182 | m, n, p); 183 | double detL = Determinant3x3(a, b, c, 184 | e, f, g, 185 | m, n, o); 186 | double detM = Determinant3x3(b, c, d, 187 | f, g, h, 188 | j, k, l); 189 | double detN = Determinant3x3(a, c, d, 190 | e, g, h, 191 | i, k, l); 192 | double detO = Determinant3x3(a, b, d, 193 | e, f, h, 194 | i, j, l); 195 | double detP = Determinant3x3(a, b, c, 196 | e, f, g, 197 | i, j, k); 198 | Matrix mat = new Matrix(+detA, -detB, +detC, -detD, 199 | -detE, +detF, -detG, +detH, 200 | +detI, -detJ, +detK, -detL, 201 | -detM, +detN, -detO, +detP); 202 | return mat.Transpose(); 203 | } 204 | public static Matrix operator *(Matrix a, Matrix b) 205 | { 206 | // https://en.wikipedia.org/wiki/Matrix_multiplication#Definition 207 | // row 1 208 | double c11 = a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31 + a.M14 * b.M41; 209 | double c12 = a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32 + a.M14 * b.M42; 210 | double c13 = a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33 + a.M14 * b.M43; 211 | double c14 = a.M11 * b.M14 + a.M12 * b.M24 + a.M13 * b.M34 + a.M14 * b.M44; 212 | 213 | // row 2 214 | double c21 = a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31 + a.M24 * b.M41; 215 | double c22 = a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32 + a.M24 * b.M42; 216 | double c23 = a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33 + a.M24 * b.M43; 217 | double c24 = a.M21 * b.M14 + a.M22 * b.M24 + a.M23 * b.M34 + a.M24 * b.M44; 218 | 219 | // row 3 220 | double c31 = a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31 + a.M34 * b.M41; 221 | double c32 = a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32 + a.M34 * b.M42; 222 | double c33 = a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33 + a.M34 * b.M43; 223 | double c34 = a.M31 * b.M14 + a.M32 * b.M24 + a.M33 * b.M34 + a.M34 * b.M44; 224 | 225 | // row 4 226 | double c41 = a.M41 * b.M11 + a.M42 * b.M21 + a.M43 * b.M31 + a.M44 * b.M41; 227 | double c42 = a.M41 * b.M12 + a.M42 * b.M22 + a.M43 * b.M32 + a.M44 * b.M42; 228 | double c43 = a.M41 * b.M13 + a.M42 * b.M23 + a.M43 * b.M33 + a.M44 * b.M43; 229 | double c44 = a.M41 * b.M14 + a.M42 * b.M24 + a.M43 * b.M34 + a.M44 * b.M44; 230 | 231 | return new Matrix(c11, c12, c13, c14, 232 | c21, c22, c23, c24, 233 | c31, c32, c33, c34, 234 | c41, c42, c43, c44); 235 | } 236 | public static Matrix Multiply(Matrix a, Matrix b) => a * b; 237 | public static Matrix operator *(Matrix m, double s) 238 | { 239 | double n11 = m.M11 * s; double n12 = m.M12 * s; double n13 = m.M13 * s; double n14 = m.M14 * s; 240 | double n21 = m.M21 * s; double n22 = m.M22 * s; double n23 = m.M23 * s; double n24 = m.M24 * s; 241 | double n31 = m.M31 * s; double n32 = m.M32 * s; double n33 = m.M33 * s; double n34 = m.M34 * s; 242 | double n41 = m.M41 * s; double n42 = m.M42 * s; double n43 = m.M43 * s; double n44 = m.M44 * s; 243 | return new Matrix(n11, n12, n13, n14, 244 | n21, n22, n23, n24, 245 | n31, n32, n33, n34, 246 | n41, n42, n43, n44); 247 | } 248 | public static Matrix Multiply(Matrix m, double s) => m * s; 249 | public static Matrix operator *(double s, Matrix m) => m * s; 250 | public static Matrix Multiply(double s, Matrix m) => s * m; 251 | public static Vector operator *(Matrix m, Vector v) 252 | { 253 | double x = m.M11 * v.X + m.M12 * v.Y + m.M13 * v.Z + m.M14 * 1; 254 | double y = m.M21 * v.X + m.M22 * v.Y + m.M23 * v.Z + m.M24 * 1; 255 | double z = m.M31 * v.X + m.M32 * v.Y + m.M33 * v.Z + m.M34 * 1; 256 | return new Vector(x, y, z); 257 | } 258 | public static Vector Multiply(Matrix m, Vector v) => m * v; 259 | public static Point operator *(Matrix m, Point p) 260 | { 261 | double x = m.M11 * p.X + m.M12 * p.Y + m.M13 * p.Z + m.M14 * 1; 262 | double y = m.M21 * p.X + m.M22 * p.Y + m.M23 * p.Z + m.M24 * 1; 263 | double z = m.M31 * p.X + m.M32 * p.Y + m.M33 * p.Z + m.M34 * 1; 264 | return new Point(x, y, z); 265 | } 266 | public static Point Multiply(Matrix m, Point p) => m * p; 267 | public static Matrix operator +(Matrix a, Matrix b) 268 | { 269 | return new Matrix(a.M11 + b.M11, a.M12 + b.M12, a.M13 + b.M13, a.M14 + b.M14, 270 | a.M21 + b.M21, a.M22 + b.M22, a.M23 + b.M23, a.M24 + b.M24, 271 | a.M31 + b.M31, a.M32 + b.M32, a.M33 + b.M33, a.M34 + b.M34, 272 | a.M41 + b.M41, a.M42 + b.M42, a.M43 + b.M43, a.M44 + b.M44); 273 | } 274 | public static Matrix Add(Matrix a, Matrix b) => a + b; 275 | public static Matrix Zero() => new Matrix(0, 0, 0, 0, 276 | 0, 0, 0, 0, 277 | 0, 0, 0, 0, 278 | 0, 0, 0, 0); 279 | public static Matrix Identity() => new Matrix(1, 0, 0, 0, 280 | 0, 1, 0, 0, 281 | 0, 0, 1, 0, 282 | 0, 0, 0, 1); 283 | public static Matrix Translate(double x, double y, double z) 284 | { 285 | // https://en.wikipedia.org/wiki/Translation_(geometry) 286 | Matrix m = Identity(); 287 | m.M14 += x; 288 | m.M24 += y; 289 | m.M34 += z; 290 | return m; 291 | } 292 | public static Matrix Translate(Vector v) => Translate(v.X, v.Y, v.Z); 293 | public static Matrix Scale(double x, double y, double z) 294 | { 295 | // https://en.wikipedia.org/wiki/Scaling_(geometry) 296 | Matrix m = Identity(); 297 | m.M11 *= x; m.M12 *= y; m.M13 *= z; 298 | m.M21 *= x; m.M22 *= y; m.M23 *= z; 299 | m.M31 *= x; m.M32 *= y; m.M33 *= z; 300 | m.M41 *= x; m.M42 *= y; m.M43 *= z; 301 | return m; 302 | } 303 | public static Matrix RotateX(double degreesCCW) 304 | { 305 | // https://en.wikipedia.org/wiki/Rotation_matrix 306 | double radians = degreesCCW * Math.PI / 180.0; 307 | Matrix m = Identity(); 308 | m.M22 = Math.Cos(radians); m.M23 = -Math.Sin(radians); 309 | m.M32 = Math.Sin(radians); m.M33 = Math.Cos(radians); 310 | return m; 311 | } 312 | public static Matrix RotateY(double degreesCCW) 313 | { 314 | // https://en.wikipedia.org/wiki/Rotation_matrix 315 | double radians = degreesCCW * Math.PI / 180.0; 316 | Matrix m = Identity(); 317 | m.M11 = Math.Cos(radians); m.M13 = Math.Sin(radians); 318 | m.M31 = -Math.Sin(radians); m.M33 = Math.Cos(radians); 319 | return m; 320 | } 321 | public static Matrix RotateZ(double degreesCCW) 322 | { 323 | // https://en.wikipedia.org/wiki/Rotation_matrix 324 | double radians = degreesCCW * Math.PI / 180.0; 325 | Matrix m = Identity(); 326 | m.M11 = Math.Cos(radians); m.M12 = -Math.Sin(radians); 327 | m.M21 = Math.Sin(radians); m.M22 = Math.Cos(radians); 328 | return m; 329 | } 330 | public static double Determinant2x2(double a, double b, 331 | double c, double d) 332 | { 333 | // https://en.wikipedia.org/wiki/Determinant 334 | return a * d - b * c; 335 | } 336 | public static double Determinant3x3(double a, double b, double c, 337 | double d, double e, double f, 338 | double g, double h, double i) 339 | { 340 | // https://en.wikipedia.org/wiki/Determinant 341 | double detA = Determinant2x2(e, f, 342 | h, i); 343 | double detB = Determinant2x2(d, f, 344 | g, i); 345 | double detC = Determinant2x2(d, e, 346 | g, h); 347 | return a * detA - b * detB + c * detC; 348 | } 349 | public static double Determinate4x4(double a, double b, double c, double d, 350 | double e, double f, double g, double h, 351 | double i, double j, double k, double l, 352 | double m, double n, double o, double p) 353 | { 354 | // https://en.wikipedia.org/wiki/Determinant 355 | double detA = Determinant3x3(f, g, h, 356 | j, k, l, 357 | n, o, p); 358 | double detB = Determinant3x3(e, g, h, 359 | i, k, l, 360 | m, o, p); 361 | double detC = Determinant3x3(e, f, h, 362 | i, j, l, 363 | m, n, p); 364 | double detD = Determinant3x3(e, f, g, 365 | i, j, k, 366 | m, n, o); 367 | return a * detA - b * detB + c * detC - d * detD; 368 | } 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /UnitTest-Point/UnitTest_Matrix.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | namespace DDD_UnitTest 4 | { 5 | [TestClass] 6 | public class Matrix 7 | { 8 | [TestMethod] 9 | public void ConstructorNoArgs() 10 | { 11 | DDD.Matrix m = new DDD.Matrix(); 12 | string ans = " 0 0 0 0\n" + 13 | " 0 0 0 0\n" + 14 | " 0 0 0 0\n" + 15 | " 0 0 0 0\n"; 16 | Assert.IsTrue(m.ToString() == ans); 17 | } 18 | [TestMethod] 19 | public void ConstructorWithValues() 20 | { 21 | DDD.Matrix m = new DDD.Matrix( 0, 1, 2, 3, 22 | 4, 5, 6, 7, 23 | 8, 9, 10, 11, 24 | 12, 13, 14, 15); 25 | string ans = " 0 1 2 3\n" + 26 | " 4 5 6 7\n" + 27 | " 8 9 10 11\n" + 28 | " 12 13 14 15\n"; 29 | Assert.IsTrue(m.ToString() == ans); 30 | } 31 | [TestMethod] 32 | public void ConstructorWithMatrix() 33 | { 34 | DDD.Matrix m1 = new DDD.Matrix( 0, 1, 2, 3, 35 | 4, 5, 6, 7, 36 | 8, 9, 10, 11, 37 | 12, 13, 14, 15); 38 | DDD.Matrix m2 = new DDD.Matrix(m1); 39 | string ans = " 0 1 2 3\n" + 40 | " 4 5 6 7\n" + 41 | " 8 9 10 11\n" + 42 | " 12 13 14 15\n"; 43 | Assert.IsTrue(m2.ToString() == ans); 44 | } 45 | 46 | [TestMethod] 47 | public void ConstructorWithNullArray() 48 | { 49 | double[] arr = null; 50 | DDD.Matrix m = new DDD.Matrix(arr); 51 | string ans = " 0 0 0 0\n" + 52 | " 0 0 0 0\n" + 53 | " 0 0 0 0\n" + 54 | " 0 0 0 0\n"; 55 | Assert.IsTrue(m.ToString() == ans); 56 | } 57 | static public void ConstructorWithEmptyArray() 58 | { 59 | double[] arr = System.Array.Empty(); 60 | DDD.Matrix m = new DDD.Matrix(arr); 61 | string ans = " 0 0 0 0\n" + 62 | " 0 0 0 0\n" + 63 | " 0 0 0 0\n" + 64 | " 0 0 0 0\n"; 65 | Assert.IsTrue(m.ToString() == ans); 66 | } 67 | [TestMethod] 68 | public void ConstructorWith3ElementArray() 69 | { 70 | double[] arr = { 1, 2, 3 }; 71 | DDD.Matrix m = new DDD.Matrix(arr); 72 | string ans = " 0 0 0 0\n" + 73 | " 0 0 0 0\n" + 74 | " 0 0 0 0\n" + 75 | " 0 0 0 0\n"; 76 | Assert.IsTrue(m.ToString() == ans); 77 | } 78 | [TestMethod] 79 | public void ConstructorWith9ElementArray() 80 | { 81 | double[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 82 | DDD.Matrix m = new DDD.Matrix(arr); 83 | string ans = " 1 2 3 0\n" + 84 | " 4 5 6 0\n" + 85 | " 7 8 9 0\n" + 86 | " 0 0 0 1\n"; 87 | Assert.IsTrue(m.ToString() == ans); 88 | } 89 | [TestMethod] 90 | public void ConstructorWith16ElementArray() 91 | { 92 | double[] arr = { 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; 93 | DDD.Matrix m = new DDD.Matrix(arr); 94 | string ans = " 16 15 14 13\n" + 95 | " 12 11 10 9\n" + 96 | " 8 7 6 5\n" + 97 | " 4 3 2 1\n"; 98 | Assert.IsTrue(m.ToString() == ans); 99 | } 100 | [TestMethod] 101 | public void ConstructorWith17ElementArray() 102 | { 103 | double[] arr = { 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; 104 | DDD.Matrix m = new DDD.Matrix(arr); 105 | string ans = " 0 0 0 0\n" + 106 | " 0 0 0 0\n" + 107 | " 0 0 0 0\n" + 108 | " 0 0 0 0\n"; 109 | Assert.IsTrue(m.ToString() == ans); 110 | } 111 | [TestMethod] 112 | public void ConstructorWith3Vectors() 113 | { 114 | DDD.Vector vx = new DDD.Vector(1, 2, 3); 115 | DDD.Vector vy = new DDD.Vector(4, 5, 6); 116 | DDD.Vector vz = new DDD.Vector(7, 8, 9); 117 | DDD.Matrix m = new DDD.Matrix(vx, vy, vz); 118 | string ans = " 1 4 7 0\n" + 119 | " 2 5 8 0\n" + 120 | " 3 6 9 0\n" + 121 | " 0 0 0 1\n"; 122 | Assert.IsTrue(m.ToString() == ans); 123 | } 124 | [TestMethod] 125 | public void TestEquals() 126 | { 127 | DDD.Matrix? m1 = DDD.Matrix.Identity(); 128 | DDD.Matrix? m2 = null; 129 | DDD.Matrix? m3 = DDD.Matrix.Identity(); 130 | DDD.Matrix? m4 = DDD.Matrix.Zero(); 131 | DDD.Matrix? m5 = null; 132 | 133 | Assert.IsTrue(m1.Equals(m3)); 134 | Assert.IsTrue(!m1.Equals(m4)); 135 | 136 | Assert.IsTrue(!m1.Equals((object)m2)); 137 | Assert.IsTrue(!m1.Equals((object)123)); 138 | Assert.IsTrue(m1.Equals((object)m3)); 139 | Assert.IsTrue(!m1.Equals((object)m4)); 140 | 141 | Assert.IsTrue(m2 == m5); 142 | Assert.IsTrue(m1 == m3); 143 | 144 | Assert.IsTrue(m1 != m2); 145 | Assert.IsTrue(m1 != m4); 146 | } 147 | [TestMethod] 148 | public void TestGetHashCode() 149 | { 150 | DDD.Matrix m = new DDD.Matrix( 0.0, .2, .3, 4.0294, 151 | 5.1232, 6.482, 7.22, 8.24342, 152 | 9.123, 10, -11.12322, 12.9053, 153 | 13, 14.123, 15.122, 16.123); 154 | int ans = -455996391; // from previous run 155 | Assert.IsTrue(m.GetHashCode() == ans); 156 | } 157 | [TestMethod] 158 | public void TestToString() 159 | { 160 | DDD.Matrix m = new DDD.Matrix((double)1/3, (double)-1/3, (double)+1/3, 123.456789, 161 | (double)1/2, (double)-1/2, (double)+1/2, 8, 162 | 0.0, -0.0, +0.0, 1234567890, 163 | -.1234567890, -00001234567890, 000.000, -01.0); 164 | string ans = " 0.33 -0.33 0.33 123.46\n" + 165 | " 0.5 -0.5 0.5 8\n" + 166 | " 0 -0 0 1,234,567,890\n" + 167 | " -0.12 -1,234,567,890 0 -1\n"; 168 | Assert.IsTrue(m.ToString() == ans); 169 | } 170 | [TestMethod] 171 | public void TranslateXYZ() 172 | { 173 | DDD.Matrix m = DDD.Matrix.Translate(1, 2, 3); 174 | string ans = " 1 0 0 1\n" + 175 | " 0 1 0 2\n" + 176 | " 0 0 1 3\n" + 177 | " 0 0 0 1\n"; 178 | Assert.IsTrue(m.ToString() == ans); 179 | } 180 | [TestMethod] 181 | public void TranslateVector() 182 | { 183 | DDD.Vector v = new DDD.Vector(10, 20, 30); 184 | DDD.Matrix m = DDD.Matrix.Translate(v); 185 | string ans = " 1 0 0 10\n" + 186 | " 0 1 0 20\n" + 187 | " 0 0 1 30\n" + 188 | " 0 0 0 1\n"; 189 | Assert.IsTrue(m.ToString() == ans); 190 | } 191 | [TestMethod] 192 | public void ScaleXYZ() 193 | { 194 | DDD.Matrix m = DDD.Matrix.Scale(2, 4, 8); 195 | string ans = " 2 0 0 0\n" + 196 | " 0 4 0 0\n" + 197 | " 0 0 8 0\n" + 198 | " 0 0 0 1\n"; 199 | Assert.IsTrue(m.ToString() == ans); 200 | } 201 | [TestMethod] 202 | public void RotateX() 203 | { 204 | DDD.Matrix m = DDD.Matrix.RotateX(90); 205 | string ans = " 1 0 0 0\n" + 206 | " 0 0 -1 0\n" + 207 | " 0 1 0 0\n" + 208 | " 0 0 0 1\n"; 209 | Assert.IsTrue(m.ToString() == ans); 210 | } 211 | [TestMethod] 212 | public void RotateY() 213 | { 214 | DDD.Matrix m = DDD.Matrix.RotateY(90); 215 | string ans = " 0 0 1 0\n" + 216 | " 0 1 0 0\n" + 217 | " -1 0 0 0\n" + 218 | " 0 0 0 1\n"; 219 | Assert.IsTrue(m.ToString() == ans); 220 | } 221 | [TestMethod] 222 | public void RotateZ() 223 | { 224 | DDD.Matrix m = DDD.Matrix.RotateZ(90); 225 | string ans = " 0 -1 0 0\n" + 226 | " 1 0 0 0\n" + 227 | " 0 0 1 0\n" + 228 | " 0 0 0 1\n"; 229 | Assert.IsTrue(m.ToString() == ans); 230 | } 231 | [TestMethod] 232 | public void Determinate() 233 | { 234 | DDD.Matrix m = new DDD.Matrix( 1, 2, 3, 4, 235 | 5, 6, 7, 8, 236 | 9, 10, 11, 12, 237 | 13, 14, 15, 16); 238 | Assert.IsTrue(m.Determinate() == 0); 239 | } 240 | [TestMethod] 241 | public void InvertBad() 242 | { 243 | DDD.Matrix m = new DDD.Matrix(7, 6, 5, 4, 244 | 1, 2, 3, 4, 245 | 9, 8, 7, 6, 246 | 2, 4, 6, 8); 247 | m.Invert(); // determinate is zero (ie not invertable), divide by zero gives NaN 248 | string ans = " NaN NaN NaN NaN\n" + 249 | " NaN NaN NaN NaN\n" + 250 | " NaN NaN NaN NaN\n" + 251 | " NaN NaN NaN NaN\n"; 252 | Assert.IsTrue(m.ToString() == ans); 253 | } 254 | [TestMethod] 255 | public void InvertGood() 256 | { 257 | // Note: Invert() tests both Adjugate() and Transpose() 258 | DDD.Matrix m = new DDD.Matrix(7, 3, 5, 4, 259 | 3, 2, 3, 1, 260 | 9, 3, 7, 6, 261 | 1, 4, 6, 8); 262 | m.Invert(); // Verified numbers are correct via http://matrix.reshish.com/inverse.php 263 | Assert.IsTrue(m.M11 == 0.34090909090909094); 264 | Assert.IsTrue(m.M12 == -0.22727272727272729); 265 | Assert.IsTrue(m.M13 == -0.068181818181818177); 266 | Assert.IsTrue(m.M14 == -0.090909090909090912); 267 | Assert.IsTrue(m.M21 == 1.0340909090909092); 268 | Assert.IsTrue(m.M22 == -0.022727272727272728); 269 | Assert.IsTrue(m.M23 == -0.80681818181818188); 270 | Assert.IsTrue(m.M24 == 0.090909090909090912); 271 | Assert.IsTrue(m.M31 == -1.125); 272 | Assert.IsTrue(m.M32 == 0.75); 273 | Assert.IsTrue(m.M33 == 0.625); 274 | Assert.IsTrue(m.M34 == 0); 275 | Assert.IsTrue(m.M41 == 0.28409090909090912); 276 | Assert.IsTrue(m.M42 == -0.52272727272727271); 277 | Assert.IsTrue(m.M43 == -0.056818181818181823); 278 | Assert.IsTrue(m.M44 == 0.090909090909090912); 279 | } 280 | [TestMethod] 281 | public void MultiplyMatrixByMatrix() 282 | { 283 | DDD.Matrix m1 = new DDD.Matrix( 1, 2, 3, 4, 284 | 5, 6, 7, 8, 285 | 9, 10, 11, 12, 286 | 13, 14, 15, 16); 287 | DDD.Matrix m2 = new DDD.Matrix( 3, 11, -3, 2, 288 | -2, 2, 99, 18, 289 | 59, 17, 121, 112, 290 | 3, 4, -5, 106); 291 | // answer verified by http://matrix.reshish.com/multCalculation.php 292 | string ans = " 188 82 538 798\n" + 293 | " 440 218 1,386 1,750\n" + 294 | " 692 354 2,234 2,702\n" + 295 | " 944 490 3,082 3,654\n"; 296 | Assert.IsTrue((m1 * m2).ToString() == ans); 297 | Assert.IsTrue(DDD.Matrix.Multiply(m1, m2).ToString() == ans); 298 | } 299 | [TestMethod] 300 | public void MultiplyMatrixByScaler() 301 | { 302 | DDD.Matrix m = DDD.Matrix.Identity(); 303 | string ans = " 3 0 0 0\n" + 304 | " 0 3 0 0\n" + 305 | " 0 0 3 0\n" + 306 | " 0 0 0 3\n"; 307 | Assert.IsTrue((m * 3.0).ToString() == ans); 308 | Assert.IsTrue(DDD.Matrix.Multiply(m, 3.0).ToString() == ans); 309 | } 310 | [TestMethod] 311 | public void MultiplyScalerByMatrix() 312 | { 313 | DDD.Matrix m = DDD.Matrix.Identity(); 314 | string ans = " 3 0 0 0\n" + 315 | " 0 3 0 0\n" + 316 | " 0 0 3 0\n" + 317 | " 0 0 0 3\n"; 318 | Assert.IsTrue((3.0 * m).ToString() == ans); 319 | Assert.IsTrue(DDD.Matrix.Multiply(3.0, m).ToString() == ans); 320 | } 321 | [TestMethod] 322 | public void MultiplyMatrixByVector() 323 | { 324 | DDD.Matrix m = DDD.Matrix.Identity(); 325 | DDD.Vector v = new DDD.Vector(1, 2, 3); 326 | string ans = "[1 2 3]\n"; 327 | Assert.IsTrue((m * v).ToString() == ans); 328 | Assert.IsTrue(DDD.Matrix.Multiply(m, v).ToString() == ans); 329 | } 330 | [TestMethod] 331 | public void MultiplyMatrixByPoint() 332 | { 333 | DDD.Matrix m = DDD.Matrix.Identity(); 334 | DDD.Point p = new DDD.Point(3, 2, 1); 335 | string ans = "(3 2 1)\n"; 336 | Assert.IsTrue((m * p).ToString() == ans); 337 | Assert.IsTrue(DDD.Matrix.Multiply(m, p).ToString() == ans); 338 | } 339 | [TestMethod] 340 | public void AddMatrixToMatrix() 341 | { 342 | var m1 = new DDD.Matrix( 1, 2, 3, 4, 343 | 5, 6, 7, 8, 344 | 9, 10, 11, 12, 345 | 13, 14, 15, 16); 346 | var m2 = new DDD.Matrix(2, 4, 6, 8, 347 | 10, 12, 14, 16, 348 | 18, 20, 22, 24, 349 | 26, 28, 30, 32); 350 | string ans = " 3 6 9 12\n" + 351 | " 15 18 21 24\n" + 352 | " 27 30 33 36\n" + 353 | " 39 42 45 48\n"; 354 | Assert.IsTrue((m1 + m2).ToString() == ans); 355 | Assert.IsTrue(DDD.Matrix.Add(m1, m2).ToString() == ans); 356 | } 357 | [TestMethod] 358 | public void Zero() 359 | { 360 | string ans = " 0 0 0 0\n" + 361 | " 0 0 0 0\n" + 362 | " 0 0 0 0\n" + 363 | " 0 0 0 0\n"; 364 | Assert.IsTrue(DDD.Matrix.Zero().ToString() == ans); 365 | } 366 | [TestMethod] 367 | public void Identity() 368 | { 369 | string ans = " 1 0 0 0\n" + 370 | " 0 1 0 0\n" + 371 | " 0 0 1 0\n" + 372 | " 0 0 0 1\n"; 373 | Assert.IsTrue(DDD.Matrix.Identity().ToString() == ans); 374 | } 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /DDD/UIWindows.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace DDD 6 | { 7 | partial class UIWindows : UI 8 | { 9 | const double MillisecondsPerRotation = 1000.0; 10 | readonly Point Origin_wld = new Point(0.0, 0.0, 0.0); 11 | 12 | // colors 13 | readonly System.Drawing.Color _black = System.Drawing.Color.FromArgb(0, 0, 0); // Black https://rgbcolorcode.com/color/000000 14 | readonly System.Drawing.Color _darkGray = System.Drawing.Color.FromArgb(74, 74, 74); // Quartz https://rgbcolorcode.com/color/4a4a4a 15 | readonly System.Drawing.Color _white = System.Drawing.Color.FromArgb(255, 255, 255); // White https://rgbcolorcode.com/color/FFFFFF 16 | readonly System.Drawing.Color _red = System.Drawing.Color.FromArgb(230, 38, 0); // Ferrari Red https://rgbcolorcode.com/color/E62600 17 | readonly System.Drawing.Color _green = System.Drawing.Color.FromArgb(25, 255, 25); // Neon Green https://rgbcolorcode.com/color/19FF19 18 | readonly System.Drawing.Color _blue = System.Drawing.Color.FromArgb(0, 68, 204); // Sapphire https://rgbcolorcode.com/color/0044CC 19 | readonly System.Drawing.Color _yellow = System.Drawing.Color.FromArgb(255, 255, 0); // Electric Yellow https://rgbcolorcode.com/color/FFFF00 20 | readonly System.Drawing.Color _orange = System.Drawing.Color.FromArgb(255, 140, 25); // Carrot Orange https://rgbcolorcode.com/color/FF8C19 21 | // readonly System.Drawing.Color _cyan = System.Drawing.Color.FromArgb(0, 255, 255); // Aqua https://rgbcolorcode.com/color/00FFFF 22 | // readonly System.Drawing.Color _magenta = System.Drawing.Color.FromArgb(255, 0, 255); // Fuchsia https://rgbcolorcode.com/color/FF00FF 23 | 24 | System.Drawing.Point _mouseMovePos; 25 | System.Drawing.Point _mouseLeftButtonDownPos; 26 | System.Drawing.Point _mouseRightButtonDownPos; 27 | bool _mouseLeftButtonDown = false; 28 | bool _mouseRightButtonDown = false; 29 | 30 | int _xaxis = 0; 31 | int _yaxis = 0; 32 | int _zaxis = 0; 33 | 34 | Matrix _wld2cam = Matrix.Identity(); // world to camera 35 | 36 | DateTime _xaxisStart = DateTime.Now; 37 | double _xDegreesCurrent = 0.0; 38 | double _xDegreesAtButtonDown = 0.0; 39 | double _xDegreesRotated = 0.0; 40 | 41 | DateTime _yaxisStart = DateTime.Now; 42 | double _yDegreesCurrent = 0.0; 43 | double _yDegreesAtButtonDown = 0.0; 44 | double _yDegreesRotated = 0.0; 45 | 46 | DateTime _zaxisStart = DateTime.Now; 47 | double _zDegreesCurrent = 0.0; 48 | double _zDegreesAtButtonDown = 0.0; 49 | double _zDegreesAtRotateGestureBegin = 0.0; 50 | double _zDegreesRotated = 0.0; 51 | 52 | int _width = 0; 53 | int _height = 0; 54 | int _minDimension = 0; 55 | 56 | double _maxDistance = 0.0; 57 | 58 | Point _xAxis_wld; 59 | Point _yAxis_wld; 60 | Point _zAxis_wld; 61 | 62 | bool _showBoundingBox = false; 63 | 64 | IntPtr MyWndProc(IntPtr hWnd, WindowsMessage msg, IntPtr wParam, IntPtr lParam) 65 | { 66 | // Console.WriteLine($"MyWndProc: {hWnd}, {msg}, {wParam}, {lParam}"); 67 | switch (msg) 68 | { 69 | case WindowsMessage.WM_GESTURE: 70 | IntPtr hGestureInfo = lParam; 71 | var gi = new GESTUREINFO(); 72 | gi.cbSize = (uint)Marshal.SizeOf(typeof(GESTUREINFO)); 73 | bool gestureInfoGotten = GetGestureInfo(hGestureInfo, ref gi); 74 | if (!gestureInfoGotten) PrintErrorAndExit("GetGestureInfo"); 75 | 76 | if (gi.dwID == GestureID.GID_ROTATE) 77 | { 78 | double angleInRadians = GID_ROTATE_ANGLE_FROM_ARGUMENT(gi.ulArguments); 79 | double angle = angleInRadians * 180.0 / Math.PI; 80 | if (gi.dwFlags == GestureFlags.GF_BEGIN) 81 | { 82 | _zDegreesAtRotateGestureBegin = _zDegreesCurrent; 83 | } 84 | else 85 | { 86 | _zDegreesCurrent = _zDegreesAtRotateGestureBegin - angle; 87 | } 88 | return IntPtr.Zero; 89 | } 90 | return DefWindowProc(hWnd, msg, wParam, lParam); 91 | 92 | // Left Mouse 93 | case WindowsMessage.WM_LBUTTONDOWN: 94 | _mouseLeftButtonDown = true; 95 | _mouseLeftButtonDownPos.X = GET_X_LPARAM(lParam); 96 | _mouseLeftButtonDownPos.Y = GET_Y_LPARAM(lParam); 97 | _xDegreesAtButtonDown = _xDegreesCurrent; 98 | _yDegreesAtButtonDown = _yDegreesCurrent; 99 | return IntPtr.Zero; 100 | case WindowsMessage.WM_LBUTTONUP: 101 | _mouseLeftButtonDown = false; 102 | return IntPtr.Zero; 103 | 104 | // Right Mouse 105 | case WindowsMessage.WM_RBUTTONDOWN: 106 | _mouseRightButtonDown = true; 107 | _mouseRightButtonDownPos.X = GET_X_LPARAM(lParam); 108 | _mouseRightButtonDownPos.Y = GET_Y_LPARAM(lParam); 109 | _zDegreesAtButtonDown = _zDegreesCurrent; 110 | return IntPtr.Zero; 111 | case WindowsMessage.WM_RBUTTONUP: 112 | _mouseRightButtonDown = false; 113 | return IntPtr.Zero; 114 | 115 | // Mouse move 116 | case WindowsMessage.WM_MOUSEMOVE: 117 | _mouseMovePos.X = GET_X_LPARAM(lParam); 118 | _mouseMovePos.Y = GET_Y_LPARAM(lParam); 119 | _mouseLeftButtonDown = ((uint)wParam & (uint)MouseKeyStateMasks.MK_LBUTTON) > 0 ? true : false; 120 | _mouseRightButtonDown = ((uint)wParam & (uint)MouseKeyStateMasks.MK_RBUTTON) > 0 ? true : false; 121 | return IntPtr.Zero; 122 | // Key down 123 | case WindowsMessage.WM_KEYDOWN: 124 | switch ((VIRTUALKEY)wParam) 125 | { 126 | // x axis 127 | case VIRTUALKEY.VK_W: 128 | case VIRTUALKEY.VK_UP: 129 | if (_xaxis != 1) 130 | { 131 | _xaxis = 1; 132 | _xDegreesRotated = 0.0; 133 | // _xDegreesAtButtonDown = _xDegreesCurrent; 134 | _xaxisStart = DateTime.Now; 135 | } 136 | break; 137 | case VIRTUALKEY.VK_S: 138 | case VIRTUALKEY.VK_DOWN: 139 | if (_xaxis != -1) 140 | { 141 | _xaxis = -1; 142 | _xDegreesRotated = 0.0; 143 | // _xDegreesAtButtonDown = _xDegreesCurrent; 144 | _xaxisStart = DateTime.Now; 145 | } 146 | break; 147 | // y axis 148 | case VIRTUALKEY.VK_A: 149 | case VIRTUALKEY.VK_LEFT: 150 | if (_yaxis != 1) 151 | { 152 | _yaxis = 1; 153 | _yDegreesRotated = 0.0; 154 | // _yDegreesAtButtonDown = _yDegreesCurrent; 155 | _yaxisStart = DateTime.Now; 156 | } 157 | break; 158 | case VIRTUALKEY.VK_D: 159 | case VIRTUALKEY.VK_RIGHT: 160 | if (_yaxis != -1) 161 | { 162 | _yaxis = -1; 163 | _yDegreesRotated = 0.0; 164 | // _yDegreesAtButtonDown = _yDegreesCurrent; 165 | _yaxisStart = DateTime.Now; 166 | } 167 | break; 168 | // z axis 169 | case VIRTUALKEY.VK_Q: 170 | case VIRTUALKEY.VK_OEM_COMMA: 171 | case VIRTUALKEY.VK_PRIOR: 172 | if (_zaxis != 1) 173 | { 174 | _zaxis = 1; 175 | _zDegreesRotated = 0.0; 176 | _zaxisStart = DateTime.Now; 177 | } 178 | break; 179 | case VIRTUALKEY.VK_E: 180 | case VIRTUALKEY.VK_OEM_PERIOD: 181 | case VIRTUALKEY.VK_NEXT: 182 | if (_zaxis != -1) 183 | { 184 | _zaxis = -1; 185 | _zDegreesRotated = 0.0; 186 | _zaxisStart = DateTime.Now; 187 | } 188 | break; 189 | } 190 | return IntPtr.Zero; 191 | // Key up 192 | case WindowsMessage.WM_KEYUP: 193 | switch ((VIRTUALKEY)wParam) 194 | { 195 | // x axis 196 | case VIRTUALKEY.VK_W: 197 | case VIRTUALKEY.VK_UP: 198 | case VIRTUALKEY.VK_S: 199 | case VIRTUALKEY.VK_DOWN: 200 | _xaxis = 0; 201 | break; 202 | // y axis 203 | case VIRTUALKEY.VK_A: 204 | case VIRTUALKEY.VK_LEFT: 205 | case VIRTUALKEY.VK_D: 206 | case VIRTUALKEY.VK_RIGHT: 207 | _yaxis = 0; 208 | break; 209 | // z axis 210 | case VIRTUALKEY.VK_Q: 211 | case VIRTUALKEY.VK_OEM_COMMA: 212 | case VIRTUALKEY.VK_PRIOR: 213 | case VIRTUALKEY.VK_E: 214 | case VIRTUALKEY.VK_OEM_PERIOD: 215 | case VIRTUALKEY.VK_NEXT: 216 | _zaxis = 0; 217 | break; 218 | // show bounding box toggle 219 | case VIRTUALKEY.VK_B: 220 | _showBoundingBox = !_showBoundingBox; 221 | break; 222 | // reset 223 | case VIRTUALKEY.VK_R: 224 | _wld2cam = Matrix.Identity(); 225 | _xDegreesCurrent = 0.0; 226 | _yDegreesCurrent = 0.0; 227 | _zDegreesCurrent = 0.0; 228 | _xaxis = 0; 229 | _yaxis = 0; 230 | _zaxis = 0; 231 | _mouseLeftButtonDown = false; 232 | _mouseRightButtonDown = false; 233 | break; 234 | case VIRTUALKEY.VK_ESCAPE: 235 | DestroyWindow(hWnd); 236 | break; 237 | } 238 | return IntPtr.Zero; 239 | // Window resize 240 | case WindowsMessage.WM_SIZE: 241 | const int x = 0; 242 | const int y = 0; 243 | _width = LOWORD(lParam); 244 | _height = HIWORD(lParam); 245 | _minDimension = _width < _height ? _width : _height; 246 | glViewport(x, y, _width, _height); 247 | return IntPtr.Zero; 248 | // Quit 249 | case WindowsMessage.WM_DESTROY: 250 | PostQuitMessage(0); 251 | return IntPtr.Zero; 252 | default: 253 | return DefWindowProc(hWnd, msg, wParam, lParam); 254 | } 255 | } 256 | private void Display(List Objects, Point BoundingBoxMin, Point BoundingBoxMax) 257 | { 258 | /* 259 | Coordinate System Definitions 260 | 261 | Name TLA Notes 262 | ---- --- ----- 263 | Object obj Relative to an object 264 | World wld Global space 265 | Camera cam Observers view. Looking at world origin. 266 | Screen scr OpenGL. XY origin at center of screen. +X from left (-1) to right (+1). +Y from bottom (-1) to top (+1). 267 | */ 268 | 269 | #region UPDATE WLD2CAM 270 | 271 | // update cam2wld 272 | Matrix cam2wld = _wld2cam; 273 | cam2wld.Invert(); 274 | 275 | if (_xaxis != 0) 276 | { 277 | TimeSpan interval = DateTime.Now - _xaxisStart; 278 | double rotations = interval.TotalMilliseconds / MillisecondsPerRotation; // 1.0 equals 360 degree rotation 279 | double degrees = 360.0 * rotations * -_xaxis; 280 | double deltaDegrees = degrees - _xDegreesRotated; // degrees to rotate since last rotation 281 | cam2wld *= Matrix.RotateX(deltaDegrees); 282 | _xDegreesRotated += deltaDegrees; 283 | } 284 | if (_yaxis != 0) 285 | { 286 | TimeSpan interval = DateTime.Now - _yaxisStart; 287 | double rotations = interval.TotalMilliseconds / MillisecondsPerRotation; // 1.0 equals 360 degree rotation 288 | double degrees = 360.0 * rotations * -_yaxis; 289 | double deltaDegrees = degrees - _yDegreesRotated; // degrees to rotate since last rotation 290 | cam2wld *= Matrix.RotateY(deltaDegrees); 291 | _yDegreesRotated += deltaDegrees; 292 | } 293 | if (_zaxis != 0) 294 | { 295 | TimeSpan interval = DateTime.Now - _zaxisStart; 296 | double rotations = interval.TotalMilliseconds / MillisecondsPerRotation; // 1.0 equals 360 degree rotation 297 | double degrees = 360.0 * rotations * -_zaxis; 298 | double deltaDegrees = degrees - _zDegreesRotated; // degrees to rotate since last rotation 299 | cam2wld *= Matrix.RotateZ(deltaDegrees); 300 | _zDegreesRotated += deltaDegrees; 301 | } 302 | if (_mouseLeftButtonDown) 303 | { 304 | // y axis via horizontal movement 305 | int deltaPosX = _mouseLeftButtonDownPos.X - _mouseMovePos.X; 306 | double x = (double)deltaPosX / (double)_minDimension; 307 | double newCurrentY = 360.0 * x + _yDegreesAtButtonDown; 308 | cam2wld *= Matrix.RotateY(newCurrentY - _yDegreesCurrent); 309 | _yDegreesCurrent = newCurrentY; 310 | 311 | // x axis via vertical movement 312 | int deltaPosY = _mouseLeftButtonDownPos.Y - _mouseMovePos.Y; 313 | double y = (double)deltaPosY / (double)_minDimension; 314 | double newCurrentX = 360.0 * y + _xDegreesAtButtonDown; 315 | cam2wld *= Matrix.RotateX(newCurrentX - _xDegreesCurrent); 316 | _xDegreesCurrent = newCurrentX; 317 | } 318 | if (_mouseRightButtonDown) 319 | { 320 | // z axis via horizontal movement 321 | int deltaPosX = _mouseRightButtonDownPos.X - _mouseMovePos.X; 322 | double x = (double)deltaPosX / (double)_width; 323 | double newCurrentZ = 360.0 * x + _zDegreesAtButtonDown; 324 | cam2wld *= Matrix.RotateZ(newCurrentZ - _zDegreesCurrent); 325 | _zDegreesCurrent = newCurrentZ; 326 | } 327 | 328 | // update wld2cam 329 | _wld2cam = cam2wld; 330 | _wld2cam.Invert(); 331 | #endregion 332 | 333 | #region CAM2SCR 334 | Matrix cam2scr = Matrix.Identity(); 335 | 336 | // fit bounding box within opengl window 337 | if (_maxDistance != 0) 338 | { 339 | const double OPENGL_MAX = 1.0; 340 | const double COVERAGE = 0.95; // 1.0 == max coverage, 0.5 == half coverage, .95 looks about right 341 | double s = OPENGL_MAX / _maxDistance * COVERAGE; 342 | cam2scr *= Matrix.Scale(s, s, s); 343 | } 344 | 345 | // Keep 1:1 ratio, even when window is not square 346 | if (_width != 0 && _height != 0) 347 | { 348 | double w2h = (double)_width/(double)_height; 349 | if (_width < _height) 350 | { 351 | cam2scr *= Matrix.Scale(1.0, w2h, 1.0); 352 | } 353 | else 354 | { 355 | cam2scr *= Matrix.Scale(1.0/w2h, 1.0, 1.0); 356 | } 357 | } 358 | #endregion 359 | 360 | Matrix wld2scr = cam2scr * _wld2cam; 361 | 362 | 363 | glEnable(GetTarget.GL_DEPTH_TEST); // TODO: This only needs to be done once 364 | glClear(AttribMask.GL_COLOR_BUFFER_BIT | AttribMask.GL_DEPTH_BUFFER_BIT); 365 | 366 | #region DRAW AXES 367 | { 368 | // Map axis to screen space 369 | Point origin_scr = wld2scr * Origin_wld; 370 | Point xaxis_scr = wld2scr * _xAxis_wld; 371 | Point yaxis_scr = wld2scr * _yAxis_wld; 372 | Point zaxis_scr = wld2scr * _zAxis_wld; 373 | 374 | glEnable(GetTarget.GL_LINE_SMOOTH); 375 | glHint(GetTarget.GL_LINE_SMOOTH_HINT, HintMode.GL_NICEST); 376 | glBegin(BeginMode.GL_LINES); 377 | 378 | // x axis (red) - left 379 | glColor3ub(_red.R, _red.G, _red.B); 380 | glVertex3d(origin_scr.X, origin_scr.Y, origin_scr.Z); 381 | glColor3ub(_black.R, _black.G, _black.B); 382 | glVertex3d(xaxis_scr.X, xaxis_scr.Y, xaxis_scr.Z); 383 | 384 | // y axis (green) - up 385 | glColor3ub(_green.R, _green.G, _green.B); 386 | glVertex3d(origin_scr.X, origin_scr.Y, origin_scr.Z); 387 | glColor3ub(_black.R, _black.G, _black.B); 388 | glVertex3d(yaxis_scr.X, yaxis_scr.Y, yaxis_scr.Z); 389 | 390 | // z axis (blue) - towards user 391 | glColor3ub(_blue.R, _blue.G, _blue.B); 392 | glVertex3d(origin_scr.X, origin_scr.Y, origin_scr.Z); 393 | glColor3ub(_black.R, _black.G, _black.B); 394 | glVertex3d(zaxis_scr.X, zaxis_scr.Y, zaxis_scr.Z); 395 | 396 | glEnd(); 397 | } 398 | #endregion 399 | 400 | #region DRAW OBJECTS 401 | foreach (object o in Objects) 402 | { 403 | if (o is Point p_wld) 404 | { 405 | Point p_scr = wld2scr * p_wld; 406 | glPointSize(5.0f); 407 | glEnable(GetTarget.GL_POINT_SMOOTH); 408 | glHint(GetTarget.GL_POINT_SMOOTH_HINT, HintMode.GL_FASTEST); 409 | glBegin(BeginMode.GL_POINTS); 410 | glColor3ub(_yellow.R, _yellow.G, _yellow.B); 411 | glVertex3d(p_scr.X, p_scr.Y, p_scr.Z); 412 | glEnd(); 413 | } 414 | else if (o is Vector v_wld) 415 | { 416 | Vector v_scr = wld2scr * v_wld; 417 | Vector origin_scr = wld2scr * new Vector(0.0, 0.0, 0.0); 418 | glEnable(GetTarget.GL_LINE_SMOOTH); 419 | glHint(GetTarget.GL_LINE_SMOOTH_HINT, HintMode.GL_NICEST); 420 | glBegin(BeginMode.GL_LINES); 421 | glLineWidth(1.0f); 422 | glColor3ub(_darkGray.R, _darkGray.G, _darkGray.B); 423 | glVertex3d(origin_scr.X, origin_scr.Y, origin_scr.Z); 424 | glColor3ub(_orange.R, _orange.G, _orange.B); 425 | glVertex3d(v_scr.X, v_scr.Y, v_scr.Z); 426 | glEnd(); 427 | } 428 | else if (o is Matrix m_wld) 429 | { 430 | Matrix m_scr = wld2scr * m_wld; 431 | Point origin_scr = m_scr * new Point(0.0, 0.0, 0.0); 432 | Point xaxis_scr = m_scr * new Point(1.0, 0.0, 0.0); 433 | Point yaxis_scr = m_scr * new Point(0.0, 1.0, 0.0); 434 | Point zaxis_scr = m_scr * new Point(0.0, 0.0, 1.0); 435 | 436 | glEnable(GetTarget.GL_LINE_SMOOTH); 437 | glHint(GetTarget.GL_LINE_SMOOTH_HINT, HintMode.GL_NICEST); 438 | glBegin(BeginMode.GL_LINES); 439 | 440 | // x axis (red) - left 441 | glColor3ub(_red.R, _red.G, _red.B); 442 | glVertex3d(origin_scr.X, origin_scr.Y, origin_scr.Z); 443 | glVertex3d(xaxis_scr.X, xaxis_scr.Y, xaxis_scr.Z); 444 | 445 | // y axis (green) - up 446 | glColor3ub(_green.R, _green.G, _green.B); 447 | glVertex3d(origin_scr.X, origin_scr.Y, origin_scr.Z); 448 | glVertex3d(yaxis_scr.X, yaxis_scr.Y, yaxis_scr.Z); 449 | 450 | // z axis (blue) - towards user 451 | glColor3ub(_blue.R, _blue.G, _blue.B); 452 | glVertex3d(origin_scr.X, origin_scr.Y, origin_scr.Z); 453 | glVertex3d(zaxis_scr.X, zaxis_scr.Y, zaxis_scr.Z); 454 | 455 | glEnd(); 456 | } 457 | } 458 | #endregion 459 | 460 | #region DRAW BOUNDING BOX 461 | if (_showBoundingBox) 462 | { 463 | Vector delta_bbox = BoundingBoxMax - BoundingBoxMin; 464 | if (delta_bbox.X != 0 && delta_bbox.Y != 0 && delta_bbox.Z != 0) 465 | { 466 | // Map bounding box to screen space 467 | Point min_scr = wld2scr * BoundingBoxMin; 468 | Point max_scr = wld2scr * BoundingBoxMax; 469 | 470 | Point xmin_scr = wld2scr * new Point(BoundingBoxMax.X, BoundingBoxMin.Y, BoundingBoxMin.Z); 471 | Point ymin_scr = wld2scr * new Point(BoundingBoxMin.X, BoundingBoxMax.Y, BoundingBoxMin.Z); 472 | Point zmin_scr = wld2scr * new Point(BoundingBoxMin.X, BoundingBoxMin.Y, BoundingBoxMax.Z); 473 | 474 | Point xmax_scr = wld2scr * new Point(BoundingBoxMin.X, BoundingBoxMax.Y, BoundingBoxMax.Z); 475 | Point ymax_scr = wld2scr * new Point(BoundingBoxMax.X, BoundingBoxMin.Y, BoundingBoxMax.Z); 476 | Point zmax_scr = wld2scr * new Point(BoundingBoxMax.X, BoundingBoxMax.Y, BoundingBoxMin.Z); 477 | 478 | glEnable(GetTarget.GL_LINE_SMOOTH); 479 | glHint(GetTarget.GL_LINE_SMOOTH_HINT, HintMode.GL_NICEST); 480 | glBegin(BeginMode.GL_LINES); 481 | 482 | // x axis (red) - left 483 | glColor3ub(_white.R, _white.G, _white.B); 484 | glVertex3d(min_scr.X, min_scr.Y, min_scr.Z); 485 | glVertex3d(xmin_scr.X, xmin_scr.Y, xmin_scr.Z); 486 | 487 | // y axis (green) - up 488 | glVertex3d(min_scr.X, min_scr.Y, min_scr.Z); 489 | glVertex3d(ymin_scr.X, ymin_scr.Y, ymin_scr.Z); 490 | 491 | // z axis (blue) - towards user 492 | glVertex3d(min_scr.X, min_scr.Y, min_scr.Z); 493 | glVertex3d(zmin_scr.X, zmin_scr.Y, zmin_scr.Z); 494 | 495 | // max x white 496 | glVertex3d(max_scr.X, max_scr.Y, max_scr.Z); 497 | glVertex3d(xmax_scr.X, xmax_scr.Y, xmax_scr.Z); 498 | 499 | // max y white 500 | glVertex3d(max_scr.X, max_scr.Y, max_scr.Z); 501 | glVertex3d(ymax_scr.X, ymax_scr.Y, ymax_scr.Z); 502 | 503 | // max z white 504 | glVertex3d(max_scr.X, max_scr.Y, max_scr.Z); 505 | glVertex3d(zmax_scr.X, zmax_scr.Y, zmax_scr.Z); 506 | 507 | // x axis (red) - left 508 | glVertex3d(xmin_scr.X, xmin_scr.Y, xmin_scr.Z); 509 | glVertex3d(ymax_scr.X, ymax_scr.Y, ymax_scr.Z); 510 | 511 | glVertex3d(xmin_scr.X, xmin_scr.Y, xmin_scr.Z); 512 | glVertex3d(zmax_scr.X, zmax_scr.Y, zmax_scr.Z); 513 | 514 | // y axis (green) - up 515 | glVertex3d(ymin_scr.X, ymin_scr.Y, ymin_scr.Z); 516 | glVertex3d(xmax_scr.X, xmax_scr.Y, xmax_scr.Z); 517 | 518 | glVertex3d(ymin_scr.X, ymin_scr.Y, ymin_scr.Z); 519 | glVertex3d(zmax_scr.X, zmax_scr.Y, zmax_scr.Z); 520 | 521 | // z axis (blue) - towards user 522 | glVertex3d(zmin_scr.X, zmin_scr.Y, zmin_scr.Z); 523 | glVertex3d(xmax_scr.X, xmax_scr.Y, xmax_scr.Z); 524 | 525 | glVertex3d(zmin_scr.X, zmin_scr.Y, zmin_scr.Z); 526 | glVertex3d(ymax_scr.X, ymax_scr.Y, ymax_scr.Z); 527 | 528 | glEnd(); 529 | } 530 | } 531 | #endregion 532 | 533 | glFlush(); 534 | } 535 | static void PrintErrorAndExit(string cmd) 536 | { 537 | // System Error Codes: https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes 538 | int error = GetLastError(); 539 | // Throw a terminating error for types that are not supported. 540 | string msg = string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0} returned error code 0x{1:X}. \nSee https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes", cmd, error); 541 | throw new System.InvalidOperationException(msg); 542 | } 543 | public void ShowWindow(List Objects, Point BoundingBoxMin, Point BoundingBoxMax, string Title) 544 | { 545 | // compute _maxDistance 546 | double maxX = Math.Max(Math.Abs(BoundingBoxMin.X), Math.Abs(BoundingBoxMax.X)); // max distance from origin in X direction 547 | double maxY = Math.Max(Math.Abs(BoundingBoxMin.Y), Math.Abs(BoundingBoxMax.Y)); // max distance from origin in Y direction 548 | double maxZ = Math.Max(Math.Abs(BoundingBoxMin.Z), Math.Abs(BoundingBoxMax.Z)); // max distance from origin in Z direction 549 | double maxAxis = Math.Max(Math.Max(maxX, maxY), maxZ); // overall max distance for any axis 550 | _maxDistance = Math.Sqrt(3 * maxAxis * maxAxis); // Distance from origin to (maxAxis, maxAxis, maxAxis) 551 | 552 | // initialize globals 553 | _xAxis_wld.X = _maxDistance; 554 | _xAxis_wld.Y = 0; 555 | _xAxis_wld.Z = 0; 556 | _yAxis_wld.X = 0; 557 | _yAxis_wld.Y = _maxDistance; 558 | _yAxis_wld.Z = 0; 559 | _zAxis_wld.X = 0; 560 | _zAxis_wld.Y = 0; 561 | _zAxis_wld.Z = _maxDistance; 562 | _showBoundingBox = false; 563 | 564 | _mouseLeftButtonDown = false; 565 | _mouseRightButtonDown = false; 566 | 567 | // Create window 568 | IntPtr hInstance = GetModuleHandle(null); 569 | if (hInstance == null) PrintErrorAndExit("GetModuleHandle"); 570 | 571 | const string className = "DDDWindow"; 572 | var wc = WNDCLASSEX.Build(); 573 | wc.Style = ClassStyles.CS_OWNDC; 574 | wc.WndProc = new WndProc(MyWndProc); 575 | wc.ClassName = className; 576 | wc.Instance = hInstance; 577 | wc.Cursor = LoadCursor(IntPtr.Zero, IDC_STANDARD_CURSORS.IDC_ARROW); 578 | 579 | ushort atom = RegisterClassEx(ref wc); 580 | if (atom == 0) PrintErrorAndExit("RegisterClassEx"); 581 | 582 | const int dwExStyle = 0; 583 | const int x = CW_USEDEFAULT; 584 | const int y = CW_USEDEFAULT; 585 | const int width = CW_USEDEFAULT; 586 | const int height = CW_USEDEFAULT; 587 | IntPtr hWndParent = IntPtr.Zero; 588 | IntPtr hMenu = IntPtr.Zero; 589 | IntPtr lpParam = IntPtr.Zero; 590 | 591 | IntPtr hWnd = CreateWindowEx( 592 | dwExStyle, 593 | atom, 594 | Title, 595 | WindowStyles.WS_CLIPCHILDREN | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_OVERLAPPEDWINDOW, 596 | x, 597 | y, 598 | width, 599 | height, 600 | hWndParent, 601 | hMenu, 602 | hInstance, 603 | lpParam); 604 | if (hWnd == IntPtr.Zero) PrintErrorAndExit("CreateWindowEx"); 605 | 606 | const uint dwReserved = 0; 607 | const uint cIDs = 1; 608 | var pGestureConfig = new GESTURECONFIG[cIDs]; 609 | pGestureConfig[0].dwID = GestureID.GID_ROTATE; 610 | pGestureConfig[0].dwWant = GC_ROTATE; 611 | pGestureConfig[0].dwBlock = 0; 612 | uint cbSize = (uint)Marshal.SizeOf(typeof(GESTURECONFIG)); 613 | bool gestureConfigSet = SetGestureConfig(hWnd, dwReserved, cIDs, pGestureConfig, cbSize); 614 | if (!gestureConfigSet) PrintErrorAndExit("SetGestureConfig"); 615 | 616 | bool foregroundWindow = SetForegroundWindow(hWnd); 617 | // NOTE: The following line is commented out because it fails in Windows Terminal because of a bug. 618 | // When bug fixed, uncomment next line. 619 | // Bug report: https://github.com/microsoft/terminal/issues/2988 620 | // if (!foregroundWindow) PrintErrorAndExit("SetForegroundWindow"); 621 | 622 | // Set pixel format 623 | var pfd = PixelFormatDescriptor.Build(); 624 | pfd.Flags = PixelFormatDescriptorFlags.PFD_DRAW_TO_WINDOW | 625 | PixelFormatDescriptorFlags.PFD_SUPPORT_OPENGL | 626 | PixelFormatDescriptorFlags.PFD_DOUBLEBUFFER; 627 | pfd.PixelType = PixelType.PFD_TYPE_RGBA; 628 | pfd.ColorBits = 24; // bits for color: 8 red + 8 blue + 8 green = 24 629 | pfd.AlphaBits = 0; 630 | pfd.AccumBits = 0; 631 | pfd.DepthBits = 32; 632 | pfd.StencilBits = 0; 633 | pfd.AuxBuffers = 0; 634 | pfd.LayerType = 0; // PFD_MAIN_PLANE 635 | 636 | IntPtr hDC = GetDC(hWnd); 637 | if (hDC == IntPtr.Zero) PrintErrorAndExit("GetDC"); 638 | 639 | int pf = ChoosePixelFormat(hDC, ref pfd); 640 | if (pf == 0) PrintErrorAndExit("ChoosePixelFormat"); 641 | 642 | bool pixelFormatSet = HACK_SetPixelFormat(hDC, pf, ref pfd); 643 | if (!pixelFormatSet) PrintErrorAndExit("HACK_SetPixelFormat"); 644 | 645 | IntPtr hRC = wglCreateContext(hDC); 646 | if (hRC == IntPtr.Zero) PrintErrorAndExit("wglCreateContext"); 647 | 648 | bool makeCurrent = wglMakeCurrent(hDC, hRC); 649 | if (!makeCurrent) PrintErrorAndExit("wglMakeCurrent"); 650 | 651 | ShowWindow(hWnd, ShowWindowCommand.Show); 652 | 653 | // Message loop 654 | bool running = true; 655 | while (running) 656 | { 657 | Display(Objects, BoundingBoxMin, BoundingBoxMax); 658 | 659 | bool buffersSwapped = SwapBuffers(hDC); 660 | if (!buffersSwapped) PrintErrorAndExit("SwapBuffers"); 661 | 662 | // Do not use 100% of CPU. Yield to others for a minimal amount of time (1 millisecond) 663 | System.Threading.Tasks.Task.Delay(1).Wait(); 664 | 665 | MSG msg; 666 | while (PeekMessage(out msg, IntPtr.Zero, 0, 0, PM_NOREMOVE)) 667 | { 668 | if (GetMessage(out msg, IntPtr.Zero, 0, 0) != 0) 669 | { 670 | TranslateMessage(ref msg); 671 | DispatchMessage(ref msg); 672 | } 673 | else // Got WM_QUIT 674 | { 675 | running = false; 676 | break; 677 | } 678 | } 679 | } 680 | 681 | bool makeCurrentZeroed = wglMakeCurrent(IntPtr.Zero, IntPtr.Zero); 682 | if (!makeCurrentZeroed) PrintErrorAndExit("wglMakeCurrent"); 683 | 684 | bool contextDeleted = wglDeleteContext(hRC); 685 | if (!contextDeleted) PrintErrorAndExit("wglDeleteContext"); 686 | 687 | bool classUnregistered = UnregisterClass(className, hInstance); 688 | if (!classUnregistered) PrintErrorAndExit("UnregisterClass"); 689 | } 690 | } 691 | } --------------------------------------------------------------------------------