Calculating point and dimensions of maximum rotated rectangle inside rectangle - objective-c

I have a rectangle that has to be rotated always the same amount of degrees. Lets call this angle alpha (𝜶).
The width (w) and height (h) of this rectangle can vary. The rectangle has always to fit rotated inside the big rectangle. It must be scaled up or down to fit inside the gray rectangle.
NOTE: Alpha is the angle between w and the horizontal line.
So, there are 3 kinds of rectangles where
w > h
w < h or
w = h
See the picture below.
What I know:
The big rectangle has width of R and height of K and I know both values;
w and h are unknown;
the rectangle is always rotated 𝜶 degrees;
I know the value of w/h. I call this "ratioWH";
red rectangle is always centered horizontally and vertically on the gray rectangle
what I need to know:
the maximum values of w and h that will fit the gray rectangle for each case of w and h.
the coordinates of point P, assuming that 0,0 is at the upper left of the gray rectangle.
This is what I did so far, but this is not giving the correct values:
CGPoint P = CGPointZero;
if (ratioWH > 0) { // means w > h
maxH = R / (ratioWH * fabsf(cosf(theta)) + fabsf(sinf(theta)));
maxW = maxH * ratioWH;
// P.x = 0.0f; // P.x is already zero
CGFloat marginY = (K - maxW * fabsf(sinf(theta)) - maxH * fabsf(cosf(theta))) / 2.0f;
P.y = marginY + maxW * fabsf(sinf(theta));
} else { // w <= h
maxW = K / (fabsf(cosf(theta) / ratioImagemXY) + fabsf(sinf(theta)));
maxH = maxW / ratioWH;
P.x = (R - maxW * fabsf(cosf(theta)) - maxH * fabsf(sinf(theta))) / 2.0f;
P.y = maxW * fabsf(sinf(theta));
}
any clues? Thanks.

The way I see it is like this... You work out the total width and total height of the rectangle. For that, you simply walk along two edges. Like this:
dx = w * cos(theta) + h * sin(theta)
dy = h * cos(theta) + w * sin(theta)
These could be negative, so special handling would apply if the rectangle is rotated into other quadrants. This will happen later.
You now just need the ratio between the width and height. This is where you decide whether to scale by the vertical amount or the horizontal amount. It's nothing to do with w and h -- it's actually about where the rectangle ends up as a result of rotation. That's what dx and dy are for.
rectratio = abs( dx / dy )
viewratio = R / K
If rectratio comes out larger than viewratio that means the rotated rectangle's horizontal footprint needs to be scaled. Otherwise you scale by the vertical footprint.
if rectratio > viewratio
scale = R / abs(dx)
else
scale = K / abs(dy)
end
And the scale itself is applied to the original width and height
sw = scale * w
sh = scale * h
So now you can compute the corners of your rectangle. It doesn't matter where you start.
x[0] = 0
x[1] = x[0] + sw * cos(theta)
x[2] = x[1] + sh * sin(theta)
x[3] = x[2] - sw * cos(theta)
y[0] = 0
y[1] = y[0] - sw * sin(theta)
y[2] = y[1] + sh * cos(theta)
y[3] = y[2] + sw * sin(theta)
I've assumed image co-ordinates given that (0,0) is top-left, so increasing y moves down. So, if I haven't made a mistake in my math, the above gives you the rectangle vertices (in clockwise order).
The last thing to do is normalise them... This means finding the min value of px and py. Call them pxmin and pymin. I don't need to show code for that. The idea is to calculate an offset for your rectangle such that the view area is defined by the rectangle (0,0) to (R,K).
First we need to find the left and right value of the subview that completely contains our rotated rectangle... Remember the ratio before:
if( rectratio > viewratio )
// view is too tall, so centre vertically:
left = 0
top = (K - scale * abs(dy)) / 2.0
else
// view is too wide, so centre horizontally:
left = (R - scale * abs(dx)) / 2.0
top = 0
end
left and top are now the 'minimum' co-ordinate of our subview that exactly contains the rectangle (floating point rounding errors exempted). So:
left += pxmin
top += pymin
Now they are the offset required to shift the rectangle to where it's wanted. All you do is add left and top to all your rectangle co-ordinates, and you are done. The position of P is px[0] and py[0]. If you rotated by 90 degrees or more, it won't be the top-left vertex.

Related

Drawing triangles on a slope

I am writing an objective-c method that draws a series of triangles on a slope. In order to complete this, I need to calculate the vertex point of each triangle (C,D). The position starting and ending points are variable.
This seems like it should be an easy math problem. But so far I haven't been able to work it out on paper. Can anyone point me in the right direction?
No trigonometry involved.
Let D= Sqrt(X12^2+Y12^2) the Euclidean distance between P1 and P2 (X12 = X2-X1 and Y12 = Y2-Y1), and let p= P/D, a= A/D.
If P1P2 was the line segment (0, 0)-(1, 0), the vertices would be at (0, 0), (a, p/2), (0, p), (a, 3p/2), (0, 2p)...
The transform below scales and rotates (0, 0)-(1, 0) to P1P2:
X = X1 + X12.x - Y12.y
Y = Y1 + Y12.x + X12.y
Set triangle at origin horizontally:
(0, 0), (p, 0), (p/2, a)
Rotate to get needed slope alpha:
(0, 0), (p*cos(alpha), p*sin(alpha)), (p/2 * cos(alpha) - a * sin(alpha), p/2 * sin(alpha) + a*sin(alpha))
Shift by adding (x1, y1) to all of the coordinates.
The third coordinate is your vertex:
(Cx, Cy) = (p/2 * cos(alpha) - a * sin(alpha) + x1, p/2 * sin(alpha) + a*sin(alpha) + y1)
To find other vertices use the fact that they are shifted by p from each other, under the angle alpha:
(Cx_i, Cy_i) = (Cx, Cy) + i*(p * cos(alpha), p * sin(alpha))

Arithmetic for converting index to coordinates just returns index

I have a grid of images, like a 2D tile set. This method is supposed to give me the image from the tile set when I just give the the tile index.
Can anyone see where my math is screwed up? Currently this function just gives me n and n.(random decimal) for the x and y.
- (CGImageRef)tileFromTileset:(int)gid {
CGImageRef tile;
CGPoint point;
CGFloat fGid = (CGFloat)gid;
point.x = fmodf(fGid, tileSource.size.width);
point.y = (fGid - (fmod(fGid, tileSource.size.width)) / (tileSource.size.width) + 1);
NSLog(#"%#", NSStringFromCGPoint(point));
tile = CGImageCreateWithImageInRect([tileSource CGImage], CGRectMake(point.x, point.y, kTileWidth, kTileHeight));
return tile;
}
I think you have a simple error of units. At the moment, you're dividing a number representing an index by a number representing a coordinate; the unit that you end up with is therefore "index per coordinate".* What you want, though, is just coordinate units.
The assignment to point.x should therefore be:
point.x = fmodf(fGid * kTileWidth, tileSource.size.width);
That is, the tile index (units: "index"), multiplied by the width of one tile (units: "coordinates per index"), gives you its location if this were a single row of tiles (units: "coordinates"). Taking the remainder after dividing by the actual width of a row of tiles gives you the correct x coordinate.
For point.y, you need the quotient of the same calculation:
point.y = trunc(fGid * kTileWidth / tileSouce.size.width) * kTileHeight;
The index, multiplied by the width of a tile, divided by the width of a row, tells you how many rows are completed at that index; multiply by the height of a tile to get the coordinate.
This might be even clearer with one more constant, kNumTilesInRow:
point.x = (gid % kNumTilesInRow) * kTileWidth;
point.y = (gid / kNumTilesInRow) * kTileHeight;
*You keep getting n back because, for abs(n) < abs(m), n % m always equals n.
I think you need to move a parenthesis.
Assuming a 3 x 3 layout as follows:
1 2 3
4 5 6
7 8 9
And you call this passing a 7, here's what I see:
- (CGImageRef)tileFromTileset:(int)gid { // 7
CGImageRef tile;
CGPoint point;
CGFloat fGid = (CGFloat)gid; // 7
point.x = fmodf(fGid, tileSource.size.width); // x= 7 % 3, so x=1
// y= (7 - 1 / 3 + 1), so y= 7.6666666
point.y = (fGid - (fmod(fGid, tileSource.size.width)) / (tileSource.size.width) + 1);
// Try this instead of the above
// y = ((7 - 1) / 3 + 1), so y=3
// point.y = ((fGid - fmod(fGid, tileSource.size.width)) / (tileSource.size.width) + 1);
NSLog(#"%#", NSStringFromCGPoint(point));
tile = CGImageCreateWithImageInRect([tileSource CGImage], CGRectMake(point.x, point.y, kTileWidth, kTileHeight));
return tile;

NSPoint offset by pixels toward angle?

Let me just start with the code.
- (NSPoint*) pointFromPoint:(NSPoint*)point withDistance:(float)distance towardAngle:(float)angle; {
float newX = distance * cos(angle);
float newY = distance * sin(angle);
NSPoint * anNSPoint;
anNSPoint.x = newX;
anNSPoint.y = newY;
return thePoint;
}
This should, based on my knowledge, be perfect. It should return and x value of 0 and a y value of 2 if I call this code.
somePoint = [NSPoint pointFromPoint:somePoint withDistance:2 towardAngle:90];
Instead, I get and x value of 1.05 and a y of 1.70. How can I find the x and y coordinates based on an angle and a distance?
Additional note: I have looked on math.stackexchange.com, but the formulas there led me to this. I need the code, not the normal math because I know I will probably screw this up.
A working version of your function, which accepts values in degrees instead of radians, would look like this:
- (NSPoint)pointFromPoint:(NSPoint)origin withDistance:(float)distance towardAngle:(float)angle
{
double radAngle = angle * M_PI / 180.0;
return NSMakePoint(origin.x + distance * cos(radAngle), point.y + distance * sin(radAngle));
}
Your problem is you're giving the angle in degrees (e.g. 90), but the math is expecting it in radians. Try replacing the 90 with M_PI_2

Map GPS Coordinates to an Image and draw some GPS Points on it

I have some problems figuring out where my error is. I got the following:
Have an image and corresponding GPS coordinates of its top-left and bottom-right vertices.
E.g:
topLeft.longitude = 8.235128;
topLeft.latitude = 49.632383;
bottomRight.longitude = 8.240547;
bottomRight.latitude = 49.629808;
Now a have an Point that lies in that map:
p.longitude = 8.238567;
p.latitude = 49.630664;
I draw my image in landscape fullscreen (1024*748).
Now I want to calculate the exact Pixel position (x,y) of my point.
For doing that I am trying to use the great circle distance approach from here: Link.
CGFloat DegreesToRadians(CGFloat degrees)
{
return degrees * M_PI / 180;
};
- (float) calculateDistanceP1:(CLLocationCoordinate2D)p1 andP2:(CLLocationCoordinate2D)p2 {
double circumference = 40000.0; // Erdumfang in km am Äquator
double distance = 0.0;
double latitude1Rad = DegreesToRadians(p1.latitude);
double longitude1Rad = DegreesToRadians(p1.longitude);
double latititude2Rad = DegreesToRadians(p2.latitude);
double longitude2Rad = DegreesToRadians(p2.longitude);
double logitudeDiff = fabs(longitude1Rad - longitude2Rad);
if (logitudeDiff > M_PI)
{
logitudeDiff = 2.0 * M_PI - logitudeDiff;
}
double angleCalculation =
acos(sin(latititude2Rad) * sin(latitude1Rad) + cos(latititude2Rad) * cos(latitude1Rad) * cos(logitudeDiff));
distance = circumference * angleCalculation / (2.0 * M_PI);
NSLog(#"%f",distance);
return distance;
}
Here is my code for getting the Pixel position:
- (CGPoint) calculatePoint:(CLLocationCoordinate2D)point {
float x_coord;
float y_coord;
CLLocationCoordinate2D x1;
CLLocationCoordinate2D x2;
x1.longitude = p.longitude;
x1.latitude = topLeft.latitude;
x2.longitude = p.longitude;
x2.latitude = bottomRight.latitude;
CLLocationCoordinate2D y1;
CLLocationCoordinate2D y2;
y1.longitude = topLeft.longitude;
y1.latitude = p.latitude;
y2.longitude = bottomRight.longitude;
y2.latitude = p.latitude;
float distanceX = [self calculateDistanceP1:x1 andP2:x2];
float distanceY = [self calculateDistanceP1:y1 andP2:y2];
float distancePX = [self calculateDistanceP1:x1 andP2:p];
float distancePY = [self calculateDistanceP1:y1 andP2:p];
x_coord = fabs(distancePX * (1024 / distanceX))-1;
y_coord = fabs(distancePY * (748 / distanceY))-1;
return CGPointMake(x_coord,y_coord);
}
x1 and x2 are the points on the longitude of p and with latitude of topLeft and bottomRight.
y1 and y2 are the points on the latitude of p and with longitude of topLeft and bottomRight.
So I got the distance between left and right on longitude of p and distance between top and bottom on latitude of p. (Needed for calculate the pixel position)
Now I calculate the distance between x1 and p (my distance between x_0 and x_p) after that I calculate the distance between y1 and p (distance between y_0 and y_p)
Last but not least the Pixel position is calculated and returned.
The Result is, that my point is on the red and NOT on the blue position:
Maybe you find any mistakes or have any suggestions for improving the accuracy.
Maybe I didn't understand your question, but shouldn't you be using the Converting Map Coordinates methods of MKMapView?
See this image
I used your co-ordinates, and simply did the following:
x_coord = 1024 * (p.longitude - topLeft.longitude)/(bottomRight.longitude - topLeft.longitude);
y_coord = 748 - (748 * (p.latitude - bottomRight.latitude)/(topLeft.latitude - bottomRight.latitude));
The red dot markes this point. For such small distances you don't really need to use great circles, and your rounding errors will be making things much more inaccurate

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);
}