From 1a3280e7a01c2d9b0c4a933b27ba0ae0962cf9ea Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Sun, 28 Jan 2018 23:30:54 +0000 Subject: [PATCH] Initial commit. I can't believe I haven't committed this already..... --- .gitignore | 446 ++++++++++++++++++++++++ SimpleTurtle.sln | 17 + SimpleTurtle/Help.txt | 45 +++ SimpleTurtle/LSystemEngine.cs | 125 +++++++ SimpleTurtle/Program.cs | 113 ++++++ SimpleTurtle/Properties/AssemblyInfo.cs | 27 ++ SimpleTurtle/SimpleTurtle.csproj | 55 +++ SimpleTurtle/SimpleTurtle.sln | 17 + SimpleTurtle/Turtle.cs | 186 ++++++++++ SimpleTurtle/Utilities/EmbeddedFiles.cs | 191 ++++++++++ SimpleTurtle/Utilities/Timer.cs | 36 ++ 11 files changed, 1258 insertions(+) create mode 100644 .gitignore create mode 100755 SimpleTurtle.sln create mode 100644 SimpleTurtle/Help.txt create mode 100755 SimpleTurtle/LSystemEngine.cs create mode 100755 SimpleTurtle/Program.cs create mode 100755 SimpleTurtle/Properties/AssemblyInfo.cs create mode 100644 SimpleTurtle/SimpleTurtle.csproj create mode 100755 SimpleTurtle/SimpleTurtle.sln create mode 100755 SimpleTurtle/Turtle.cs create mode 100644 SimpleTurtle/Utilities/EmbeddedFiles.cs create mode 100755 SimpleTurtle/Utilities/Timer.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3951092 --- /dev/null +++ b/.gitignore @@ -0,0 +1,446 @@ + +# Created by https://www.gitignore.io/api/csharp,git,monodevelop,visualstudio + +### Csharp ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Uncomment the next line to ignore your web deploy settings. +# By default, sensitive information, such as encrypted password +# should be stored in the .pubxml.user file. +#*.pubxml +*.pubxml.user +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +### Git ### +*.orig + +### MonoDevelop ### +#User Specific +*.usertasks + +#Mono Project Files +*.resources +test-results/ + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files + +# User-specific files (MonoDevelop/Xamarin Studio) + +# Build results + +# Visual Studio 2015 cache/options directory +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results + +# NUNIT + +# Build Results of an ATL Project + +# .NET Core + + +# Chutzpah Test files + +# Visual C++ cache files + +# Visual Studio profiler + +# TFS 2012 Local Workspace + +# Guidance Automation Toolkit + +# ReSharper is a .NET coding add-in + +# JustCode is a .NET coding add-in + +# TeamCity is a build add-in + +# DotCover is a Code Coverage Tool + +# Visual Studio code coverage results + +# NCrunch + +# MightyMoose + +# Web workbench (sass) + +# Installshield output folder + +# DocProject is a documentation generator add-in + +# Click-Once directory + +# Publish Web Output +# TODO: Uncomment the next line to ignore your web deploy settings. +# By default, sensitive information, such as encrypted password +# should be stored in the .pubxml.user file. +#*.pubxml + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted + +# NuGet Packages +# The packages folder can be ignored because of Package Restore +# except build/, which is used as an MSBuild target. +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files + +# Microsoft Azure Build Output + +# Microsoft Azure Emulator + +# Windows Store app package directories and files + +# Visual Studio cache files +# files ending in .cache can be ignored +# but keep track of directories ending in .cache + +# Others + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) + +# SQL Server files + +# Business Intelligence projects + +# Microsoft Fakes + +# GhostDoc plugin setting file + +# Node.js Tools for Visual Studio + +# Typescript v1 declaration files + +# Visual Studio 6 build log + +# Visual Studio 6 workspace options file + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) + +# Visual Studio LightSwitch build output + +# Paket dependency manager + +# FAKE - F# Make + +# JetBrains Rider + +# CodeRush + +# Python Tools for Visual Studio (PTVS) + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file + +# BizTalk build output + +### VisualStudio Patch ### +# By default, sensitive information, such as encrypted password +# should be stored in the .pubxml.user file. + + +# End of https://www.gitignore.io/api/csharp,git,monodevelop,visualstudio diff --git a/SimpleTurtle.sln b/SimpleTurtle.sln new file mode 100755 index 0000000..2b8ec0e --- /dev/null +++ b/SimpleTurtle.sln @@ -0,0 +1,17 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleTurtle", "SimpleTurtle\SimpleTurtle.csproj", "{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Debug|x86.ActiveCfg = Debug|x86 + {1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Debug|x86.Build.0 = Debug|x86 + {1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Release|x86.ActiveCfg = Release|x86 + {1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal diff --git a/SimpleTurtle/Help.txt b/SimpleTurtle/Help.txt new file mode 100644 index 0000000..3e1b4bc --- /dev/null +++ b/SimpleTurtle/Help.txt @@ -0,0 +1,45 @@ +SimpleTurtle L-System Renderer + by Starbeamrainbowlabs + +Usage: + ./SimpleTurtle.exe {options} + +Options: + -i --input-file The file that contains the input L-System parameters. Defaults to stdin. + -o --output-dir The output directory in which to save the rendered / simulated L-System generations. Defaults to the name of the input file, minus the file extension - or just simply 'l-system'. + -s --skip-gen The starting generation. Default: 0. + -c --count The number of generations to render. Default: 5 + -d --step-dist The number of pixels the turtle will walk forwards at a time. Default: 25. + +Input File Format: + The first line is the seed (the starting state). + Subsequent lines beginning with a # (hash sign) are ignored. + Rules are declared with an equals sign - i.e. f=f-f+, or fb=fbfafcfafb. + Lines starting with an exclamation mark specify directives that should be passed to the turtle - i.e. !angle=1.0472 + + Turtle Directives: + angle Sets the angle by which the turtle should turn. Note that this is specified in radians. + +Example Input Files: + +Hilbert Curve: +----- +a +a=-bf+afa+fb- +b=+af-bfb-fa+ +----- + +Dragon Curve: +----- +f +f=f-h +h=f+h +----- + +Sierpinski Triangle +----- +f +!angle=1.0472 +h=f+h+f +f=h-f-h +----- diff --git a/SimpleTurtle/LSystemEngine.cs b/SimpleTurtle/LSystemEngine.cs new file mode 100755 index 0000000..b8f5d12 --- /dev/null +++ b/SimpleTurtle/LSystemEngine.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Policy; + +namespace SBRL.Algorithms.LSystem +{ + public class Rule + { + public string Find; + public string Replace; + + public Rule(string inFind, string inReplace) + { + Find = inFind; + Replace = inReplace; + } + } + + /// + /// Simulates an L-System. + /// Implemented according to http://www.cs.unm.edu/~joel/PaperFoldingFractal/L-system-rules.html + /// + public class LSystemEngine + { + public readonly string Root; + private List rules = new List(); + + public int GenerationCount { get; private set; } + public string CurrentGeneration { get; private set; } + + public Dictionary Definitions { get; private set; } + + public LSystemEngine(string inRoot) + { + CurrentGeneration = Root = inRoot; + Definitions = new Dictionary(); + } + + public void AddRule(string find, string replace) + { + rules.Add(new Rule(find, replace)); + } + + public string Simulate() + { + List> rulePositions = new List>(); + // Find all the current positions + foreach(Rule rule in rules) + { + List positions = AllIndexesOf(CurrentGeneration, rule.Find); + foreach (int pos in positions) + rulePositions.Add(new KeyValuePair(pos, rule)); + } + rulePositions.Sort(compareRulePairs); + + string nextGeneration = CurrentGeneration; + int replaceOffset = 0; + foreach(KeyValuePair rulePos in rulePositions) + { + int nextPos = rulePos.Key + replaceOffset; + nextGeneration = nextGeneration.Substring(0, nextPos) + rulePos.Value.Replace + nextGeneration.Substring(nextPos + rulePos.Value.Find.Length); + replaceOffset += rulePos.Value.Replace.Length - rulePos.Value.Find.Length; + } + CurrentGeneration = nextGeneration; + GenerationCount++; + return CurrentGeneration; + } + + private int compareRulePairs(KeyValuePair a, KeyValuePair b) + { + return a.Key - b.Key; + } + + /// + /// From http://stackoverflow.com/a/2641383/1460422 + /// + /// + /// + /// + private List AllIndexesOf(string str, string value) { + if (String.IsNullOrEmpty(value)) + throw new ArgumentException("the string to find may not be empty", "value"); + List indexes = new List(); + for (int index = 0;; index += value.Length) { + index = str.IndexOf(value, index); + if (index == -1) + return indexes; + indexes.Add(index); + } + } + + public static LSystemEngine FromFile(string filename) + { + return FromStream(new StreamReader(filename)); + } + public static LSystemEngine FromStream(StreamReader source) + { + LSystemEngine resultSystem = new LSystemEngine(source.ReadLine()); + + string nextLine = string.Empty; + while (true) { + nextLine = source.ReadLine(); + if (nextLine == null) + break; + if (!nextLine.Contains("=") || nextLine.StartsWith("#") || nextLine.Trim().Length == 0) + continue; + string[] parts = nextLine.Split(new char[]{'='}, 2); + + if(parts[0].StartsWith("!")) + { + // This is a definition + resultSystem.Definitions.Add(parts[0].Trim('!'), parts[1]); + } + else + { + resultSystem.AddRule(parts[0].Trim(), parts[1].Trim()); + } + } + return resultSystem; + } + } +} + diff --git a/SimpleTurtle/Program.cs b/SimpleTurtle/Program.cs new file mode 100755 index 0000000..f074e15 --- /dev/null +++ b/SimpleTurtle/Program.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using SBRL.Utilities; +using SBRL.SimpleTurtle; + +namespace SBRL.Algorithms.LSystem +{ + class MainClass + { + public static void Main(string[] args) + { + string inputFilename = string.Empty; + string outputDirectory = string.Empty; + int startingGeneration = 0; + int generations = 5; + int stepCount = 25; + + for (int i = 0; i < args.Length; i++) + { + switch (args[i].ToLower()) + { + case "--help": + Console.WriteLine(EmbeddedFiles.ReadAllText("SimpleTurtle.Help.txt")); + return; + + case "--input-file": + case "-i": + inputFilename = args[++i]; + break; + case "--output-dir": + case "-o": + outputDirectory = args[++i]; + break; + case "--skip-gen": + case "-s": + startingGeneration = int.Parse(args[++i]); + break; + case "--count": + case "-c": + generations = int.Parse(args[++i]); + break; + case "--step-dist": + case "-d": + stepCount = int.Parse(args[++i]); + break; + } + } + + StreamReader source; + if (inputFilename == string.Empty) + source = new StreamReader(Console.OpenStandardInput()); + else + source = new StreamReader(inputFilename); + + if (outputDirectory == string.Empty) { + if (inputFilename == string.Empty) + outputDirectory = "./l-system"; + else + outputDirectory = inputFilename.Substring(0, inputFilename.LastIndexOf(".")); + } + + // Normalise & create the output directory + outputDirectory = outputDirectory.TrimEnd('/') + "/"; + if(!Directory.Exists(outputDirectory)) + Directory.CreateDirectory(outputDirectory); + + LSystemEngine lsystem = LSystemEngine.FromStream(source); + foreach (KeyValuePair kvp in lsystem.Definitions) + { + Console.WriteLine("Setting {0} to {1}.", kvp.Key, kvp.Value); + } + Turtle turtle = new Turtle(); + + Timer gentimer = Timer.GetStarted(), stagetimer = Timer.GetStarted(); + for (int i = startingGeneration; i < generations; i++) + { + stagetimer.Reset(); gentimer.Reset(); + + Console.Write($"Generation {i}: "); + turtle.ApplyDefinitions(lsystem.Definitions); + turtle.MovementStep = stepCount; + turtle.Commands(lsystem.CurrentGeneration); + turtle.Draw($"{outputDirectory}lsystem_generation_{i}.png"); + + Console.Write("{0:0.000000}s draw", stagetimer.GetElapsedSeconds()); + stagetimer.Reset(); + + // ---------- + + lsystem.Simulate(); + + Console.Write(", {0:0.000000}s simulation", stagetimer.GetElapsedSeconds()); + stagetimer.Reset(); + + // ---------- + + File.WriteAllText($"{outputDirectory}lsystem_generation_{i}.txt", lsystem.CurrentGeneration); + + Console.WriteLine( + ", {0:0.000000}s textoutput - {1:0.000000}s total", + stagetimer.GetElapsedSeconds(), + gentimer.GetElapsedSeconds() + ); + } + + //Turtle turtle = new Turtle(); + //turtle.Commands(Console.ReadLine()); + //turtle.Draw("turtleImage.png"); + } + } +} \ No newline at end of file diff --git a/SimpleTurtle/Properties/AssemblyInfo.cs b/SimpleTurtle/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..4d3778d --- /dev/null +++ b/SimpleTurtle/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("SimpleTurtle")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Starbeamrainbowlabs")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/SimpleTurtle/SimpleTurtle.csproj b/SimpleTurtle/SimpleTurtle.csproj new file mode 100644 index 0000000..26215a9 --- /dev/null +++ b/SimpleTurtle/SimpleTurtle.csproj @@ -0,0 +1,55 @@ + + + + Debug + x86 + {1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24} + Exe + SBRL.SimpleTurtle + SimpleTurtle + v4.5 + 8.0.30703 + 2.0 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + full + true + bin\Release + prompt + 4 + true + x86 + + + + + + + + + + + + + + + + SimpleTurtle.Help.txt + + + + + + + \ No newline at end of file diff --git a/SimpleTurtle/SimpleTurtle.sln b/SimpleTurtle/SimpleTurtle.sln new file mode 100755 index 0000000..ee91909 --- /dev/null +++ b/SimpleTurtle/SimpleTurtle.sln @@ -0,0 +1,17 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleTurtle", "SimpleTurtle.csproj", "{1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Debug|x86.ActiveCfg = Debug|x86 + {1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Debug|x86.Build.0 = Debug|x86 + {1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Release|x86.ActiveCfg = Release|x86 + {1AF04EB3-4F9C-4AA6-98C5-693F6B54DB24}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal diff --git a/SimpleTurtle/Turtle.cs b/SimpleTurtle/Turtle.cs new file mode 100755 index 0000000..76d426b --- /dev/null +++ b/SimpleTurtle/Turtle.cs @@ -0,0 +1,186 @@ +using System; +using Cairo; +using System.Text.RegularExpressions; +using System.Resources; +using System.Collections.Generic; + +namespace SBRL.SimpleTurtle +{ + public class Area + { + public double X; + public double Y; + public double Width; + public double Height; + + public Area(double inX, double inY, double inWidth, double inHeight) + { + X = inX; + Y = inY; + Width = inWidth; + Height = inHeight; + } + } + public class Turtle + { + private bool strictMode = false; + private string commandQueue; + + private PointD position; + private Area bounds; + private double heading; + private double headingStep; + public double HeadingStep + { + get { return headingStep; } + set { headingStep = value; } + } + private double movementStep ; + public double MovementStep + { + get { return movementStep; } + set { movementStep = value; } + } + + public Turtle () + { + Reset(); + } + + public void ApplyDefinitions(Dictionary definitions) + { + foreach(KeyValuePair definition in definitions) + { + switch(definition.Key.ToLower()) + { + case "angle": + HeadingStep = double.Parse(definition.Value); + break; + } + } + } + + public bool Commands(string commandText) + { + // Remove all whitespace + commandText = Regex.Replace(commandText, @"\s+", ""); + commandText = commandText.Replace("h", "f"); + + string okCommands = "f+-"; + + foreach(char ch in commandText) + { + if(okCommands.Contains(ch.ToString())) + { + switch(ch) + { + case 'f': + Forwards(); + break; + case '+': + Turn(false); + break; + case '-': + Turn(true); + break; + default: + if (strictMode) { + Console.WriteLine("The unexpected character '{0}' slipped through the net!", ch); + return false; + } + break; + } + } + else if(strictMode) + { + Console.Error.WriteLine("Error: unexpected character '{0}'", ch); + return false; + } + } + commandQueue += commandText; + return true; + } + + public void Draw(string filename, bool reset = true) + { + ImageSurface canvas = new ImageSurface(Format.ARGB32, (int)Math.Ceiling(bounds.Width + 10), (int)Math.Ceiling(bounds.Height + 10)); + Context context = new Context(canvas); + position = new PointD(-bounds.X + 5, -bounds.Y + 5); + heading = 0; + + context.LineWidth = 3; + context.MoveTo(position); + foreach(char ch in commandQueue) + { + switch(ch) + { + case 'f': + PointD newPosition = new PointD( + position.X + movementStep * Math.Sin(heading), + position.Y + movementStep * Math.Cos(heading) + ); + context.LineTo(newPosition); + position = newPosition; + break; + case '+': + heading += headingStep; + break; + case '-': + heading -= headingStep; + break; + } + } + + context.Stroke(); + canvas.WriteToPng(string.Format(filename)); + context.Dispose(); + canvas.Dispose(); + if(reset) + Reset(); + } + + public void Forwards() + { + PointD newPosition = new PointD( + position.X + MovementStep * Math.Sin(heading), + position.Y + MovementStep * Math.Cos(heading) + ); + + if (newPosition.X > bounds.X + bounds.Width) + bounds.Width += newPosition.X - position.X; + if (newPosition.Y > bounds.Y + bounds.Height) + bounds.Height += newPosition.Y - position.Y; + if (newPosition.X < bounds.X) + { + bounds.X = newPosition.X; + bounds.Width += position.X - newPosition.X; + } + if (newPosition.Y < bounds.Y) + { + bounds.Y = newPosition.Y; + bounds.Height += position.Y - newPosition.Y; + } + + position = newPosition; + } + + public void Turn(bool anticlockwise = false) + { + if (!anticlockwise) + heading += HeadingStep; + else + heading -= HeadingStep; + } + + public void Reset() + { + commandQueue = string.Empty; + position = new PointD(0, 0); + bounds = new Area(position.X, position.Y, 1, 1); + heading = 0; + headingStep = Math.PI / 2; + movementStep = 25; + } + } +} + diff --git a/SimpleTurtle/Utilities/EmbeddedFiles.cs b/SimpleTurtle/Utilities/EmbeddedFiles.cs new file mode 100644 index 0000000..9b101ba --- /dev/null +++ b/SimpleTurtle/Utilities/EmbeddedFiles.cs @@ -0,0 +1,191 @@ +using System; +using System.Reflection; +using System.IO; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace SBRL.Utilities +{ + /// + /// A collection of static methods for manipulating embedded resources. + /// + /// + /// v0.5, by Starbeamrainbowlabs + /// Last updated 8th August 2016. + /// Licensed under MPL-2.0. + /// + /// Changelog: + /// v0.1 (25th July 2016): + /// - Initial release. + /// v0.2 (8th August 2016): + /// - Changed namespace. + /// v0.3 (21st January 2017): + /// - Added GetRawReader(). + /// v0.4 (8th April 2017): + /// - Removed unnecessary using statement. + /// v0.5 (3rd September 2017): + /// - Changed namespace + /// + public static class EmbeddedFiles + { + /// + /// An array of the filenames of all the resources embedded in the calling assembly. + /// + /// The resource list. + public static string[] ResourceList + { + get + { + return Assembly.GetCallingAssembly().GetManifestResourceNames(); + } + } + + public static string GetResourceListText() + { + StringWriter result = new StringWriter(); + result.WriteLine("Files embedded in {0}:", Assembly.GetCallingAssembly().GetName().Name); + foreach (string filename in ResourceList) + result.WriteLine(" - {0}", filename); + return result.ToString(); + } + /// + /// Writes a list of embedded resources to the Console's standard output. + /// + public static void WriteResourceList() + { + Console.WriteLine(GetResourceListText()); + } + + /// + /// Gets a StreamReader attached to the specified embedded resource. + /// + /// The filename of the embedded resource to get a StreamReader of. + /// A StreamReader attached to the specified embedded resource. + public static StreamReader GetReader(string filename) + { + return new StreamReader(GetRawReader(filename)); + } + + /// + /// Gets a raw Stream that's attached to the specified embedded resource. + /// Useful when you want to copy an embedded resource to some other stream. + /// + /// The path to the embedded resource. + /// A raw Stream object attached to the specified file.. + public static Stream GetRawReader(string filename) + { + return Assembly.GetCallingAssembly().GetManifestResourceStream(filename); + } + + /// + /// Gets the specified embedded resource's content as a byte array. + /// + /// The filename of the embedded resource to get conteent of. + /// The specified embedded resource's content as a byte array. + public static byte[] ReadAllBytes(string filename) + { + // Referencing the Result property will block until the async method completes + return ReadAllBytesAsync(filename).Result; + } + /// + /// Gets the specified embedded resource's content as a byte array asynchronously. + /// + /// The filename of the embedded resource to get conteent of. + /// The specified embedded resource's content as a byte array. + public static async Task ReadAllBytesAsync(string filename) + { + using (Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(filename)) + using (MemoryStream temp = new MemoryStream()) + { + await resourceStream.CopyToAsync(temp); + return temp.ToArray(); + } + } + + /// + /// Gets all the text stored in the specified embedded resource. + /// + /// The filename to fetch the content of. + /// All the text stored in the specified embedded resource. + public static string ReadAllText(string filename) + { + using (StreamReader resourceReader = new StreamReader(Assembly.GetCallingAssembly().GetManifestResourceStream(filename))) + { + return resourceReader.ReadToEnd(); + } + } + /// + /// Gets all the text stored in the specified embedded resource asynchronously. + /// + /// The filename to fetch the content of. + /// All the text stored in the specified embedded resource. + public static async Task ReadAllTextAsync(string filename) + { + using (StreamReader resourceReader = new StreamReader(Assembly.GetCallingAssembly().GetManifestResourceStream(filename))) + { + return await resourceReader.ReadToEndAsync(); + } + } + + /// + /// Enumerates the lines of text in the specified embedded resource. + /// + /// The filename of the embedded resource to enumerate. + /// An IEnumerator that enumerates the specified embedded resource. + public static IEnumerator EnumerateLines(string filename) + { + using (StreamReader resourceReader = new StreamReader(Assembly.GetCallingAssembly().GetManifestResourceStream(filename))) + { + string nextLine; + while ((nextLine = resourceReader.ReadLine()) != null) + { + yield return nextLine; + } + } + } + /// + /// Enumerates the lines of text in the specified embedded resource asynchronously. + /// Each successive call returns a task that, when complete, returns the next line of text stored + /// in the embedded resource. + /// + /// The filename of the embedded resource to enumerate. + /// An IEnumerator that enumerates the specified embedded resource. + public static IEnumerable> EnumerateLinesAsync(string filename) + { + using (StreamReader resourceReader = new StreamReader(Assembly.GetCallingAssembly().GetManifestResourceStream(filename))) + { + while (!resourceReader.EndOfStream) + { + yield return resourceReader.ReadLineAsync(); + } + } + } + + /// + /// Gets all the lines of text in the specified embedded resource. + /// You might find EnumerateLines(string filename) more useful depending on your situation. + /// + /// The filename to obtain the lines of text from. + /// A list of lines in the specified embedded resource. + public static List GetAllLines(string filename) + { + // Referencing the Result property will block until the async method completes + return GetAllLinesAsync(filename).Result; + } + /// + /// Gets all the lines of text in the specified embedded resource asynchronously. + /// + /// The filename to obtain the lines of text from. + /// A list of lines in the specified embedded resource. + public static async Task> GetAllLinesAsync(string filename) + { + List lines = new List(); + IEnumerable> lineIterator = EnumerateLinesAsync(filename); + foreach (Task nextLine in lineIterator) + { + lines.Add(await nextLine); + } + return lines; + } + } +} diff --git a/SimpleTurtle/Utilities/Timer.cs b/SimpleTurtle/Utilities/Timer.cs new file mode 100755 index 0000000..547c7ad --- /dev/null +++ b/SimpleTurtle/Utilities/Timer.cs @@ -0,0 +1,36 @@ +using System; + +namespace SBRL.Utilities +{ + public class Timer + { + public DateTime StartTime { get; private set; } + + public Timer() { } + + public static Timer GetStarted() + { + Timer result = new Timer(); + result.Start(); + return result; + } + + public void Start() + { + StartTime = DateTime.Now; + } + + public float GetElapsedSeconds() + { + DateTime now = DateTime.Now; + TimeSpan elasped = now - StartTime; + StartTime = now; + return (float)elasped.Ticks / TimeSpan.TicksPerSecond; + } + + public void Reset() + { + Start(); + } + } +} \ No newline at end of file