I need to create screenshots with an extremely large resolution. Because the resolution is larger than my renderer supports, they need to be split up into smaller resolutions and stitched together later.
I have figured out the stitching/rendering part of rectangles, but I am struggling trying to find the most optimal rectangle resolution to use for each tile.
My renderer has a limit of 4096x4096.
For resolutions like 16384x16384 this would be easy to figure out: 4*4 rectangles of 4096x4096.
My input resolutions aren't always a power of two however, one such example could be 5005x5000, for which the optimal rectangle size would be 5*2 rectangles of 1001x2500.
What I'm looking for is a function that, when given a desired resolution and a maximum resolution, outputs a resolution within the maximum resolution that can be multiplied to reach the desired resolution.
Something like this:
public IntRect FindOptimalTileSize(IntRect desiredResolution, IntRect maximumTileSize)
{
//some magic
return new IntRect(magic.X, magic.Y);
}
The output requirements in a row:
I have tried searching the internet for a solution, but those all seem to be for a different, more common usecase.
It might not always be possible to find a rectangle that satisfies all my rules, in which case I'd like the function to return something distinguishable, like -1x-1
I'm no math expert, but this might get you a starting point. This code is a sketch. It is not production-ready. It is not of beta quality. It is only a sketch and you will have to do a lot of work to clean it up and make it usable.
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);
}
}
}
Your problem can be broken down into two equal problems: find the equal integer partition p of an integer n where p < m.
Using an extension method and some helpers, you can generate the Prime Factorization of 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;
}
Now, using the prime factorization, you can find the largest p less than m (per the comment, if n > m, then n is returned):
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;
}
Finally, just apply LargestPartition
to each side of the rectangles:
public IntRect FindOptimalTileSize(IntRect desiredResolution, IntRect maximumTileSize) {
return new IntRect(desiredResolution.X.LargestPartition(maximumTileSize.X),
desiredResolution.Y.LargestPartition(maximumTileSize.Y));
}
NOTE: I updated my PrimeFactors
function to be faster for more generic cases in case someone comes across this. Where previously I couldn't compute from 2 to int.MaxValue
in an hour, now it takes 36 seconds. The Math.Sqrt
could be replaced if performance was still an issue, but I would think by that point other overhead would dominate.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.