我需要一种算法,可以适应任何大小的n个矩形,在较大的矩形中最小化其面积

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我需要一种算法,可以适应任何大小的n个矩形,在较大的矩形中最小化其面积相关的知识,希望对你有一定的参考价值。

我需要一个算法,它将采用任意大小的n个矩形,并计算一个足够大的矩形以适应它们,最小化其面积,使浪费区域最小,并返回所有较小矩形的位置。

我需要实现的具体任务是在一个精灵表编译器中,该编译器将获取单个PNG文件并生成包含其中所有图像的大型PNG,因此可以在运行时从该表面对各个帧进行blitting。

一个很好的功能是它针对特定的给定宽度/高度比,但它不是强制性的。

我更喜欢简单,通用的代码,我可以移植到另一种语言。

答案

Petruza,

我将从这篇文章开始:http://www.codeproject.com/KB/web-image/rectanglepacker.aspx#basic

它对问题空间有很好的解释,适用于您的特定用法(精灵打包)。它还详细描述了算法,并在文章的底部有示例C#代码。

快乐的编码。

另一答案

这就是我为了自己的需要而放在一起的东西。 T参数是您想要与结果相关联的任何对象(将其视为Tag属性)。它采用大小列表并返回排列的Rect列表

static class LayoutHelper
{

    /// <summary>
    /// Determines the best fit of a List of Sizes, into the desired rectangle shape
    /// </summary>
    /// <typeparam name="T">Holder for an associated object (e.g., window, UserControl, etc.)</typeparam>
    /// <param name="desiredWidthToHeightRatio">the target rectangle shape</param>
    /// <param name="rectsToArrange">List of sizes that have to fit in the rectangle</param>
    /// <param name="lossiness">1 = non-lossy (slow).  Greater numbers improve speed, but miss some best fits</param>
    /// <returns>list of arranged rects</returns>
    static public List<Tuple<T, Rect>> BestFitRects<T>(double desiredWidthToHeightRatio,
        List<Tuple<Size, T>> rectsToArrange, int lossiness = 10)
    {
        // helper anonymous function that tests for rectangle intersections or boundary violations
        var CheckIfRectsIntersect = new Func<Rect, List<Rect>, double, bool>((one, list, containerHeight) =>
        {
            if (one.Y + one.Height > containerHeight) return true;
            return list.Any(two =>
            {
                if ((one.Top > two.Bottom) ||
                    (one.Bottom < two.Top) ||
                    (one.Left > two.Right) ||
                    (one.Right < two.Left)) return false; // no intersection
                return true; // intersection found
            });
        });

        // helper anonymous function for adding drop points
        var AddNewPotentialDropPoints = new Action<SortedDictionary<Point, object>, Rect>(
            (potentialDropPoints, newRect) =>
            {
                // Only two locations make sense for placing a new rectangle, underneath the 
                // bottom left corner or to the right of a top right corner
                potentialDropPoints[new Point(newRect.X + newRect.Width + 1,
                    newRect.Y)] = null;
                potentialDropPoints[new Point(newRect.X,
                    newRect.Y + newRect.Height + 1)] = null;
            });

        var sync = new object();
        // the outer boundary that limits how high the rectangles can stack vertically
        var containingRectHeight = Convert.ToInt32(rectsToArrange.Max(a => a.Item1.Height));
        // always try packing using the tallest rectangle first, working down in height
        var largestToSmallest = rectsToArrange.OrderByDescending(a => a.Item1.Height).ToList();
        // find the maximum possible container height needed
        var totalHeight = Convert.ToInt32(rectsToArrange.Sum(a => a.Item1.Height));
        List<Tuple<T, Rect>> bestResults = null;
        // used to find the best packing arrangement that approximates the target container dimensions ratio
        var bestResultsProximityToDesiredRatio = double.MaxValue;
        // try all arrangements for all suitable container sizes
        Parallel.For(0, ((totalHeight + 1) - containingRectHeight) / lossiness, 
            //new ParallelOptions() { MaxDegreeOfParallelism = 1}, 
            currentHeight =>
        {
            var potentialDropPoints = new SortedDictionary<Point, object>(Comparer<Point>.Create((p1, p2) =>
            {
                // choose the leftmost, then highest point as earlier in the sort order
                if (p1.X != p2.X) return p1.X.CompareTo(p2.X);
                return p1.Y.CompareTo(p2.Y);
            }));
            var localResults = new List<Tuple<T, Rect>>();
            // iterate through the rectangles from largest to smallest
            largestToSmallest.ForEach(currentSize =>
            {
                // check to see if the next rectangle fits in with the currently arranged rectangles
                if (!potentialDropPoints.Any(dropPoint =>
                {
                    var workingPoint = dropPoint.Key;
                    Rect? lastFittingRect = null;
                    var lowY = workingPoint.Y;
                    var highY = workingPoint.Y - 1;
                    var boundaryFound = false;
                    // check if it fits in the current arrangement of rects
                    do
                    {
                        // create a positioned rectangle out of the size dimensions
                        var workingRect = new Rect(workingPoint,
                                new Point(workingPoint.X + currentSize.Item1.Width,
                                workingPoint.Y + currentSize.Item1.Height));
                        // keep moving it up in binary search fashion until it bumps the higher rect
                        if (!CheckIfRectsIntersect(workingRect, localResults.Select(a => a.Item2).ToList(),
                            containingRectHeight + (currentHeight * lossiness)))
                        {
                            lastFittingRect = workingRect;
                            if (!boundaryFound)
                            {
                                highY = Math.Max(lowY - ((lowY - highY) * 2), 0);
                                if (highY == 0) boundaryFound = true;
                            }
                            else
                            {
                                lowY = workingPoint.Y;
                            }
                        }
                        else
                        {
                            boundaryFound = true;
                            highY = workingPoint.Y;
                        }
                        workingPoint = new Point(workingPoint.X, lowY - (lowY - highY) / 2);
                    } while (lowY - highY > 1);

                    if (lastFittingRect.HasValue) // found the sweet spot for this rect
                    {
                        var newRect = lastFittingRect.Value;
                        potentialDropPoints.Remove(dropPoint.Key);
                        // successfully found the best location for the new rectangle, so add it to the pending results
                        localResults.Add(Tuple.Create(currentSize.Item2, newRect));
                        AddNewPotentialDropPoints(potentialDropPoints, newRect);
                        return true;
                    }
                    return false;
                }))
                {
                    // this only occurs on the first square
                    var newRect = new Rect(0, 0, currentSize.Item1.Width, currentSize.Item1.Height);
                    localResults.Add(Tuple.Create(currentSize.Item2, newRect));
                    AddNewPotentialDropPoints(potentialDropPoints, newRect);
                }
            });
            //  layout is complete, now see if this layout is the best one found so far
            var layoutHeight = localResults.Max(a => a.Item2.Y + a.Item2.Height);
            var layoutWidth = localResults.Max(a => a.Item2.X + a.Item2.Width);
            var widthMatchingDesiredRatio = desiredWidthToHeightRatio * layoutHeight;
            double ratioProximity;
            if (layoutWidth < widthMatchingDesiredRatio)
                ratioProximity = widthMatchingDesiredRatio / layoutWidth;
            else
                ratioProximity = layoutWidth / widthMatchingDesiredRatio;
            lock (sync)
            {
                if (ratioProximity < bestResultsProximityToDesiredRatio)
                {
                    // this layout is the best a

以上是关于我需要一种算法,可以适应任何大小的n个矩形,在较大的矩形中最小化其面积的主要内容,如果未能解决你的问题,请参考以下文章

优化在矩形中包含相同大小的正方形

调整字体大小以适应边界矩形?

是否有一种算法可以在图像中找到已知大小和形状的对象?

包装矩形算法

遗传算法-总结

算法笔记_185:历届试题 格子刷油漆(Java)