Changing the angle depending on position changes - scripting

I'm writing a scenario to a game which allows to use custom scripts.
I want to change the camera angle using SetCameraHeading(float in_fAngle), where:
"0 is facing east, 90 is facing north, 180 is facing west, and 270 is facing south".
I made something like this:
//The map size is 150x300:
NWX = 0;
NWY = 300;
NEX = 150;
NEY = 300;
SWX = 0;
SWY = 0;
SEX = 150;
SEY = 0;
Player1NW = GetDistanceFromUnitToPoint("Survivor1", NWX, NWY);
Player1NE = GetDistanceFromUnitToPoint("Survivor1", NEX, NEY);
Player1SW = GetDistanceFromUnitToPoint("Survivor1", SWX, SWY);
Player1SE = GetDistanceFromUnitToPoint("Survivor1", SEX, SEY);
Player1NWDiff = Player1NWOld - Player1NW;
Player1NEDiff = Player1NEOld - Player1NE;
Player1SWDiff = Player1SWOld - Player1SW;
Player1SEDiff = Player1SEOld - Player1SE;
//Need to set camera heading here:
SetCameraHeading(Player1CurrentAngle, false);
Player1NWOld = Player1NW;
Player1NEOld = Player1NE;
Player1SWOld = Player1SW;
Player1SEOld = Player1SE;
The problem is I don't know how to change the angle depending on position changes (for example, when an unit is walking it changes to: Player1NWDiff = -0.02, Player1NEDiff = -0.02, Player1SWDiff = 0.04, Player1SEDiff = 0.03 etc.)
Does anybody know how should I calculate it to get a correct angle (0 to 360)?

Related

DirectX11-raycast mouse picking has proplem

gif
Creating View, Porjection Matrix
void CameraSystem::CreateMatrix()
{
camera->aspect = viewport->Width / viewport->Height;
projection_matrix = XMMatrixPerspectiveFovLH(camera->fov, camera->aspect, camera->near_z, camera->far_z);
XMVECTOR s, o, q, t;
XMFLOAT3 position(camera->position.m128_f32[0], camera->position.m128_f32[1], camera->position.m128_f32[2]);
s = XMVectorReplicate(1.0f);
o = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
q = XMQuaternionRotationRollPitchYaw(camera->pitch, camera->yaw, camera->roll);
t = XMLoadFloat3(&position);
world_matrix = XMMatrixAffineTransformation(s, o, q, t);
view_matrix = XMMatrixInverse(0, world_matrix);
camera->look = XMVector3Normalize(XMMatrixTranspose(view_matrix).r[2]);
camera->right = XMVector3Normalize(XMMatrixTranspose(view_matrix).r[0]);
camera->up = XMVector3Normalize(XMMatrixTranspose(view_matrix).r[1]);
camera->position = world_matrix.r[3];
cb_viewproj.data.view_matrix = XMMatrixTranspose(view_matrix);
cb_viewproj.data.projection_matrix = XMMatrixTranspose(projection_matrix);
}
this code creating projection matrix with aspect, fov, near, far.
and create world, view with camera transform. and these work perfect for rendering but maybe not for create ray.
Creating Mouse Ray
MouseRay CameraSystem::CreateMouseRay()
{
MouseRay mouse_ray;
POINT cursor_pos;
GetCursorPos(&cursor_pos);
ScreenToClient(ENGINE->GetWindowHandle(), &cursor_pos);
// Convert the mouse position to a direction in world space
float mouse_x = static_cast<float>(cursor_pos.x);
float mouse_y = static_cast<float>(cursor_pos.y);
float ndc_x = 2.0f * mouse_x / (float)ENGINE->GetWindowSize().x - 1.0f;
float ndc_y = (2.0f * mouse_y / (float)ENGINE->GetWindowSize().y - 1.0f) * -1.0f;
float ndc_z = 1.0f;
ndc.x = ndc_x;
ndc.y = ndc_y;
XMMATRIX inv_view = XMMatrixInverse(nullptr, view_matrix);
XMMATRIX inv_world = XMMatrixInverse(nullptr, XMMatrixIdentity());
XMVECTOR ray_dir;
XMVECTOR ray_origin;
ndc_x /= projection_matrix.r[0].m128_f32[0];
ndc_y /= projection_matrix.r[1].m128_f32[1];
ray_dir.m128_f32[0] = (ndc_x * inv_view.r[0].m128_f32[0]) + (ndc_y * inv_view.r[1].m128_f32[0]) + (ndc_z * inv_view.r[2].m128_f32[0]) + inv_view.r[0].m128_f32[3];
ray_dir.m128_f32[1] = (ndc_x * inv_view.r[0].m128_f32[1]) + (ndc_y * inv_view.r[1].m128_f32[1]) + (ndc_z * inv_view.r[2].m128_f32[1]) + inv_view.r[1].m128_f32[3];
ray_dir.m128_f32[2] = (ndc_x * inv_view.r[0].m128_f32[2]) + (ndc_y * inv_view.r[1].m128_f32[2]) + (ndc_z * inv_view.r[2].m128_f32[2]) + inv_view.r[2].m128_f32[3];
ray_origin = XMVector3TransformCoord(camera->position, inv_world);
ray_dir = XMVector3TransformNormal(ray_dir, inv_world);
ray_dir = XMVector3Normalize(ray_dir);
XMtoRP(ray_origin, mouse_ray.start_point);
XMtoRP(ray_dir * 1000.f, mouse_ray.end_point);
return mouse_ray;
}
get cursor pos, and converto ndc, it's perpect, i guess 'ndc, porjection calulate' has some propelm, but i couldn't find any other code.
inversed view matrix to ray direction, and i guess this code also has problem.
As you can see, the mouse does not seem to generate a ray in the exact direction of the pointed mouse. The frustum feels narrower than it really is, and the further away from the map, the worse it gets.
It seems as if the camera position is fixed, but the camera position is being updated from the world matrix as shown in the properties window.
The fact that views and projection matrices are also honestly generated can be inferred from the fact that the rest except for raycasting are rendered correctly.
I assume that the ray direction vector is miscalculated.
I don't know what more calculations can be made in the next code.
raycast test by reactphysics3d library.

I'm trying to project 4D lines to 3D to 2D and clip them at z=p and w=p, but I'm not sure if it's rendering correctly

I've been trying to write a program that can render 4D lines, the specific function doing this gets the lines already rotated, and the function attempts to clip the lines at planes z = p and w = p if needed, and then draw the line to the screen.
I think that I am doing at least most of this properly, however I am unsure, and not having much experience viewing the fourth dimension I cannot tell what might be a visual bug, or what is actually how it should be rendered.
The function first loads a line into two variables, each is one of the two endpoints of the line. If both points are beyond clippl (the clipping plane variable) for z = clippl and w = clippl, it then applies perspective transformation to them, and subsequently renders a line on the screen correspondingly.
If certain logic is met for the points, the function goes through a process of clipping them, and then continues the same as it would outside the clipping planes.
The location of the camera is held in the variables Ox, Oy, Oz, Ow at the beginning of the full program.
I can't tell if I've done this properly, can anyone tell me if this works right as a 4D perspective projection from a first person camera?
EDIT: I've added points to the rendering list that are at the corners of the cube I'm rendering, and it seems to show that there is in fact some problem with the line clipping, as I am fairly certain that the points are rendering properly, and there is not always a line showing up at it. Could the problem have to do with the w = p clip?
Here's the function, the program uses p5.js:
function drawPLines(P){
var lA,lB;
for(var i=0;i<P.length;i++){
lA = [P[i][0],P[i][1],P[i][2],P[i][3]];
lB = [P[i][4],P[i][5],P[i][6],P[i][7]];
//X: ( x*VS+(width*0.5)+(ox*VS) )
//Y: ( y*VS+(height*0.5)+(oy*VS) )
//x: (XV[0]*P[i][0])+(YV[0]*P[i][1])+(ZV[0]*P[i][2])+(WV[0]*P[i][3])
//y: (XV[1]*P[i][0])+(YV[1]*P[i][1])+(ZV[1]*P[i][2])+(WV[1]*P[i][3])
var x0,y0,x1,y1;
//x0 = (XV[0]*lA[0])+(YV[0]*lA[1])+(ZV[0]*lA[2])+(WV[0]*lA[3]);
//y0 = (XV[1]*lA[0])+(YV[1]*lA[1])+(ZV[1]*lA[2])+(WV[1]*lA[3]);
//new rendering pipeline
//old rendering pipeline
if(lA[2]>clippl&&lB[2]>clippl&&lA[3]>clippl&&lB[3]>clippl){
x0 = XV[0]*lA[0];
y0 = YV[1]*lA[1];
x0 = (x0/lA[3])/(lA[2]/lA[3]);
y0 = (y0/lA[3])/(lA[2]/lA[3]);
//console.log(y);
x0 = ( x0*VS+(width*0.5)+(ox*VS) );
y0 = ( y0*VS+(height*0.5)+(oy*VS) );
//x1 = (XV[0]*lB[0])+(YV[0]*lB[1])+(ZV[0]*lB[2])+(WV[0]*lB[3]);
//y1 = (XV[1]*lB[0])+(YV[1]*lB[1])+(ZV[1]*lB[2])+(WV[1]*lB[3]);
x1 = XV[0]*lB[0];
y1 = YV[1]*lB[1];
x1 = (x1/lB[3])/(lB[2]/lB[3]);
y1 = (y1/lB[3])/(lB[2]/lB[3]);
//console.log(y);
x1 = ( x1*VS+(width*0.5)+(ox*VS) );
y1 = ( y1*VS+(height*0.5)+(oy*VS) );
stroke([P[i][8],P[i][9],P[i][10],P[i][11]]);
line(x0,y0,x1,y1);
}else if((lA[2]>clippl||lA[3]>clippl||lB[2]>clippl||lB[3]>clippl)){
var V = 0;
var zV = 0;
var wV = 0;
//var oV = 0;
if(lA[2]>clippl&&lA[3]>clippl){
V++;
}else if(lA[2]>clippl&&lA[3]<=clippl){
zV++;
}else if(lA[2]<=clippl&&lA[3]>clippl){
wV++;
}/*else{
oV++;
}*/
if(lB[2]>clippl&&lB[3]>clippl){
V++;
}else if(lB[2]>clippl&&lB[3]<=clippl){
zV++;
}else if(lB[2]<=clippl&&lB[3]>clippl){
wV++;
}/*else{
oV++;
}*/
if((V==1)||(wV==1&&(V==1||zV==1))||(zV==1&&(V==1||wV==1))){
var lin = lB;
var out = lA;
if(lA[2]<=clippl){
out = lB;
lin = lA;
}
if(lin[2]<=clippl){
lin = [((((lA[0]-lB[0])*clippl)-((lA[0]-lB[0])*lB[2]))/(lA[2]-lB[2]))+lB[0],((((lA[1]-lB[1])*clippl)-((lA[1]-lB[1])*lB[2]))/(lA[2]-lB[2]))+lB[1],clippl,((((lA[3]-lB[3])*clippl)-((lA[3]-lB[3])*lB[2]))/(lA[2]-lB[2]))+lB[3]];
}
if((lA[2]-lB[2])!==0){
lA = lin;
lB = out;
}
lin = lA;
out = lB;
if(lB[3]<=clippl){
out = lA;
lin = lB;
}
if(lin[3]<=clippl){
lin = [((((lA[0]-lB[0])*clippl)-((lA[0]-lB[0])*lB[3]))/(lA[3]-lB[3]))+lB[0],((((lA[1]-lB[1])*clippl)-((lA[1]-lB[1])*lB[3]))/(lA[3]-lB[3]))+lB[1],((((lA[2]-lB[2])*clippl)-((lA[2]-lB[2])*lB[3]))/(lA[3]-lB[3]))+lB[2],clippl];
//alert(lin);
//alert(out);
}
if((lA[3]-lB[3])!==0){
lA = lin;
lB = out;
}
if(lA[2]>clippl||lB[2]>clippl||lA[3]>clippl||lB[3]>clippl){
x0 = XV[0]*lA[0];
y0 = YV[1]*lA[1];
x0 = (x0/lA[3])/(lA[2]/lA[3]);
y0 = (y0/lA[3])/(lA[2]/lA[3]);
//console.log(y);
x0 = ( x0*VS+(width*0.5)+(ox*VS) );
y0 = ( y0*VS+(height*0.5)+(oy*VS) );
//x1 = (XV[0]*lB[0])+(YV[0]*lB[1])+(ZV[0]*lB[2])+(WV[0]*lB[3]);
//y1 = (XV[1]*lB[0])+(YV[1]*lB[1])+(ZV[1]*lB[2])+(WV[1]*lB[3]);
x1 = XV[0]*lB[0];
y1 = YV[1]*lB[1];
x1 = (x1/lB[3])/(lB[2]/lB[3]);
y1 = (y1/lB[3])/(lB[2]/lB[3]);
//console.log(y);
x1 = ( x1*VS+(width*0.5)+(ox*VS) );
y1 = ( y1*VS+(height*0.5)+(oy*VS) );
stroke([P[i][8],P[i][9],P[i][10],P[i][11]]);
line(x0,y0,x1,y1);
}
}
}
}
}
You can see the full program at https://editor.p5js.org/hpestock/sketches/Yfagz4Bz3

Vulkan: Support alpha channel for sprites

I'm trying to draw some sprites where the alpha channel of the image is taken into account.
What is the correct set of values for the following structures to support alpha channel of textures in the fragment shader?
vk::PipelineColorBlendAttachmentState colorBlendAttachment;
colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = vk::BlendFactor::eOne;
colorBlendAttachment.dstColorBlendFactor = vk::BlendFactor::eZero;
colorBlendAttachment.colorBlendOp = vk::BlendOp::eAdd;
colorBlendAttachment.srcAlphaBlendFactor = vk::BlendFactor::eOne;
colorBlendAttachment.dstAlphaBlendFactor = vk::BlendFactor::eZero;
colorBlendAttachment.alphaBlendOp = vk::BlendOp::eSubtract;
vk::PipelineColorBlendStateCreateInfo colorBlending;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = vk::LogicOp::eCopy;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
Per Ekzusy's answer, here are 2 ways:
Using the 'discard' keyword in the fragment shader.
// Read data from some texture.
vec4 color = texture(...);
// This makes the alpha channel (w component) act as a boolean.
if (color.w < 1) { discard; }
For my original question, these values will do:
vk::PipelineColorBlendAttachmentState colorBlendAttachment;
colorBlendAttachment.colorWriteMask =
vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
colorBlendAttachment.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
colorBlendAttachment.colorBlendOp = vk::BlendOp::eAdd;
colorBlendAttachment.srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha;
colorBlendAttachment.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
colorBlendAttachment.alphaBlendOp = vk::BlendOp::eSubtract;

Removing the spacing between tiles in tilesheet

So I have an image which contains a tile-sheet, where each tile is approx 16 pixels wide, and high. But there spaced out with a transparent spacer between each tile.
Like so:
But this is ugly, and makes displaying the sprites in the program annoying, not to mention it wastes valuable image space. Is there any easy (Besides me manually using Photoshop to move each individual tile) way to make it look like this?
I looked through Photoshop macros, as-well as other programs and I diden't seem to find anything that would directly do this.
Google also suggests I go to home-depo and get tile caulk remover.
Try this snippet. As you said, it assumes tiles are always going to be 16 pixels. Top left one is in the correct position and a single pixel gap. The script assumes the document will opened with the layer containing your tiles set as the active layer.
#target photoshop
app.preferences.rulerUnits = Units.PIXELS;
app.preferences.typeUnits = TypeUnits.PIXELS;
var gap = 1;
var tileSize = 16;
var doc = app.activeDocument.duplicate();
var sourceLyr = doc.activeLayer;
var xTilePosition = 0;
var yTilePosition = 0;
for (var x = 0; x < sourceLyr.bounds[2]; x = x+ tileSize + 1 ) {
for (var y = 0; y < sourceLyr.bounds[3]; y = y + tileSize + 1) {
if (x > 0 || y > 0) {
app.activeDocument = doc;
doc.activeLayer = sourceLyr;
selRegion = Array(Array(x, y),
Array(x + tileSize, y),
Array(x + tileSize, y + tileSize),
Array(x, y + tileSize),
Array(x, y))
doc.selection.select(selRegion);
var dx = x - (xTilePosition * tileSize);
var dy = y - (yTilePosition * tileSize);
doc.selection.translate(0 - dx, 0 - dy);
}
yTilePosition ++;
}
xTilePosition++;
yTilePosition = 0;
}

Resizing N # of squares to be as big as possible while still fitting into box of X by Y dimensions. (Thumbnails!)

I have N squares.
I have a Rectangular box.
I want all the squares to fit in the box.
I want the squares to be as large as possible.
How do I calculate the largest size for the squares such that they all fit in the box?
This is for thumbnails in a thumbnail gallery.
int function thumbnailSize(
iItems, // The number of items to fit.
iWidth, // The width of the container.
iHeight, // The height of the container.
iMin // The smallest an item can be.
)
{
// if there are no items we don't care how big they are!
if (iItems = 0) return 0;
// Max size is whichever dimension is smaller, height or width.
iDimension = (iWidth min iHeight);
// Add .49 so that we always round up, even if the square root
// is something like 1.2. If the square root is whole (1, 4, etc..)
// then it won't round up.
iSquare = (round(sqrt(iItems) + 0.49));
// If we arrange our items in a square pattern we have the same
// number of rows and columns, so we can just divide by the number
// iSquare, because iSquare = iRows = iColumns.
iSize = (iDimension / iSquare);
// Don't use a size smaller than the minimum.
iSize = (iSize max iMin);
return iSize;
}
This code currently works OK. The idea behind it is to take the smallest dimension of the rectangular container, pretend the container is a square of that dimension, and then assume we have an equal number of rows and columns, just enough to fit iItems squares inside.
This function works great if the container is mostly squarish. If you have a long rectangle, though, the thumbnails come out smaller than they could be. For instance, if my rectangle is 100 x 300, and I have three thumbnails, it should return 100, but instead returns 33.
Probably not optimal (if it works which I haven't tried), but I think better than you current approach :
w: width of rectangle
h: height of rectangle
n: number of images
a = w*h : area of the rectangle.
ia = a/n max area of an image in the ideal case.
il = sqrt(ia) max length of an image in the ideal case.
nw = round_up(w/il): number of images you need to stack on top of each other.
nh = round_up(h/il): number of images you need to stack next to each other.
l = min(w/nw, w/nh) : length of the images to use.
The solution on https://math.stackexchange.com/a/466248 works perfectly.
An unoptimized javascript implementation:
var getMaxSizeOfSquaresInRect = function(n,w,h)
{
var sw, sh;
var pw = Math.ceil(Math.sqrt(n*w/h));
if (Math.floor(pw*h/w)*pw < n) sw = h/Math.ceil(pw*h/w);
else sw = w/pw;
var ph = Math.ceil(Math.sqrt(n*h/w));
if (Math.floor(ph*w/h)*ph < n) sh = w/Math.ceil(w*ph/h);
else sh = h/ph;
return Math.max(sw,sh);
}
I was looking for a similar solution, but instead of squares I had to fit rectangles in the container. Since a square is also a rectangle, my solution also answers this question.
I combined the answers from Neptilo and mckeed into the fitToContainer() function. Give it the number of rectangles to fit n, the containerWidth and containerHeight and the original itemWidth and itemHeight. In case items have no original width and height, use itemWidth and itemHeight to specify the desired ratio of the items.
For example fitToContainer(10, 1920, 1080, 16, 9) results in {nrows: 4, ncols: 3, itemWidth: 480, itemHeight: 270}, so four columns and 3 rows of 480 x 270 (pixels, or whatever the unit is).
And to fit 10 squares in the same example area of 1920x1080 you could call fitToContainer(10, 1920, 1080, 1, 1) resulting in {nrows: 2, ncols: 5, itemWidth: 384, itemHeight: 384}.
function fitToContainer(n, containerWidth, containerHeight, itemWidth, itemHeight) {
// We're not necessarily dealing with squares but rectangles (itemWidth x itemHeight),
// temporarily compensate the containerWidth to handle as rectangles
containerWidth = containerWidth * itemHeight / itemWidth;
// Compute number of rows and columns, and cell size
var ratio = containerWidth / containerHeight;
var ncols_float = Math.sqrt(n * ratio);
var nrows_float = n / ncols_float;
// Find best option filling the whole height
var nrows1 = Math.ceil(nrows_float);
var ncols1 = Math.ceil(n / nrows1);
while (nrows1 * ratio < ncols1) {
nrows1++;
ncols1 = Math.ceil(n / nrows1);
}
var cell_size1 = containerHeight / nrows1;
// Find best option filling the whole width
var ncols2 = Math.ceil(ncols_float);
var nrows2 = Math.ceil(n / ncols2);
while (ncols2 < nrows2 * ratio) {
ncols2++;
nrows2 = Math.ceil(n / ncols2);
}
var cell_size2 = containerWidth / ncols2;
// Find the best values
var nrows, ncols, cell_size;
if (cell_size1 < cell_size2) {
nrows = nrows2;
ncols = ncols2;
cell_size = cell_size2;
} else {
nrows = nrows1;
ncols = ncols1;
cell_size = cell_size1;
}
// Undo compensation on width, to make squares into desired ratio
itemWidth = cell_size * itemWidth / itemHeight;
itemHeight = cell_size;
return { nrows: nrows, ncols: ncols, itemWidth: itemWidth, itemHeight: itemHeight }
}
The JavaScript implementation form mckeed gave me better results then the other answers I found. The idea to first stretch the rectangle to a square came from Neptilo.
you want something more like
n = number of thumbnails
x = one side of a rect
y = the other side
l = length of a side of a thumbnail
l = sqrt( (x * y) / n )
Here is my final code based off of unknown (google)'s reply:
For the guy who wanted to know what language my first post is in, this is VisualDataflex:
Function ResizeThumbnails Integer iItems Integer iWidth Integer iHeight Returns Integer
Integer iArea iIdealArea iIdealSize iRows iCols iSize
// If there are no items we don't care how big the thumbnails are!
If (iItems = 0) Procedure_Return
// Area of the container.
Move (iWidth * iHeight) to iArea
// Max area of an image in the ideal case (1 image).
Move (iArea / iItems) to iIdealArea
// Max size of an image in the ideal case.
Move (sqrt(iIdealArea)) to iIdealSize
// Number of rows.
Move (round((iHeight / iIdealSize) + 0.50)) to iRows
// Number of cols.
Move (round((iWidth / iIdealSize) + 0.50)) to iCols
// Optimal size of an image.
Move ((iWidth / iCols) min (iHeight / iRows)) to iSize
// Check to make sure it is at least the minimum.
Move (iSize max iMinSize) to iSize
// Return the size
Function_Return iSize
End_Function
This should work. It is solved with an algorithm rather than an equation. The algorithm is as follows:
Span the entire short side of the rectangles with all of the squares
Decrease the number of squares in this span (as a result, increasing the size) until the depth of the squares exceeds the long side of the rectangle
Stop when the span reaches 1, because this is as good as we can get.
Here is the code, written in JavaScript:
function thumbnailSize(items, width, height, min) {
var minSide = Math.min(width, height),
maxSide = Math.max(width, height);
// lets start by spanning the short side of the rectange
// size: the size of the squares
// span: the number of squares spanning the short side of the rectangle
// stack: the number of rows of squares filling the rectangle
// depth: the total depth of stack of squares
var size = 0;
for (var span = items, span > 0, span--) {
var newSize = minSide / span;
var stack = Math.ceil(items / span);
var depth = stack * newSize;
if (depth < maxSide)
size = newSize;
else
break;
}
return Math.max(size, min);
}
In Objective C ... the length of a square side for the given count of items in a containing rectangle.
int count = 8; // number of items in containing rectangle
int width = 90; // width of containing rectangle
int height = 50; // width of container
float sideLength = 0; //side length to use.
float containerArea = width * height;
float maxArea = containerArea/count;
float maxSideLength = sqrtf(maxArea);
float rows = ceilf(height/maxSideLength); //round up
float columns = ceilf(width/maxSideLength); //round up
float minSideLength = MIN((width/columns), (height/rows));
float maxSideLength = MAX((width/columns), (height/rows));
// Use max side length unless this causes overlap
if (((rows * maxSideLength) > height) && (((rows-1) * columns) < count) ||
(((columns * maxSideLength) > width) && (((columns-1) * rows) < count))) {
sideLength = minSideLength;
}
else {
sideLength = maxSideLength;
}
My JavaScript implementation:
var a = Math.floor(Math.sqrt(w * h / n));
return Math.floor(Math.min(w / Math.ceil(w / a), h / Math.ceil(h / a)));
Where w is width of the rectangle, h is height, and n is number of squares you want to squeeze in.
double _getMaxSizeOfSquaresInRect(double numberOfItems, double parentWidth, double parentHeight) {
double sw, sh;
var pw = (numberOfItems * parentWidth / parentHeight).ceil();
if ((pw * parentHeight / parentWidth).floor() * pw < numberOfItems) {
sw = parentHeight / (pw * parentHeight / parentWidth).ceil();
} else {
sw = parentWidth / pw;
}
var ph = (sqrt(numberOfItems * parentHeight / parentWidth)).ceil();
if ((ph * parentWidth / parentHeight).floor() * ph < numberOfItems) {
sh = parentWidth / (parentWidth * ph / parentHeight).ceil();
} else {
sh = parentHeight / ph;
}
return max(sw, sh);
}