From f7bcde91ffd1ec285a3f6d3febbb2433daf96349 Mon Sep 17 00:00:00 2001 From: Rory& Date: Fri, 23 Feb 2024 12:33:12 +0100 Subject: [PATCH] Identicons --- .gitignore | 1 + ArcaneLibs.UsageTest/Program.cs | 88 ++++++++++++++++++----- ArcaneLibs/Extensions/StringExtensions.cs | 4 ++ ArcaneLibs/SvgIdenticonGenerator.cs | 51 +++++++++++++ 4 files changed, 125 insertions(+), 19 deletions(-) create mode 100644 ArcaneLibs/SvgIdenticonGenerator.cs diff --git a/.gitignore b/.gitignore index 8afdcb6..8563eda 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +ArcaneLibs.UsageTest/identicons/ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## diff --git a/ArcaneLibs.UsageTest/Program.cs b/ArcaneLibs.UsageTest/Program.cs index ccc8b88..a4753fa 100644 --- a/ArcaneLibs.UsageTest/Program.cs +++ b/ArcaneLibs.UsageTest/Program.cs @@ -1,5 +1,6 @@ // See https://aka.ms/new-console-template for more information +using System.Diagnostics; using System.Drawing; using ArcaneLibs; using ArcaneLibs.Collections; @@ -48,23 +49,72 @@ Console.WriteLine(Util.GetCommandOutputSync("bash", "-c asdf")); Console.WriteLine(Util.GetCommandOutputSync("bash", "-c ls")); -var arc = new RandomClass { - Id = 1, - Name = "asdf", - DateCreated = DateTime.Now, - SubClassInst = new RandomClass.SubClass { - Description = "asdf" - } -}; -var brc = new RandomClass { - Id = 2, - Name = "asdf", - DateCreated = DateTime.Now, - SubClassInst = new RandomClass.SubClass { - Description = "asdf" - } -}; +// var arc = new RandomClass { +// Id = 1, +// Name = "asdf", +// DateCreated = DateTime.Now, +// SubClassInst = new RandomClass.SubClass { +// Description = "asdf" +// } +// }; +// var brc = new RandomClass { +// Id = 2, +// Name = "asdf", +// DateCreated = DateTime.Now, +// SubClassInst = new RandomClass.SubClass { +// Description = "asdf" +// } +// }; +// +// var differences = arc.FindDifferencesDeep(brc); +// Console.WriteLine(differences.left.ToJson()); +// Console.WriteLine(differences.right.ToJson()); -var differences = arc.FindDifferencesDeep(brc); -Console.WriteLine(differences.left.ToJson()); -Console.WriteLine(differences.right.ToJson()); \ No newline at end of file +// identicon test +Directory.CreateDirectory("identicons"); + +// for (int i = 0; i < 100; i++) { +// var ident = new SvgIdenticonGenerator(); +// var sw = Stopwatch.StartNew(); +// var count = 0; +// while (sw.ElapsedMilliseconds < 10) { +// count++; +// var hash = Guid.NewGuid().ToString("N"); +// var svg = ident.Generate(hash); +// // File.WriteAllText($"identicons/{hash}.svg", svg); +// } +// +// Console.WriteLine($"Generated {count} identicons in {sw.Elapsed}"); +// await Task.Delay(1000); +// } + +// ident.Generate("asdf"); + +List values = [ + "Zoey", + "Willow", + "Whiskers", + "Zoe", + "Sam", + "Salem", + "Snickers", + "Snowball", + "Angel", + "Simon", + "Charlie", + "Daisy", + "Sasha", + "Samantha", + "Sammy", + "Dusty", + "Sassy", + "Annie", + "Felix", + "Pepper" +]; + +var ident2 = new SvgIdenticonGenerator(); +foreach (var value in values) { + var svg = ident2.Generate(value); + File.WriteAllText($"identicons/{values.IndexOf(value)} - {value}.svg", svg); +} \ No newline at end of file diff --git a/ArcaneLibs/Extensions/StringExtensions.cs b/ArcaneLibs/Extensions/StringExtensions.cs index 0459472..2fa0716 100644 --- a/ArcaneLibs/Extensions/StringExtensions.cs +++ b/ArcaneLibs/Extensions/StringExtensions.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text; using System.Text.RegularExpressions; +using System.Web; namespace ArcaneLibs.Extensions; @@ -69,4 +70,7 @@ public static string[] ParseArguments(this string text) => .SelectMany(element => element).ToArray(); public static IEnumerable AsBytes(this string str) => Encoding.UTF8.GetBytes(str); + + public static string UrlEncode(this string str) => HttpUtility.UrlEncode(str); + public static string UrlDecode(this string str) => HttpUtility.UrlDecode(str); } \ No newline at end of file diff --git a/ArcaneLibs/SvgIdenticonGenerator.cs b/ArcaneLibs/SvgIdenticonGenerator.cs new file mode 100644 index 0000000..c5c5015 --- /dev/null +++ b/ArcaneLibs/SvgIdenticonGenerator.cs @@ -0,0 +1,51 @@ +using System.Security.Cryptography; +using ArcaneLibs.Extensions; + +namespace ArcaneLibs; + +public class SvgIdenticonGenerator { + // based on https://github.com/stewartlord/identicon.js/blob/master/identicon.js + + public string BackgroundColor { get; set; } = "#FEFEFEFF"; + public float Saturation { get; set; } = 0.7f; + public float Brightness { get; set; } = 0.5f; + public string? ForegroundColor { get; set; } + + + public string Generate(string identity) { + var hash = SHA1.HashData(identity.AsBytes().ToArray()); + + + var hashArray = new byte[hash.Length]; + for (var i = 0; i < hash.Length; i++) { + hashArray[i] = hash[i]; + } + + var hashInt = BitConverter.ToInt32(hashArray, 0); + var hashIntAbs = Math.Abs(hashInt); + var color = ForegroundColor ?? $"#{(hashIntAbs % 0xFFFFFF):X6}"; + + var svg = ""; + svg += $""; + for (var i = 0; i < 25; i++) { + if ((hashIntAbs & (1 << i)) != 0) { + var x = i % 3; // Only use the first 3 columns + var y = (int)Math.Floor(i / 5f); + svg += $""; + svg += $""; // Mirror the square to the other half + } + } + + svg += ""; + return svg; + } + + public string GenerateAsBase64(string identity) { + return Convert.ToBase64String(Generate(identity).AsBytes().ToArray()); + } + + public string GenerateAsDataUri(string identity) { + return $"data:image/svg+xml;base64,{GenerateAsBase64(identity)}"; + } + +} \ No newline at end of file