I'm using this binary pack code. I'm using its GrowingPacker approach. It works fine, but I have a hard time figuring out the dimensions of the final pack, I mean the bounding box of final result like (xMin, xMax, yMin, yMax). What is the best approach to find it?
Can I use the final values of x, y, w, h for this.root to compute the bounding box?
I'm not sure if it is correct, but currently I'm using a loop like that loop to compute the bounding box of final pack (I'm using C++):
#include <cfloat> // For FLT_MAX
float minX = FLT_MAX;
float minY = FLT_MAX;
float maxW = - FLT_MAX;
float maxH = - FLT_MAX;
for (const Block &block : blocks) {
if (block.fit) {
if (block.fit->x < minX)
minX = block.fit->x;
if (block.fit->y < minY)
minY = block.fit->y;
if (block.fit->w > maxW)
maxW = block.fit->w;
if (block.fit->h > maxH)
maxH = block.fit->h;
}
}
To calculate the bounding box that encompasses all the bounding boxes in your list of blocks, you need find the minimum and maximum X and Y and then use that to calculate the width and height of the overall bounding box.
Calculating the maximum width and height of the smaller boxes won't work because that will be the size of the widest and highest of the individual smaller boxes, which won't necessarily be the dimensions of the overall bounding box that encompasses all of them.
float minX = FLT_MAX;
float minY = FLT_MAX;
float maxX = - FLT_MAX;
float maxY = - FLT_MAX;
for (const Block &block : blocks) {
if (block.fit) {
if (block.fit->x < minX)
minX = block.fit->x;
if (block.fit->y < minY)
minY = block.fit->y;
if (block.fit->x + block.fit->w > maxX)
maxX = block.fit->x + block.fit->w;
if (block.fit->y + block.fit->h > maxY)
maxY = block.fit->y + block.fit->h;
}
}
// now that you have the min/max of x and y, calculate overall w and h:
float w = maxX > minX ? maxX - minX : 0.0f;
float h = maxY > minY ? maxY - minY : 0.0f;
Related
I load a 3D-model from a file and need to see it all on my screen. All vertices should be on the screen within the main window. Then I rotate and zoom the model and at some point I would like to fit the model to the window again. So, I have written the function OptimiseView (see below) which multiplies the view matrix by each vertex position and then calculates minimum and maximum coordinates of the screen plane.
The above multiplication takes a lot of time. My shader does the same multiplication but I can't manage to store minimum and maximum coordinates in the shader (GPU) and return these values back to the program (CPU) after processing the last vertex.
Is this possible at all? How do CAD-systems implement this Fit View (Optimise View) feature? How does a space mouse (e.g. 3Dconnexion) work in this relation?
I am currently using C++ and OpenGL.
void OptimiseView(glm::mat4& view, glm::mat4* proj, int trianglesNumber, float* positions)
{
if ((rotAngleX != 0) || (rotAngleY != 0) || (mouseScroll != 0))
{
float minX, maxX, minY, maxY, minZ, maxZ;
float centreX, centreY;
float largestDimension;
int triangleCount{ 0 };
glm::vec4 vertexPosition;
if (trianglesNumber < 1)
{
minX = maxX = minY = maxY = minZ = maxZ = 0;
}
else
{
vertexPosition = view * glm::vec4(
positions[0],
positions[1],
positions[2],
1.0f);
minX = maxX = vertexPosition.x;
minY = maxY = vertexPosition.y;
minZ = maxZ = vertexPosition.z;
}
while (triangleCount < trianglesNumber)
{
for (int initialPosition : {0, 3, 9})
{
vertexPosition = view * glm::vec4(
positions[triangleCount * 9 * 2 + initialPosition],
positions[triangleCount * 9 * 2 + initialPosition + 1],
positions[triangleCount * 9 * 2 + initialPosition + 2],
1.0f);
if (vertexPosition.x < minX) minX = vertexPosition.x;
if (vertexPosition.x > maxX) maxX = vertexPosition.x;
if (vertexPosition.y < minY) minY = vertexPosition.y;
if (vertexPosition.y > maxY) maxY = vertexPosition.y;
if (vertexPosition.z < minZ) minZ = vertexPosition.z;
if (vertexPosition.z > maxZ) maxZ = vertexPosition.z;
}
triangleCount++;
}
centreX = minX + (maxX - minX) / 2.0f;
centreY = minY + (maxY - minY) / 2.0f;
largestDimension = ((maxX - minX) >= (maxY - minY)) ? (maxX - minX) : (maxY - minY);
minX = centreX - largestDimension / 2.0f;
maxX = centreX + largestDimension / 2.0f;
minY = centreY - largestDimension / 2.0f;
maxY = centreY + largestDimension / 2.0f;
*proj = glm::ortho(minX, maxX, minY, maxY, -minZ, -maxZ);
}
}
I have also tried to use global variables in the shader but they seem to store values during processing a single vertex only.
I know the Fit View feature already works in different CAD-systems and am just wondering what approach would be the best.
Thanks.
After more deep research, I found the OpenGL thing which is called 'Transform Feedback'. The best practical manual and the answer to my question is here.
More information is here.
It will also be useful to read the book of Sam Buss '3D Computer Graphics: A mathematical approach with OpenGL.' It contains a lot of examples on using different OpenGL functions.
I am re asking this question with a better explanation.
I have obtained the four points of a rectangle that I wish to perform a warp perspective transformation on. I have successfully achieved a transform on one of my images as I manually selected the points and assigned their location for the transform.
cv::Point2f src_vertices[4];
src_vertices[0] = corners[3];
src_vertices[1] = corners[1];
src_vertices[2] = corners[0];
src_vertices[3] = corners[2];
Point2f dst_vertices[4];
dst_vertices[0] = Point(0, 0);
dst_vertices[1] = Point(box.boundingRect().width-1, 0);
dst_vertices[2] = Point(0, box.boundingRect().height-1);
dst_vertices[3] = Point(box.boundingRect().width-1, box.boundingRect().height-1);
As you can see I assigned the corners to the transformation vertices manually.
As I wish to use this code on multiple slightly different images I would like a more accurate way of assigning the corners.
All the images will look similar to this:
http://imgur.com/t0cgTzr
The corners for this image are as follows:
Top left - (1106, 331),
Top right- (810, 747),
Bottom left- (825, 187),
Bottom right- (510, 537)
I have found the max and min x and y values as follows:
float X, Y;
float maxX= 0;
float minX = 10000;
float maxY= 0;
float minY = 10000;
for(int i=0; i< 4; i++){
if( corners[i].x > maxX){
maxX = corners[i].x;
}
if( corners[i].x < minX){
minX = corners[i].x;
}
if( corners[i].y > maxY){
maxY = corners[i].y;
}
if( corners[i].y < minY){
minY = corners[i].y;
}
}
This gives me:
maxX - 1106, minX - 510, maxY - 747, minY - 187.
I would like to know how to recombine the max and min values to their respective values so I can use the points to perform the transform. I am quite new to opencv so sorry if this is very obvious.
What you can do to get the corners without losing information on the other coordinate is to find the whole point. Instead of storing just float maxX= 0; you can store Point2f maxX(0,0); and modify you for loop to keep track of the point itself, not only the x coordinate.
Better yet, get rid of those loops and make use of STL
Point2f maxX =
*std::max_element(corners, corners+4, [](Point2f a, Point2f b){return a.x < b.x;});
I am using fitellipse of Opencv and C++, and I'm getting these values:
/// Find the rotated rectangles and ellipses for each contour
vector<RotatedRect> minRect( contours.size() );
vector<RotatedRect> minEllipse( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{
minRect[i] = minAreaRect( Mat(contours[i]) );
if( contours[i].size() > 5 )
minEllipse[i] = fitEllipse( Mat(contours[i]) );
// ...
}
float xc = minEllipse[element].center.x;
float yc = minEllipse[element].center.y;
float a = minEllipse[element].size.width / 2;
float b = minEllipse[element].size.height / 2;
float theta = minEllipse[element].angle;
But with these values how can I draw the axis of an ellipse, for example of the following ellipse?
NOTE: Element is an ellipse stored in minEllipse.
You can use minEllipse[element].points to get the four corners of the rotated bounding rectangle, like described here.
Then you only need to calculate the average of the two points on each side of the rectangle to get the endpoints for the axes...
Point2f vertices[4];
minEllipse[element].points(vertices);
line(image, (vertices[0] + vertices[1])/2, (vertices[2] + vertices[3])/2, Scalar(0,255,0));
line(image, (vertices[1] + vertices[2])/2, (vertices[3] + vertices[0])/2, Scalar(0,255,0));
You are probably looking for those formulas:
ct = cos(theta)
st = sin(theta)
LongAxix0.x = xc - a*ct
LongAxis0.y = yc - a*st
LongAxis1.x = xc + a*ct
LongAxix1.y = yc + a*st
ShortAxix0.x = xc - b*st
ShortAxix0.y = yc + b*ct
ShortAxis1.x = xc + b*st
ShortAxix2.y = yc - b*ct
But with these values how can I draw the axis of an ellipse?
The axis of the ellipse are passing through its centre:
float xc = minEllipse[element].center.x;
float yc = minEllipse[element].center.y;
the start and end points of the axis could be at an offset from the centre defined by the ellipse's width and height, i.e.:
// horizontal axis start/ end point
// coordinates
int HxStart = xc - size.width / 2;
int HyStart = yc;
int HxEnd = xc + size.width / 2;
int HyEnd = yc;
// points
Point Hstart(HxStart, HyStart);
Point Hend(HxEnd, HyEnd);
// horizontal axis
Line horizontalAxis(Hstart, Hend);
// vertical axis start/ end point
int VxStart = xc;
int VyStart = yc - size.height / 2;
int VxEnd = xc;
int VyEnd = yc + size.height / 2;
// ----//----
Now, you can rotate the axis (the above for points) by the provided angle theta, around the centre of the ellipse.
Having the above and knowing how to construct a line you can build the two axis at any given angle theta.
I have attempted to produce an algorithm that uses world coordinates and a bounding box structure to
detect collision between two bounding boxes. I really don't know what I'm doing, but I thought the code below would work. My issue is that it only detects collision if the bounding boxes are on the exact same x,y,z position.
BOOL AABB::isCollidedWith(AABB* bb)
{
if(bb == NULL) return FALSE;
float radX1,radX2;
float radY1,radY2;
float radZ1,radZ2;
float arr[12];
//please note that all the mins are set to 0
//and all the maxes are set to 1
radX1 = (bb->maxX - bb->minX) / 2;
radX2 = (this->maxX - this->minX) / 2;
radY1 = (bb->maxY - bb->minY) / 2;
radY2 = (this->maxY - this->minY) / 2;
radZ1 = (bb->maxZ - bb->minZ) / 2;
radZ2 = (this->maxZ - this->minZ) / 2;
//bb coords
arr[1] = bb->bbX - radX1;
arr[2] = bb->bbX + radX1;
arr[3] = bb->bbY - radY1;
arr[4] = bb->bbY + radY1;
arr[5] = bb->bbZ - radZ1;
arr[6] = bb->bbZ + radZ1;
//this coords
arr[7] = this->bbX - radX2;
arr[8] = this->bbX + radX2;
arr[9] = this->bbY - radY2;
arr[10] = this->bbY + radY2;
arr[11] = this->bbZ - radZ2;
arr[12] = this->bbZ + radZ2;
if(arr[2] >= arr[7] && arr[1] <= arr[8])
{
if(arr[4] >= arr[9] && arr[3] <= arr[10])
{
if(arr[6] >= arr[11] && arr[5] <= arr[12])
{
this->collided = TRUE;
OutputDebugStringA("Collided!\n");
return TRUE;
}
}
}
}
Structures I am comparing:
AABB* aabb1 = new AABB(0.0f,0.0f,0.0f,1.0f,1.0f,1.0f,0.0f,0.0f,0.0f);
AABB* aabb2 = new AABB(0.0f,0.0f,0.0f,1.0f,1.0f,1.0f,0.0f,0.0f,0.0f);
aabb2->isCollidedWith(aabb1);
Constructor snippet :
Also note that the last three parameters dictate the x,y,z cords of the bounding box
AABB::AABB(float minx,float maxx,float miny,float maxy,float minz,float maxz,float x,float y,float z)
{
this->minX = minx;
this->maxX = maxx;
this->minY = miny;
this->maxY = maxy;
this->minZ = minz;
this->maxZ = maxz;
Any help,criticism, or advice would help.
As you are creating the boxes with minX=0.0 and maxX=0.0, the bbX coordinate must be the same for the boxes to collide (because radX = 0). The same goes for minZ=maxZ=1.0.
Note the order of parameters in your constructor: it's minX, maxX, minY, maxY, minZ, maxZ and not minX, minY, minZ, maxX, maxY, maxZ (I guess you supposed the second order and wanted to define a box of 1.0 x 1.0 x 1.0 dimensions).
Simple Error! I ignored the way the parameters were listed, causing the issue.
Also, I had subtract 0.5 from each member of the array "arr" to find the center of AABB.
I am trying to rotate a bmp image using EasyBMP. when the angle is between 0 and 90 or 270 and 360 the rotation is fine. but when between 180 and 270 the boundary rectangle is stretched and for angle between 90 and 180 I get segmentation fault. I am convinced that the problem arises from
int width = image.TellWidth();
int height = image.TellHeight();
float sine= sin(angle);
float cosine=cos(angle);
float x1=-height*sine;
float y1=height*cosine;
float x2=width*cosine-height*sine;
float y2=height*cosine+width*sine;
float x3=width*cosine;
float y3=width*sine;
float minx=min(0,min(x1,min(x2,x3)));
float miny=min(0,min(y1,min(y2,y3)));
float maxx=max(x1,max(x2,x3));
float maxy=max(y1,max(y2,y3));
int outWidth;
int outHeight;
outWidth=(int)ceil(fabs(maxx)-minx);
outHeight=(int)ceil(fabs(maxy)-miny);
output.SetSize(outHeight,outWidth);
for(int x=0; x<outWidth; x++)
{
for(int y=0; y<outHeight; y++)
{
int srcX=(int)((x+minx)*cosine+(y+miny)*sine);
int srcY=(int)((y+miny)*cosine-(x+minx)*sine);
if(srcX>=0 &&srcX<width && srcY>=0 && srcY<height)
{
output.SetPixel(x,y,image.GetPixel(srcX,srcY));
}
}
}
The following is how I solved this. The TL;DR: the rotation transform goes around 0, 0, so if your image coordinates set 0,0 to bottom left, you need to translate the image to be centered on 0,0 first. Also, sin and cos expect radians, not degrees, so remember to convert first
The long way:
I started by creating a simple program that has easily verified answers, to find out where things are going wrong.
The first thing I noticed was that 90.0f wouldn't produce any output. That seemed weird, so I broke in at the "output image size" printf and realized that the output height was being calculated as -87. Clearly that's not right, so let's see why that might happen.
Going up a bit, outHeight=(int)ceil(fabs(maxy)-miny); so let's figure out how we're ending up with a negative output height when subtracting maxy and miny. It appears maxy is -0.896... and miny is 88.503... However, the absolute value of maxy is taken before subtracting miny, meaning we're ending up with 0.896 - 88.503. Whoa, that's not good! Let's try doing the subtraction then taking the absolute value.
Recompiling with both width and height as such:
outWidth=(int)ceil(fabs(maxx-minx));
outHeight=(int)ceil(fabs(maxy-miny));
Gets us much better values. Now outWidth and outHeight are 2 and 90, respectively. This is massively improved, but the height should be 100. We'll address that later.
To figure out where the math is going wrong, I reorganize the terms to go together: x with x, y with y. Next I adjusted spacing and added parenthesis to make it more readable and ensure order of operations (sure beats trying to look at an OoO table ;) ). Since it's clear you're breaking out the rotation matrix multiplication, I'm going to name your variables something a bit more intuitive than x1, x2, etc. From now on, x1 is topLeftTransformedX, x2 is topRightTransformedX, x3 will exist as bottomLeftTransformedX (always 0), and x4 will be bottomRightTransformedX, same for Y. Longer, but much easier to know what you're dealing with.
Using this, at this point, I see the same thing you do... then I remembered something, based on the numbers seen from this cleaner code (same math as yours, but still easier to debug).
Suddenly, my math for X looks like this:
// x = x cos - y sin
float topLeftTransformedX = (-midX * cosine) - (midY * sine);
float topRightTransformedX = (midX * cosine) - (midY * sine);
float bottomLeftTransformedX = (-midX * cosine) - (-midY * sine);
float bottomRightTransformedX = (midX * cosine) - (-midY * sine);
The rotation matrix rotates around the center point. You have to translate the image to be centered around that for a proper rotation.
Then, when trying to figure out why this would be giving the values it is, i recalled something else - angle needs to be in radians.
Suddenly, it almost all works. There's still some more to do, but this should get you 95% of the way there or more. Hope it helps!
// bmprotate.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <math.h>
#define min(x,y) x < y ? x : y
#define max(x,y) x > y ? x : y
#define PI 3.14159
void rotate(int width, int height, float angleInDeg)
{
float angle = angleInDeg * (PI/180.0f);
float midX = ((float)width) / 2.0f;
float midY = ((float)height) / 2.0f;
float sine = sin(angle);
float cosine = cos(angle);
// x = x cos - y sin
float topLeftTransformedX = (-midX * cosine) - (midY * sine);
float topRightTransformedX = (midX * cosine) - (midY * sine);
float bottomLeftTransformedX = (-midX * cosine) - (-midY * sine);
float bottomRightTransformedX = (midX * cosine) - (-midY * sine);
float minx = min( topLeftTransformedX, min(topRightTransformedX, min(bottomLeftTransformedX, bottomRightTransformedX)) );
float maxx = max( topLeftTransformedX, max(topRightTransformedX, max(bottomLeftTransformedX, bottomRightTransformedX)) );
// y = x sin + y cos
float topLeftTransformedY = (-midX * sine) + (midY * cosine);
float topRightTransformedY = (midX * sine) + (midY * cosine);
float bottomLeftTransformedY = (-midX * sine) + (-midY * cosine);
float bottomRightTransformedY = (midX * sine) + (-midY * cosine);
float miny = min( topLeftTransformedY, min(topRightTransformedY, min(bottomLeftTransformedY, bottomRightTransformedY)) );
float maxy = max( topLeftTransformedY, max(topRightTransformedY, max(bottomLeftTransformedY, bottomRightTransformedY)) );
int outWidth;
int outHeight;
printf("(%f,%f) , (%f,%f) , (%f,%f) , (%f,%f)\n",
topLeftTransformedX, topLeftTransformedY,
topRightTransformedX, topRightTransformedY,
bottomLeftTransformedX, bottomLeftTransformedY,
bottomRightTransformedX, bottomRightTransformedY);
outWidth = (int) ceil( fabs(maxx) + fabs(minx));
outHeight = (int) ceil( fabs(maxy) + fabs(miny) );
printf("output image size: (%d,%d)\n",outWidth,outHeight);
for(int x=0; x<outWidth; x++)
{
for(int y=0; y<outHeight; y++)
{
int srcX=(int)((x+minx)*cosine+(y+miny)*sine);
int srcY=(int)((y+miny)*cosine-(x+minx)*sine);
if(srcX >=0 && srcX < width && srcY >= 0 && srcY < height)
{
printf("(x,y) = (%d,%d)\n",srcX, srcY);
}
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
rotate(100,2,90.0f);
for (int i = 0; i < 360; i++)
{
}
return 0;
}