public MaxRectsPacker(Settings settings) { _settings = settings; _methods = new List <FreeRectChoiceHeuristic>(Enum.GetValues(typeof(FreeRectChoiceHeuristic)).OfType <FreeRectChoiceHeuristic>()); _rectCompartor = new RectComparer(this); _maxRects = new MaxRects(this); if (_settings.MinWidth > _settings.MaxWidth) { throw new Exception("Page min width cannot be higher than max width"); } if (_settings.MinHeight > _settings.MaxHeight) { throw new Exception("Page min height cannot be higher than max height"); } }
/// <inheritdoc /> public PackingResult PlaceRects(int width, int height, IEnumerable <PPRect> rects, CancellationToken token = default) { Progress = 0; if (width < 0 || height < 0) { throw new ArgumentOutOfRangeException($"The {nameof(width)} and {nameof(height)} should be non-negative"); } var sortedInput = sorter.SortImages(rects); int inputSize = rects.Count(); int placedRects = 0; int actualWidth = 0; int actualHeight = 0; RectComparer rectComparer = new RectComparer(); PointComparer ptComparer = new PointComparer(); SortedSet <PPRect> currentPacking = new SortedSet <PPRect>(rectComparer); SortedDictionary <SKPointI, int> pointsToTry = new SortedDictionary <SKPointI, int>(ptComparer) { { new SKPointI(0, 0), -1 } //the current packing is empty, so only point to try is point [0,0] }; SKPointI[] pointsToAdd = new SKPointI[2]; foreach (var x in sortedInput) { if (token.IsCancellationRequested) { Progress = 0; return(null); } SKPointI?pointToRemove = null; foreach (var ptToTry in pointsToTry) { PPRect tested = new PPRect(ptToTry.Key.X, ptToTry.Key.Y, ptToTry.Key.X + x.Width, ptToTry.Key.Y + x.Height); var possibleIntersections = currentPacking.AsEnumerable(); //have to test everything if (ptToTry.Key.X + x.Width <= width && ptToTry.Key.Y + x.Height <= height && !Intersects(tested, possibleIntersections)) //safe to pack here { if (ptToTry.Key.X + x.Width > actualWidth) { actualWidth = ptToTry.Key.X + x.Width; } if (ptToTry.Key.Y + x.Height > actualHeight) { actualHeight = ptToTry.Key.Y + x.Height; } int improved = 0; if (TryImprove(ref tested, currentPacking, 0)) //Try to position it further to the top / left { improved++; } //Add it to the packing tested.Image = x.Image; currentPacking.Add(tested); if (improved == 0) { pointToRemove = ptToTry.Key; } pointsToAdd[0] = new SKPointI(ptToTry.Key.X + x.Width, ptToTry.Key.Y); pointsToAdd[1] = new SKPointI(ptToTry.Key.X, ptToTry.Key.Y + x.Height); break; } } if (pointToRemove != null) { pointsToTry.Remove(pointToRemove.Value); pointsToTry[pointsToAdd[0]] = -1; pointsToTry[pointsToAdd[1]] = -1; Progress = (int)((++placedRects / (double)inputSize) * 100.0); ProgressChange?.Invoke(this, Progress); } else { Progress = 100; return(null); //we cannot pack it anywhere } } //var result = new PackingResult(width, height, currentPacking.Select(x => (x.Value, x.Key))); // probably better to return result with actual width & height instead of those needed //actual height can be lower than height specified, width also BUT THIS IS NOT DESIRED, BECAUSE THIS CAN BE CALLED FROM FIXEDSIZE..? OR chhange size in FixedSize.. var result = new PackingResult(actualWidth, actualHeight, currentPacking); return(result); }