public SortingViewModel(IFileListGenerator fileListGenerator, ImageSortingModule.IImageSorter imageSorter, IDirectorySelector directorySelector, IDialogHostWrapper dialogHostWrapper) { this.fileListGenerator = fileListGenerator; sorter = imageSorter; this.directorySelector = directorySelector; this.dialogHostWrapper = dialogHostWrapper; Initialize(); }
/// <summary> /// Constructs a MaxRects algorithm with a given image sorter /// </summary> /// <param name="sorter">Image sorter to be used</param> public MaximalRectanglesAlgorithm(IImageSorter sorter) { this.placementAlgorithm = new GuillotinePlacementAlgorithm(new BestAreaFitFreeRectangleExtractor(), new MaxRectsFreeRectangleSplitter(), new MaxRectsFreeRectangleSortedMerger(), rectOrientationSelector: null, new MaxRectsFreeRectanglePostProcessor(), sorter); }
private void StartSort() { try { string str = this.commandLineParser.SortDirectory ?? this.systemContext.GetCurrentWorkingDirectory(); string outputDirectory = string.IsNullOrWhiteSpace(this.commandLineParser.OutputDirectory) ? str : this.commandLineParser.OutputDirectory; this.RaiseSorterInitialized(str, outputDirectory); IImageSorter imageSorter = this.imageManipulatorFactory.CreateImageSorter(this.componentsFactory, this.systemContext); imageSorter.MessageSent += new EventHandler <MessageSentEventArgs>(this.HandleMessageSent); imageSorter.UserInput += new EventHandler <UserInputEventArgs>(this.HandleUserInput); imageSorter.ProgressChanged += new EventHandler <ProgressChangedEventArgs>(this.HandleProgressChanged); imageSorter.Completed += new EventHandler <CompletedEventArgs>(this.HandleImageSorterCompleted); this.stopWatch.Start(); imageSorter.SortImages(str, !this.commandLineParser.IsNoRotate, outputDirectory, this.commandLineParser.NamePattern); imageSorter.MessageSent -= new EventHandler <MessageSentEventArgs>(this.HandleMessageSent); imageSorter.UserInput -= new EventHandler <UserInputEventArgs>(this.HandleUserInput); imageSorter.ProgressChanged -= new EventHandler <ProgressChangedEventArgs>(this.HandleProgressChanged); imageSorter.Completed -= new EventHandler <CompletedEventArgs>(this.HandleImageSorterCompleted); } catch (Exception ex) { this.SendMessage(new MessageSentEventArgs(ex.Message, MessageType.Error)); } }
/// <summary> /// Accepts additional parameters and runs the tests inside the given class /// </summary> /// <typeparam name="T">The type of class that contains the tests</typeparam> private static void RunTests <T>() { if (typeof(T) != typeof(IPlacementAlgorithm) && typeof(T) != typeof(IMinimumBoundingBoxFinder)) { throw new NotSupportedException(); } string str = ""; IImageSorter selectedImageSorter = null; IPlacementAlgorithm selectedPlacementAlgorithm = null; //Ask the user to select an image sorter that will parametrize the placement algorithm / minimum bounding box finder while (true) { do { Console.WriteLine("Select image sorter to be used: "); Console.WriteLine($"\tEnter 'a' to select {typeof(ByHeightAndWidthImageSorter).Name}"); Console.WriteLine($"\tEnter 'b' to select {typeof(ByHeightAndWidthImageSorterDesc).Name}"); Console.WriteLine($"\tEnter 'c' to select {typeof(PreserveOrderImageSorter).Name}"); } while ((str = Console.ReadLine()).Length != 1); switch (str[0]) { case 'a': selectedImageSorter = new ByHeightAndWidthImageSorter(); break; case 'b': selectedImageSorter = new ByHeightAndWidthImageSorterDesc(); break; case 'c': selectedImageSorter = new PreserveOrderImageSorter(); break; default: continue; } break; } //If the users wants to benchmark Minimum bounding box finders // then ask the user to select an placement algorithm that will parametrize tminimum bounding box finders if (typeof(T) == typeof(IMinimumBoundingBoxFinder)) { while (true) { do { Console.WriteLine("Select placement algorithm to be used: "); Console.WriteLine($"\tEnter 'a' to select {typeof(BLAlgorithmPacker).Name}"); Console.WriteLine($"\tEnter 'b' to select {typeof(SkylineAlgorithm).Name}"); Console.WriteLine($"\tEnter 'c' to select {typeof(MaximalRectanglesAlgorithm).Name}"); } while ((str = Console.ReadLine()).Length != 1); switch (str[0]) { case 'a': selectedPlacementAlgorithm = new BLAlgorithmPacker(); break; case 'b': selectedPlacementAlgorithm = new SkylineAlgorithm(); break; case 'c': selectedPlacementAlgorithm = new MaximalRectanglesAlgorithm(); break; default: continue; } break; } } bool isItSquaresTest = true; int numOfRects = 0; int seed = 0; //Ask the user to select test type //Currently, two types of tests are implemented (differing in the input sequence) // 1) Input sequence consisting of n squares with sizes 1x1, ..., nxn (given in random order) // 2) Input sequence consisting of n rectangles with random (but upper-bounded) sizes (given in random order) while (true) { str = ""; do { Console.WriteLine("Select test type:"); Console.WriteLine("\tEnter 'a' to perform test with squares of sizes 1x1, 2x2, ..., nxn"); Console.WriteLine("\tEnter 'b' to perform test with n rectangles of random sizes"); } while ((str = Console.ReadLine()).Length != 1); switch (str[0]) { case 'b': case 'a': isItSquaresTest = str[0] == 'a'; //Ask the user to enter number of rectangles/squares while (true) { Console.WriteLine("\tEnter number of 'n' - rectangles(squares)"); str = Console.ReadLine(); if (Int32.TryParse(str, out numOfRects)) { if (numOfRects > 0) { break; } } } //If it is the random rectangles test then ask the user to enter the random seed // The seed will be user to construct the random generator used to generate the random rectangle sizes if (!isItSquaresTest) { while (true) { Console.WriteLine("\tEnter seed"); str = Console.ReadLine(); if (Int32.TryParse(str, out seed)) { break; } } } break; default: continue; } break; } //Now when the user has selected the type of test and all the parameters // It is time to actaully run the tests. But because we need to test types // that are contained within plugins and also because BenchmarkDotNet (in a standard scenario) // requires to use (static) attribute [Benchmark] on the methods that should be called // we have decided to generate the benchmark class (with benchmark methods) dynamically at runtime // and then compile the generated class and pass the type of this class to the BenchmarkRunner string assemblyName = Path.GetRandomFileName(); //Assemblies referenced by the assembly that will be generated at runtime //For each type that will be used in the generated test, an assembly containing //the used type has to be loaded var references = new List <MetadataReference>() { MetadataReference.CreateFromFile(typeof(BenchmarkDotNet.Attributes.BenchmarkAttribute).Assembly.Location), MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.CSharp")).Location), MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("netstandard")).Location), MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("mscorlib")).Location), MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("System.Runtime")).Location), MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Unity.Container")).Location), MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Unity.Abstractions")).Location), MetadataReference.CreateFromFile(typeof(IMinimumBoundingBoxFinder).Assembly.Location), MetadataReference.CreateFromFile(typeof(TestUtil).Assembly.Location), MetadataReference.CreateFromFile(typeof(CancellationToken).Assembly.Location), MetadataReference.CreateFromFile(typeof(UnityContainerExtensions).Assembly.Location), MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location), MetadataReference.CreateFromFile(typeof(Random).Assembly.Location), MetadataReference.CreateFromFile(typeof(System.Collections.Concurrent.ConcurrentDictionary <string, int>).Assembly.Location), MetadataReference.CreateFromFile(typeof(ValueTuple <long, long>).Assembly.Location) }; //Create same IoC here and in the generated class //This is because inside this class we try if (using the same IoC container with same registrations) //a given type could be resolved and if it does not, we exclude benchmark for this type in the generated class using UnityContainer IoC = new UnityContainer(); IoC.RegisterFactory <IImageSorter>(_ => selectedImageSorter); if (selectedPlacementAlgorithm != null) { IoC.RegisterFactory <IPlacementAlgorithm>(_ => selectedPlacementAlgorithm); } //The generated source var benchmarkClassTemplate = new StringBuilder(); //Using statements benchmarkClassTemplate.AppendLine("using System;"); benchmarkClassTemplate.AppendLine("using Unity;"); benchmarkClassTemplate.AppendLine("using System.Linq;"); benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.MBBF;"); benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Placement;"); benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Sorting;"); benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Placement.Skyline;"); benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Placement.Guillotine;"); benchmarkClassTemplate.AppendLine("using PaunPacker.Core.Packing.Placement.MaximalRectangles;"); benchmarkClassTemplate.AppendLine("using System.Collections.Generic;"); benchmarkClassTemplate.AppendLine("using System.Threading;"); benchmarkClassTemplate.AppendLine("using PaunPacker.Tests;"); //Beginning of the class benchmarkClassTemplate.AppendLine($"public class {typeof(T).Name}Benchmarks"); benchmarkClassTemplate.AppendLine("{"); //Member declarations benchmarkClassTemplate.AppendLine(@" private static UnityContainer IoC { get; set; } private static Random rnd; private static List<PaunPacker.Core.Types.PPRect> randomRects; static System.Collections.Concurrent.ConcurrentDictionary<string, ValueTuple<long, long>> results; "); benchmarkClassTemplate.AppendLine($"\n\tstatic {typeof(T).Name}Benchmarks()"); benchmarkClassTemplate.AppendLine("\t{"); //Constructor code benchmarkClassTemplate.AppendLine($@" IoC = new UnityContainer(); IoC.RegisterInstance<IImageSorter>(new {selectedImageSorter.GetType().Name}()); results = new System.Collections.Concurrent.ConcurrentDictionary<string, ValueTuple<long, long>>(); rnd = new Random(" + seed + @"); randomRects = new List<PaunPacker.Core.Types.PPRect>(" + numOfRects + @"); int dimensionBound = Int32.MaxValue / " + numOfRects + @"; dimensionBound = dimensionBound > 2049 ? 2049 : dimensionBound; for (int i = 0; i < " + numOfRects + @"; i++) { randomRects.Add(new PaunPacker.Core.Types.PPRect(0, 0, rnd.Next(1, dimensionBound), rnd.Next(1, dimensionBound))); } "); if (selectedPlacementAlgorithm != null) { benchmarkClassTemplate.AppendLine($"IoC.RegisterInstance<IPlacementAlgorithm>(new { selectedPlacementAlgorithm.GetType().Name }());"); } benchmarkClassTemplate.AppendLine("\t}"); //Constructor ends here if (isItSquaresTest) { benchmarkClassTemplate.AppendLine($@" [BenchmarkDotNet.Attributes.ParamsSourceAttribute(nameof(ValuesForN))] public int N; public IEnumerable<int> ValuesForN => System.Linq.Enumerable.Range(1, {numOfRects}); "); } int numOfTests = 0; // We are testing minimum bounding box finders, so generate tests for them if (selectedPlacementAlgorithm != null) { foreach (var x in MinimumBoundingBoxFinderTypes) { try { UnityContainerExtensions.Resolve(IoC, Type.GetType(x.AssemblyQualifiedName), null); references.Add(MetadataReference.CreateFromFile(x.Assembly.Location)); benchmarkClassTemplate.AppendLine("\t[BenchmarkDotNet.Attributes.BenchmarkAttribute]"); benchmarkClassTemplate.AppendLine($"\tpublic void Test{x.Name}()"); benchmarkClassTemplate.AppendLine("{"); benchmarkClassTemplate.AppendLine($"\tvar x = Type.GetType(\"{x.AssemblyQualifiedName}\");"); benchmarkClassTemplate.AppendLine($"\tvar res = (UnityContainerExtensions.Resolve(IoC, x, null) as IMinimumBoundingBoxFinder).FindMinimumBoundingBox(TestUtil.Shuffle(" + (isItSquaresTest ? "TestUtil.GetIncreasingSquares(N)" : $"randomRects") + "), CancellationToken.None);"); benchmarkClassTemplate.AppendLine($"results.AddOrUpdate(\"Test{x.Name}\", (res.Width * res.Height, 1), (key, old) => ((long)(old.Item1 + (double)(res.Width * res.Height - old.Item1) / (double)(old.Item2 + 1)), old.Item2 + 1));"); benchmarkClassTemplate.AppendLine("}"); benchmarkClassTemplate.AppendLine(); numOfTests++; } catch (ResolutionFailedException) { //Do not add benchmark for types that could not be resolved (for example types that are extensible by other plugins // via plugin view ...). These types will simply not be benchmarked. } } } else { foreach (var x in PlacementAlgorithmTypes) { try { UnityContainerExtensions.Resolve(IoC, Type.GetType(x.AssemblyQualifiedName), null); references.Add(MetadataReference.CreateFromFile(x.Assembly.Location)); benchmarkClassTemplate.AppendLine("\t[BenchmarkDotNet.Attributes.BenchmarkAttribute]"); benchmarkClassTemplate.AppendLine($"\tpublic void Test{x.Name}()"); benchmarkClassTemplate.AppendLine("{"); benchmarkClassTemplate.AppendLine($"\tvar x = Type.GetType(\"{x.AssemblyQualifiedName}\");"); benchmarkClassTemplate.AppendLine($"\tvar res = (UnityContainerExtensions.Resolve(IoC, x, null) as IPlacementAlgorithm).PlaceRects(Int32.MaxValue, Int32.MaxValue, TestUtil.Shuffle(" + (isItSquaresTest ? "TestUtil.GetIncreasingSquares(N)" : $"randomRects") + "));"); benchmarkClassTemplate.AppendLine("long actualW = res.Rects.Max(y => y.Right); long actualH = res.Rects.Max(y => y.Bottom);"); benchmarkClassTemplate.AppendLine($"checked {{ results.AddOrUpdate(\"Test{x.Name}\", (actualW * actualH, 1), (key, old) => ((long)(old.Item1 + (double)(actualW * actualH - old.Item1) / (double)(old.Item2 + 1)), old.Item2 + 1));"); benchmarkClassTemplate.AppendLine("}}"); benchmarkClassTemplate.AppendLine(); numOfTests++; } catch (ResolutionFailedException) { //Do not add benchmark for types that could not be resolved (for example types that are extensible by other plugins // via plugin view ...). These types will simply not be benchmarked. } } } //Global cleanup method //Because BenchmarkDotNet does not allow to report benchmark methods return values //We had to "hack" around it by using these two methods //And save the method results inside a file and then (when showing the report) load it from the file //The GlobalCleanup should not be measured as a part of the benchmark //However we still need to somewhere remember the return value of the methods //Because writing to file in the benchmark method itself would totally kill the performance, we have decided to store it in dictionary //Which still causes some test distortion but it should be OK (all the tests use it so their base-line is just shifted) benchmarkClassTemplate.AppendLine(@" [BenchmarkDotNet.Attributes.GlobalCleanup] public void GlobalCleanup() { //We want to call it only once, not after every [Benchmark] method if (results.Count() == " + numOfTests + @") { using (var sw = new System.IO.StreamWriter(""results.txt"", true)) { //foreach (var r in randomRects) //for debug purposes // sw.WriteLine(r.Width + ""x"" + r.Height); foreach (var res in results) sw.WriteLine(res.Key + "";"" + res.Value.Item1 + "";"" + res.Value.Item1 + "";"" + res.Value.Item2); } results.Clear(); } } "); benchmarkClassTemplate.AppendLine("}"); //Benchmark class ends here Console.WriteLine("Please wait ... (Compiling the Benchmarks)"); //Create syntax tree and compile it SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(benchmarkClassTemplate.ToString()); CSharpCompilation compilation = CSharpCompilation.Create( assemblyName, syntaxTrees: new[] { syntaxTree }, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release)); //Emit the CIL into memory stream using var ms = new MemoryStream(); EmitResult result = compilation.Emit(ms); if (!result.Success) { IEnumerable <Microsoft.CodeAnalysis.Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); foreach (Microsoft.CodeAnalysis.Diagnostic diagnostic in failures) { Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage()); } } else { ms.Seek(0, SeekOrigin.Begin); Assembly assembly = Assembly.Load(ms.ToArray()); Type type = assembly.GetType($"{typeof(T).Name}Benchmarks"); Console.WriteLine("Starting the Benchmarks"); var x = BenchmarkDotNet.Running.BenchmarkRunner.Run(type, new Config()); Console.WriteLine("Done"); } }
/// <summary> /// Constructs a SkylineAlgorithm with a given image sorter and ISkylineRectAndPointPicker /// </summary> /// <param name="imageSorter">The image sorter to be used</param> /// <param name="rectangleAndPointPicker">The rectangle and point picker to be used</param> /// <remarks> /// When <paramref name="imageSorter"/> is null, the <see cref="ByHeightAndWidthImageSorterDesc"/> is used as a default image sorter /// When <paramref name="rectangleAndPointPicker"/> is null, the <see cref="MinimalAreaWasteRectAndPointPicker"/> is used as a default rect and point picker /// </remarks> public SkylineAlgorithm(IImageSorter imageSorter = null, ISkylineRectAndPointPicker rectangleAndPointPicker = null) { feasiblePoints = new LinkedList <FeasiblePoint>(); this.imageSorter = imageSorter ?? new ByHeightAndWidthImageSorterDesc(); this.rectAndPointPicker = rectangleAndPointPicker ?? new MinimalAreaWasteRectAndPointPicker(); }
/// <summary> /// Constructs a new GuillotineBestAreaFitAlgorithm with a given image sorter /// </summary> /// <param name="imageSorter">Image sorter to be used</param> public GuillotineBestAreaFitAlgorithm(IImageSorter imageSorter) { this.placementAlgorithm = new GuillotinePlacementAlgorithm(new BestAreaFitFreeRectangleExtractor(), new LongerAxisGuillotineFreeRectangleSplitter(), null, null, null, imageSorter); }
/// <summary> /// Constructs a new Guillotine algorithm based on the passed parameters and ImageSorter /// </summary> /// <param name="freeRectExtractor">Free rectangle extractor (<seealso cref="IFreeRectangleExtractor"/>) used by the Guillotine algorithm</param> /// <param name="freeRectangleSplitter">Free rectangle splitter (<seealso cref="IFreeRectangleSplitter"/>) used by the Guillotine algorithm</param> /// <param name="freeRectangleMerger">Free rectangle extractor (<seealso cref="IFreeRectangleMerger"/>) used by the Guillotine algorithm</param> /// <param name="rectOrientationSelector">Rectangle orientation selector (<seealso cref="IRectOrientationSelector"/>) used by the Guillotine algorithm</param> /// <param name="freeRectanglePostProcessor">Free post processor (<seealso cref="IFreeRectanglePostProcessor"/>) used by the Guillotine algorithm</param> /// <param name="sorter">The image sorter</param> public GuillotinePlacementAlgorithm(IFreeRectangleExtractor freeRectExtractor, IFreeRectangleSplitter freeRectangleSplitter, IFreeRectangleMerger freeRectangleMerger, IRectOrientationSelector rectOrientationSelector, IFreeRectanglePostProcessor freeRectanglePostProcessor, IImageSorter sorter) { this.freeRectExtractor = freeRectExtractor ?? new BestAreaFitFreeRectangleExtractor(); this.freeRectangleSplitter = freeRectangleSplitter ?? new LongerAxisGuillotineFreeRectangleSplitter(); this.rectOrientationSelector = rectOrientationSelector ?? new DummyRectOrientationSelector(); this.freeRectangleMerger = freeRectangleMerger ?? new GuillotineFreeRectangleSortedMerger(); this.freeRectanglePostProcessor = freeRectanglePostProcessor; this.imageSorter = sorter ?? new ByHeightAndWidthImageSorterDesc(); freeRectanglesList = new List <PPRect>(); }
/// <summary> /// Constructs a BLAlgorithmPacker using a given image sorter /// </summary> /// <param name="sorter">The image sorter to be used</param> public BLAlgorithmPacker(IImageSorter sorter) { this.sorter = sorter; }