将矩形划分为最大尺寸的较小,相等,整数的矩形/切片

时间:2018-04-10 20:20:11

标签: c# math tiling

我需要创建分辨率极高的屏幕截图。由于分辨率大于渲染器支持的分辨率,因此需要将它们拆分为较小的分辨率并在以后拼接在一起。

我已经找到了矩形的拼接/渲染部分,但我正在努力寻找最适合每个拼贴的矩形分辨率。

我的渲染器限制为4096x4096。

对于像16384x16384这样的分辨率,这很容易弄明白:4 * 4矩形的4096x4096。

我的输入分辨率并不总是2的幂,但是一个这样的例子可能是5005x5000,最佳矩形大小为5 * 2矩形1001x2500。

我正在寻找的功能是,当给定所需的分辨率和最大分辨率时,输出最大分辨率内的分辨率,可以乘以达到所需的分辨率。

这样的事情:

public IntRect FindOptimalTileSize(IntRect desiredResolution, IntRect maximumTileSize)
{
    //some magic
    return new IntRect(magic.X, magic.Y);
}

连续的输出要求:

  • 分辨率x和y必须为整数
  • 分辨率x和y必须更低 比给定的最大x和y
  • 分辨率x和y必须是整数 所需分辨率的划分
  • 分辨率x和y应尽可能高,只要它们满足其他规则
  • 即可
  • 不需要保留宽高比

我曾尝试在互联网上搜索解决方案,但这些似乎都是针对不同的,更常见的用例。

可能无法始终找到满足我所有规则的矩形,在这种情况下,我希望函数返回可区分的内容,例如-1x-1

2 个答案:

答案 0 :(得分:0)

我不是数学专家,但这可能会让你成为一个起点。这段代码是草图。它不是生产准备好的。它不具备测试质量。它只是一个草图,你将不得不做很多工作来清理它并使其可用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1 {
    class Program {
        static void Main(string[] args) {

            int resultX = -1;
            int resultY = -1;
            int sourceX = 5005;
            int sourceY = 5000;
            int targetX = 4096;
            int targetY = 4096;

            if (sourceX <= targetX) { resultX = sourceX; }
            if (sourceY <= targetY) { resultY = sourceY; }
            if (IsValid(resultX, resultY)) {
                //  return the results
                Console.WriteLine($"x={resultX}, y={resultY}");
                return;
            }

            for (int index=targetX; 0 < index; index--) {
                double result = (double)sourceX / index;
                if (0 == result - (int)result) {
                    resultX = index;
                    break;
                }
            }

            for (int index = targetY; 0 < index; index--) {
                double result = (double)sourceY / index;
                if (0 == result - (int)result) {
                    resultY = index;
                    break;
                }
            }

            Console.WriteLine($"x={resultX}, y={resultY}");
            Console.ReadKey(true);
        }


        static bool IsValid(int x, int y) {
            return (-1 != x) && (-1 != y);
        }
    }
}

答案 1 :(得分:0)

您的问题可以分解为两个相同的问题:找到整数n的相等整数分区p,其中p <米。

使用扩展方法和一些助手,您可以生成n:

的素数因子分解
static IEnumerable<int> PotentialPrimes() { // fails once int.MaxValue exceeded
    yield return 2;
    yield return 3;
    var pp = 5;
    for (; ; ) {
        yield return pp;
        yield return pp + 2;
        pp += 6;
    }
}
public static IEnumerable<int> Primes() {
    return PotentialPrimes().Where(p => {
        var sqrt = (int)Math.Floor(Math.Sqrt(p));
        return !PotentialPrimes().TakeWhile(f => f <= sqrt)
                                 .Any(f => p % f == 0);
    });
}

public static IEnumerable<int> PrimeFactors(this int n) {
    var maxDivisor = (int)Math.Floor(Math.Sqrt(n));
    var testDivisors = Primes().TakeWhile(pp => pp < maxDivisor);

    foreach (var f in testDivisors)
        for (; n % f == 0; n /= f)
            yield return f;

    if (n != 1)
        yield return n;
}

现在,使用素数分解,你可以找到小于m的最大p(根据注释,如果n> m,则返回n):

public static int LargestPartition(this int n, int maxPartitionSize) {
    var factors = n.PrimeFactors().Where(f => f <= maxPartitionSize);
    if (factors.Any()) {
        var flist = factors.OrderByDescending(f => f).ToList();
        var partition = flist.First();
        foreach (var f in flist.Skip(1)) {
            var tp = partition * f;
            if (tp <= maxPartitionSize)
                partition = tp;
        }

        return partition;
    }
    else
        return n;
}

最后,只需将LargestPartition应用于矩形的每一边:

public IntRect FindOptimalTileSize(IntRect desiredResolution, IntRect maximumTileSize) {
    return new IntRect(desiredResolution.X.LargestPartition(maximumTileSize.X),
                       desiredResolution.Y.LargestPartition(maximumTileSize.Y));
}

注意:我更新了我的PrimeFactors功能,以便更快地处理更多通用案例,以防有人遇到此问题。之前我无法在一小时内从2计算到int.MaxValue,现在需要36秒。如果性能仍然是一个问题,Math.Sqrt可以被替换,但我认为到那时其他开销将占主导地位。