File indexing completed on 2024-05-12 04:06:21
0001 /* 0002 SPDX-FileCopyrightText: 2010 Johannes Loehnert <loehnert.kde@gmx.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "utilities.h" 0008 0009 #include <QDebug> 0010 #include <QRandomGenerator> 0011 0012 void getBestFit(int &xCount, int &yCount, qreal target_aspect, int approx_count) { 0013 qreal nx_exact = sqrt(approx_count * target_aspect); 0014 // avoid taking the sqrt again 0015 qreal ny_exact = approx_count / nx_exact; 0016 0017 // catch very odd cases 0018 if (nx_exact < 1) nx_exact = 1.01; 0019 if (ny_exact < 1) ny_exact = 1.01; 0020 0021 qreal aspect1 = floor(nx_exact) / ceil(ny_exact); 0022 qreal aspect2 = ceil(nx_exact) / floor(ny_exact); 0023 0024 aspect1 = target_aspect - aspect1; 0025 aspect2 = aspect2 - target_aspect; 0026 0027 if (aspect1 < aspect2) ny_exact += 1.0; else nx_exact += 1.0; 0028 0029 xCount = (int)(floor(nx_exact) + 0.1); 0030 yCount = (int)(floor(ny_exact) + 0.1); 0031 } 0032 0033 void getBestFitExtended(int &xCount, int &yCount, qreal target_aspect, int approx_count, 0034 qreal tiles_per_cell, qreal additional_tiles_per_row, qreal additional_tiles_per_column, qreal additional_tiles) { 0035 // solves the equations 0036 // N = TPC * x * y + ATPC * x + ATPR * y + AT 0037 // target_aspect = x / y 0038 // 0039 // for x, y; and rounds them to the nearest integer values giving least distance to target_aspect. 0040 qreal p_half = (target_aspect * additional_tiles_per_column + additional_tiles_per_row) / (2 * target_aspect * tiles_per_cell); 0041 qreal q = (approx_count - additional_tiles) / (target_aspect * tiles_per_cell); 0042 0043 qreal p_half_sq = p_half*p_half; 0044 if (p_half_sq + q < 0) { 0045 xCount = 1; 0046 yCount = 1; 0047 return; 0048 } 0049 0050 qreal ny_exact = -p_half + sqrt(p_half_sq + q); 0051 qreal nx_exact = target_aspect * ny_exact; 0052 0053 qDebug() << "nx_exact: " << nx_exact << " ny_exact: " << ny_exact << 0054 "giving count: " << (tiles_per_cell*nx_exact*ny_exact + additional_tiles_per_column * nx_exact + additional_tiles_per_row * ny_exact + additional_tiles); 0055 0056 // catch very odd cases 0057 if (nx_exact < 1) nx_exact = 1.01; 0058 if (ny_exact < 1) ny_exact = 1.01; 0059 0060 qreal aspect1 = floor(nx_exact) / ceil(ny_exact); 0061 qreal aspect2 = ceil(nx_exact) / floor(ny_exact); 0062 qreal aspect3 = ceil(nx_exact) / ceil(ny_exact); 0063 0064 aspect1 = target_aspect - aspect1; 0065 aspect2 = aspect2 - target_aspect; 0066 aspect3 = abs(aspect3 - target_aspect); 0067 0068 if (aspect1 < aspect2) { 0069 ny_exact += 1.0; 0070 if (aspect3 < aspect1) nx_exact += 1.0; 0071 } 0072 else { 0073 nx_exact += 1.0; 0074 if (aspect3 < aspect2) ny_exact += 1.0; 0075 } 0076 0077 xCount = (int)(floor(nx_exact) + 0.1); 0078 yCount = (int)(floor(ny_exact) + 0.1); 0079 } 0080 0081 // skews x with "strength" a. 0082 // x is expected to lie within [0, 1]. 0083 // a = +/-1 is already a very strong skew. 0084 // negative a: skew towards x=0, positive: skew towards x=1. 0085 qreal skew_randnum(qreal x, qreal a) { 0086 if (a==0) return x; 0087 0088 qreal asq = exp(-2 * abs(a)); 0089 if (a>0) x = 1-x; 0090 0091 qreal mp2 = (x-1) * (2/asq - 1); 0092 qreal q = (x-1)*(x-1) - 1; 0093 0094 // We apply a function on x, which is a hyperbola through (0,0) and (1,1) 0095 // with (1, 0) as focal point. You really don't want to know the gory details. 0096 x = mp2 + sqrt(mp2*mp2 - q); 0097 0098 if (a>0) x = 1-x; 0099 return x; 0100 } 0101 0102 0103 qreal nonuniform_rand(qreal min, qreal max, qreal sigma, qreal skew) { 0104 0105 // 0.4247: sigma at which distribution function is 1/2 of max at interval boundaries 0106 0107 qreal randNum; 0108 0109 auto *generator = QRandomGenerator::global(); 0110 if (sigma > 0.4247) { 0111 // "wide" distribution, use rejection sampling 0112 qreal x, y; 0113 qreal ssq = 2 * sigma * sigma; 0114 do { 0115 x = 0.000001 * qreal(generator->bounded(1000000)); 0116 y = 0.000001 * qreal(generator->bounded(1000000)); 0117 } while (y > exp(-(x-0.5)*(x-0.5)/ssq)); 0118 0119 randNum = x; 0120 } 0121 else { 0122 // "narrow" distribution, use Marsaglia method until a random number within 0, 1 is found. 0123 qreal u1, u2, p, q, x1, x2; 0124 0125 randNum = -1; 0126 do { 0127 do { 0128 u1 = 0.000002 * qreal(generator->bounded(1000000)) - 1; 0129 u2 = 0.000002 * qreal(generator->bounded(1000000)) - 1; 0130 q = u1*u1 + u2*u2; 0131 } while (q>1); 0132 p = sqrt(-2 * log(q) / q) * sigma; 0133 x1 = u1 * p + 0.5; 0134 x2 = u2 * p + 0.5; 0135 0136 if (x1>= 0 && x1 <= 1) { 0137 randNum = x1; 0138 } 0139 else { 0140 if (x2 >= 0 && x2 <= 1) randNum = x2; 0141 } 0142 } while (randNum < 0); 0143 } 0144 0145 return min + (max - min) * skew_randnum(randNum, skew); 0146 } 0147