public static List <TreeNode> GetRecipes() { var categoryNodes = new List <TreeNode>(); foreach (var dict in Locate.GetCategorizedRecipes()) { string category = dict.Key; IRecipe[] recipes = dict.Value; var categoryNode = new TreeNode(category); categoryNodes.Add(categoryNode); foreach (IRecipe recipe in recipes) { categoryNode.Items.Add(new TreeNode(recipe)); } } return(categoryNodes); }
/// <summary> /// Use REFLECTION to locate all recipes, execute them and save the output images. /// </summary> /// <returns>array of recipes found using reflection</returns> public static IRecipe[] Generate(string outputPath, int width = 600, int height = 400, int thumbJpegQuality = 95) { outputPath = Path.GetFullPath(outputPath); if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); } IRecipe[] recipes = Locate.GetRecipes(); EncoderParameters thumbJpegEncoderParameters = new(1); thumbJpegEncoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, thumbJpegQuality); ImageCodecInfo thumbJpegEncoder = ImageCodecInfo.GetImageEncoders().Where(x => x.MimeType == "image/jpeg").First(); Parallel.ForEach(recipes, recipe => { var sw = System.Diagnostics.Stopwatch.StartNew(); var plt = new Plot(width, height); recipe.ExecuteRecipe(plt); // save full size image Bitmap bmp = plt.Render(); string filePath = Path.Combine(outputPath, recipe.ID.ToLower() + ".png"); bmp.Save(filePath, ImageFormat.Png); // calculate image hash Console.WriteLine($"{recipe.ID},{Tools.BitmapHash(bmp)},{sw.Elapsed.TotalMilliseconds}"); // thumbnail int thumbHeight = 180; int thumbWidth = thumbHeight * bmp.Width / bmp.Height; Bitmap thumb = Drawing.GDI.Resize(bmp, thumbWidth, thumbHeight); string thumbFilePath = Path.Combine(outputPath, recipe.ID.ToLower() + "_thumb.jpg"); thumb.Save(thumbFilePath, thumbJpegEncoder, thumbJpegEncoderParameters); }); return(recipes); }
/// <summary> /// Use a combination of file reading and reflection to get fields and source code for all recipes /// </summary> public List <(string id, string title, string description, string source)> GetRecipeSources(string sourcePath) { sourcePath = Path.GetFullPath(sourcePath); if (!File.Exists(Path.Combine(sourcePath, "IRecipe.cs"))) { throw new ArgumentException("IRecipe.cs can not be found in the given source colder"); } var sources = new List <(string id, string title, string description, string source)>(); string[] projectCsFiles = Directory.GetFiles(sourcePath, "*.cs", SearchOption.AllDirectories); foreach (string csFilePath in projectCsFiles) { string sourceCode = File.ReadAllText(csFilePath); // ensure the source code is not from this file if (Path.GetFileName(csFilePath) == "Chef.cs") { continue; } // ensure the start of the recipe is in the file string recipeStart = ": IRecipe"; if (!sourceCode.Contains(recipeStart)) { continue; } // isolate individual recipes from files with multiple recipes string[] sourceCodeByClass = sourceCode.Split( separator: new string[] { recipeStart }, options: StringSplitOptions.RemoveEmptyEntries) .Skip(1) .ToArray(); foreach (string singleClassSourceCode in sourceCodeByClass) { // ensure functions are at the correct indentation level int executionMethodCount = Regex.Matches(singleClassSourceCode, ExecutionMethod).Count; string indentedMethod = "\n " + ExecutionMethod; int indentedMethodCount = Regex.Matches(singleClassSourceCode, indentedMethod).Count; if (executionMethodCount != indentedMethodCount) { throw new InvalidOperationException($"Source code parsing error in: {csFilePath}\n\n" + "This is typically caused by an error in indentation and whitespace before '{ExecutionMethod}'.\n\n" + "Ensure cookbook classes are standalone classes not encased by another class."); } // read the file's source code for primary recipe components string id = GetRecipeID(singleClassSourceCode); IRecipe recipe = Locate.GetRecipe(id); string source = $"var plt = new ScottPlot.Plot({Width}, {Height});\n\n" + GetRecipeSource(singleClassSourceCode, csFilePath) + "\n\n" + $"plt.SaveFig(\"{id}{Ext}\");"; sources.Add((recipe.ID, recipe.Title, recipe.Description, source)); } } return(sources); }