Initial commit

This commit is contained in:
David Allemang
2017-08-01 22:42:47 -04:00
commit 65826edc9f
16 changed files with 636 additions and 0 deletions

22
Harmonograph.sln Normal file
View File

@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.0.0
MinimumVisualStudioVersion = 10.0.0.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Harmonograph", "Harmonograph\Harmonograph.csproj", "{3DEBA9F3-AD0D-43A6-BF4E-0220BDAFB099}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3DEBA9F3-AD0D-43A6-BF4E-0220BDAFB099}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DEBA9F3-AD0D-43A6-BF4E-0220BDAFB099}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DEBA9F3-AD0D-43A6-BF4E-0220BDAFB099}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DEBA9F3-AD0D-43A6-BF4E-0220BDAFB099}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

174
Harmonograph/GraphWindow.cs Normal file
View File

@@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Xml;
using Newtonsoft.Json.Linq;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
namespace Harmonograph
{
public class GraphWindow : GameWindow
{
private const double Tau = Math.PI * 2;
private const double Pi = Math.PI;
public HarmonographSolver Solver { get; } = new HarmonographSolver()
{
new PendulumSolver(Vector2d.UnitX * 1.5, 4.01, 0, 0.005),
new PendulumSolver(Vector2d.UnitY, 5.01, 0, 0.005),
new PendulumSolver(Vector2d.UnitX, 1.01, 0, 0.005),
new PendulumSolver(Vector2d.UnitY / 2, -1.01, 1, 0.005),
};
public double ViewHeight { get; set; }
public double DisplayRate { get; set; }
public int Cycles { get; set; }
public Color Back { get; }
public Color Front { get; }
private Vector2[] _path = new Vector2[0];
private double _time = 0;
private double _phaseRate = Math.PI / 4;
private bool _phaseMode = false;
public GraphWindow(string settingsFile)
: base(2560, 1440, new GraphicsMode(32, 0, 0, 4))
{
if (settingsFile != null && File.Exists(settingsFile))
{
var jo = JObject.Parse(File.ReadAllText(settingsFile));
Back = jo["back"]?.ToObject<Color>() ?? Color.Black;
Front = jo["front"]?.ToObject<Color>() ?? Color.FromArgb(0x33, 0xff, 0xff, 0xff);
ViewHeight = jo["viewHeight"]?.ToObject<int>() ?? 4;
DisplayRate = jo["displayRate"]?.ToObject<double>() ?? 1;
Cycles = jo["cycles"]?.ToObject<int>() ?? 100;
if (jo["pendulums"] != null)
{
Solver.Pendulums.Clear();
foreach (var t in jo["pendulums"])
{
var amp = t["amplitude"]?.ToObject<Vector2d>() ?? Vector2d.UnitX;
var period = t["period"]?.ToObject<double>() ?? 1;
var phase = t["phase"]?.ToObject<double>() ?? 0;
var friction = t["friction"]?.ToObject<double>() ?? 0.005;
Solver.Add(new PendulumSolver(amp, period, phase, friction));
}
}
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Width = 2560;
Height = 1440;
X = (DisplayDevice.Default.Width - Width) / 2;
Y = (DisplayDevice.Default.Height - Height) / 2;
RecalculatePath();
}
public void RecalculatePath()
{
var m = (int) (Cycles * 200 * Tau);
var path = new Vector2[m];
Parallel.For(0, m, i => path[i] = (Vector2) Solver.At(i / 20.0));
_path = path;
}
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
var proj = Matrix4d.CreateOrthographic(ViewHeight * Width / Height, ViewHeight, -1, 1);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref proj);
GL.Viewport(ClientRectangle);
GL.ClearColor(Back);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.LineWidth(1.5f);
GL.Begin(PrimitiveType.LineStrip);
GL.Color4(Front);
for (var i = 0; i < _path.Length && i < _time; i++)
{
var p = _path[i];
GL.Vertex2(p);
}
GL.End();
SwapBuffers();
}
public void SaveScreenshot() => SaveScreenshot(DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".png");
public void SaveScreenshot(string file)
{
if (GraphicsContext.CurrentContext == null)
throw new GraphicsContextMissingException();
var w = ClientSize.Width;
var h = ClientSize.Height;
var bmp = new Bitmap(w, h);
var data = bmp.LockBits(ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
GL.ReadPixels(0, 0, w, h, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
bmp.Save(file);
}
protected override void OnKeyDown(KeyboardKeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Space)
SaveScreenshot();
if (e.Key == Key.A)
_phaseMode = !_phaseMode;
if (e.Key == Key.R)
_time = 0;
}
protected override void OnUpdateFrame(FrameEventArgs e)
{
base.OnUpdateFrame(e);
Title = $"{1 / e.Time:00.00}";
if (DisplayRate <= 0)
_time = _path.Length;
if (Focused)
_time += e.Time * DisplayRate * 100;
if (_phaseMode)
{
foreach (var p in Solver.Pendulums)
p.Phase += e.Time * _phaseRate;
RecalculatePath();
}
}
}
}

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{3DEBA9F3-AD0D-43A6-BF4E-0220BDAFB099}</ProjectGuid>
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Harmonograph</RootNamespace>
<AssemblyName>Harmonograph</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
<HintPath>..\packages\OpenTK.3.0.0-pre\lib\net20\OpenTK.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="GraphWindow.cs" />
<Compile Include="HarmonographSolver.cs" />
<Compile Include="PendulumSolver.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="OpenTK.dll.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="blackonwhite.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="bow2.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="rainbowroad.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="sparse.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="spring.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="test.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="whiteonblack.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
namespace Harmonograph
{
public class HarmonographSolver : IEnumerable<PendulumSolver>
{
public const double Tau = Math.PI / 2;
public List<PendulumSolver> Pendulums { get; } = new List<PendulumSolver>();
public void Add(PendulumSolver pendulum) => Pendulums.Add(pendulum);
public Vector2d At(double time)
{
return Pendulums.Select(p => p.At(time)).Aggregate(Vector2d.Zero, Vector2d.Add);
}
public IEnumerator<PendulumSolver> GetEnumerator()
{
return Pendulums.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable) Pendulums).GetEnumerator();
}
}
}

View File

@@ -0,0 +1,25 @@
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
<!-- XQuartz compatibility (X11 on Mac) -->
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>

View File

@@ -0,0 +1,31 @@
using System;
using OpenTK;
namespace Harmonograph
{
public class PendulumSolver
{
public const double Tau = Math.PI * 2;
public Vector2d Amplitude { get; set; }
public double Period { get; set; }
public double Phase { get; set; }
public double Friction { get; set; }
public PendulumSolver(Vector2d amplitude, double period, double phase,
double friction = 0.01)
{
Amplitude = amplitude;
Period = period;
Phase = phase;
Friction = friction;
}
public Vector2d At(double time)
{
var phase = (Phase + Period * time) / Tau;
return Amplitude * Math.Sin(phase) * Math.Exp(-time * Friction / Tau);
}
}
}

23
Harmonograph/Program.cs Normal file
View File

@@ -0,0 +1,23 @@
using System;
using System.Threading;
namespace Harmonograph
{
internal class Program
{
public static void Main(string[] args)
{
string f = null;
if (args.Length > 0) f = args[0];
if (f == "-")
{
Console.Write("input file.\n>>> ");
f = Console.ReadLine() + ".json";
}
using (var w = new GraphWindow(f))
{
w.Run();
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Harmonograph")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Harmonograph")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3DEBA9F3-AD0D-43A6-BF4E-0220BDAFB099")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,28 @@
{
"back": "0x00000000",
"front": "0x10ffffff",
"displayRate": 1000,
"viewHeight": 3,
"pendulums": [
{
"amplitude": { "x": 1.5, "y": 0 },
"period": 3.001,
"friction": 0.0005
},
{
"amplitude": { "x": 0, "y": 1 },
"period": 4.001,
"friction": 0.0005
},
{
"amplitude": { "x": .5, "y": .5 },
"period": 1.001,
"friction": 0.0005
},
{
"amplitude": { "x": -.5, "y": .5 },
"period": 2.999,
"friction": 0.0005
}
]
}

29
Harmonograph/bow2.json Normal file
View File

@@ -0,0 +1,29 @@
{
"back": "0x10ffffff",
"front": "0x10000000",
"displayRate": 1000,
"cycles": 500,
"viewHeight": 3,
"pendulums": [
{
"amplitude": { "x": 2, "y": 0 },
"period": 3.001,
"friction": 0.001
},
{
"amplitude": { "x": 0, "y": 1 },
"period": 4,
"friction": 0.001
},
{
"amplitude": { "x": .5, "y": .5 },
"period": 1,
"friction": 0.001
},
{
"amplitude": { "x": -.5, "y": .5 },
"period": 3,
"friction": 0.001
}
]
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" />
<package id="OpenTK" version="3.0.0-pre" targetFramework="net45" />
</packages>

View File

@@ -0,0 +1,29 @@
{
"back": "0x10ffffff",
"front": "0x70000000",
"displayRate": 10,
"cycles": 10,
"viewHeight": 3,
"pendulums": [
{
"amplitude": { "x": 2, "y": -.5 },
"period": 2.01,
"friction": 0.01
},
{
"amplitude": { "x": 0, "y": .5 },
"period": 5.01,
"friction": 0.01
},
{
"amplitude": { "x": .5, "y": .5 },
"period": 1,
"friction": 0.01
},
{
"amplitude": { "x": -.5, "y": .5 },
"period": 3,
"friction": 0.01
}
]
}

28
Harmonograph/sparse.json Normal file
View File

@@ -0,0 +1,28 @@
{
"back": "0xffffffff",
"front": "0xff000000",
"displayRate": 500,
"viewHeight": 3,
"pendulums": [
{
"amplitude": { "x": 1.5, "y": 0 },
"period": 3.01,
"friction": 0.02
},
{
"amplitude": { "x": 0, "y": 1 },
"period": 4.01,
"friction": 0.02
},
{
"amplitude": { "x": .5, "y": .5 },
"period": 1.01,
"friction": 0.02
},
{
"amplitude": { "x": -.5, "y": .5 },
"period": 2.98,
"friction": 0.01
}
]
}

29
Harmonograph/spring.json Normal file
View File

@@ -0,0 +1,29 @@
{
"back": "0x10ffffff",
"front": "0x70000000",
"displayRate": 1,
"cycles": 10,
"viewHeight": 3,
"pendulums": [
{
"amplitude": { "x": 2, "y": -.5 },
"period": 5.01,
"friction": 0.01
},
{
"amplitude": { "x": 0, "y": .5 },
"period": 4.01,
"friction": 0.01
},
{
"amplitude": { "x": .5, "y": .5 },
"period": 1,
"friction": 0.01
},
{
"amplitude": { "x": -.5, "y": .5 },
"period": 3,
"friction": 0.01
}
]
}

24
Harmonograph/test.json Normal file
View File

@@ -0,0 +1,24 @@
{
"back": "0x10ffffff",
"front": "0x20000000",
"displayRate": 01,
"cycles": 10,
"viewHeight": 3,
"pendulums": [
{
"amplitude": { "x": 1, "y": -.5 },
"period": 5.005,
"friction": 0.001
},
{
"amplitude": { "x": .5, "y": 1 },
"period": 4.01,
"friction": 0.001
},
{
"amplitude": { "x": .5, "y": 0 },
"period": 3.01,
"friction": 0.001
}
]
}

View File

@@ -0,0 +1,28 @@
{
"back": "0xffffffff",
"front": "0x10000000",
"displayRate": 1000,
"viewHeight": 3,
"pendulums": [
{
"amplitude": { "x": 1.5, "y": 0 },
"period": 3.001,
"friction": 0.0005
},
{
"amplitude": { "x": 0, "y": 1 },
"period": 4.001,
"friction": 0.0005
},
{
"amplitude": { "x": .5, "y": .5 },
"period": 1.001,
"friction": 0.0005
},
{
"amplitude": { "x": -.5, "y": .5 },
"period": 2.999,
"friction": 0.0005
}
]
}