Positioning a widget involving intersection of line and a circle? - c++

Here is the problem I'm trying to solve for my game.
I have this scenario:
I'm trying to solve for the position and size of the green rectangle. The circle is at 50%, 40% of the screen and its radius is proportional to the height of the screen.
The green rectangle must always be 10 pixels away from the bottom. Its left corner must be 10 pixels away also. And as can be seen in the image, the distance from the top right corner until the rectangle touches the circle is 10 pixels also.
Another constraint is that the green rectangle must always be 3 times wider than its height (aspect ratio).
Given these constraints, how can I solve for the position and size of the green rectangle?
Essentially, the Game Window can have a bunch of different aspect ratios so the green rectangle must look good in any of these situations.
I'm not necessarily looking for code but just an idea on how this could be solved.
Thanks

The thing to do in these situations is to describe the constraints mathematically, and see if it simplifies. This is an essential skill for geometric processing.
Let's assume the bottom left corner of the image area is (0,0). That puts the bottom-left corner of the rectangle at (10,10); we'll call the top-right corner (x1,y1). I'll assume you've already calculated where the circle will be since that's pretty straight-forward, we'll call the center (x2,y2) and the radius r.
The first constraint: the rectangle is 3 times wider than it is tall.
x1-10 = 3 * (y1-10) or x1 = 3 * (y1-10) + 10 or x1 = 3*y1 - 20
The second constraint: x1,y1 lies 10 pixels away from the circle. If we describe another circle 10 pixels larger than the first, the point will lie on it.
(x1-x2)^2 + (y1-y2)^2 = (r+10)^2
Substituting for x1:
(3*y1 - 20 - x2)^2 + (y1-y2)^2 = (r+10)^2
This is great, because r, x2, and y2 are known; the only unknown left is y1. Let's see if we can gather all the y1's together.
(3*y1 + (-20 - x2))^2 + (y1-y2)^2 = (r+10)^2
3^2*y1^2 + 2*(3*y1*(-20-x2) + (-20-x2)^2 + y1^2 + 2*y1*-y2 + y2^2 = (r+10)^2
3^2*y1^2 + y1^2 + 6*(-20-x2)*y1 + 2*-y2*y1 + y2^2 = (r+10)^2
(3^2+1)*y1^2 + (-120 - 6*x2 - 2*y2)*y1 + y2^2 = (r+10)^2
At this point it's looking almost like a quadratic equation. One more little tweak:
10 * y1^2 + (-120 - 6*x2 - 2*y2) * y1 + (y2^2 - (r+10)^2) = 0
The final step is to apply the Quadratic Formula.
a*y1^2 + b*y1 + c = 0
a = 10
b = (-120 - 6*x2 - 2*y2)
c = (y2^2 - (r+10)^2)
y1 = (-b +/- sqrt(b^2 - 4*a*c)) / 2*a
There are two possible answers from the quadratic equation, but one of them will put the rectangle on the far side of the circle. It should be easy to eliminate that case.

What you have there is a classic circle-line intersection problem. You know a point on the line - the bottom left corner of the rectangle. And you know the slope of the line (from the aspect ratio). The circle you intersect with can be your red circle shifted left by 10 to give you your 10 pixel gap. The intersection will be the top right corner of the desired rectangle. That should be enough for an idea.

Related

Ellipse rotated not centered

I am trying to draw a rotated ellipse not centered at the origin (in c++).
so far my code "works":
for (double i = 0; i <= 360; i = i + 1) {
theta = i*pi / 180;
x = (polygonList[compt]->a_coeff / 2) * sin(theta) + polygonList[compt]->centroid->datapointx;
y = (polygonList[compt]->b_coeff / 2) * cos(theta) + polygonList[compt]->centroid->datapointy;
xTmp = (x - polygonList[compt]->centroid->datapointx)* cos(angle1) - (y - polygonList[compt]->centroid->datapointy)*sin(angle1) + polygonList[compt]->centroid->datapointx;
yTmp = (x - polygonList[compt]->centroid->datapointx)* sin(angle1) + (y - polygonList[compt]->centroid->datapointy)*cos(angle1) + polygonList[compt]->centroid->datapointy;
}
PolygonList is a list of "bloc" which will be replaced by an ellipse of same area.
My issue is that the angles are not quite exact, as if I had to put a protractor that'd fit the shape of my ellipse, the protractor would obviously get squeezed, and so would be the angles (is that clear ?)
Here is an example: I am trying to set a point on the top ellipse (E1) which would be lying on a line drawn between the centroid of E1, and any point on the second ellipse (E2).On this example, the point on E2 lies at an angle of ~220-230 degree. I am able to catch this angle, the angle seems ok.
The problem is that if I try to project this point on E1 by using this angle of ~225 degree, I end up on the second red circle on top. it looks like my angle is now ~265 degree, but in fact, if I shape the protractor to fit in my ellipse, I get the right angle (~225) ,cf img 2)
it is a bit hard to see the angle on that re-shaped protractor, but it does show ~225 degree.
My conclusion is that the ellipse is drawn like if I had to drew a circle and then I'd compress it, which changes the distance between the angles.
Could someone tell me how I could fix that ?
PS: to draw those ellipses I just use a for loop which plots a dot at every angle (from 0 to 360). we clearly see on the first picture that the distance between the dots are different whether we are at 0 or at 90 degree.
your parametrisation is exactly that, a circle is a case of ellipse with both axes are equal. It sounds like you need use rational representation of ellipse instead of standard: https://en.m.wikipedia.org/wiki/Ellipse
So, I've asked the question above so that I could find a possible overlap between 2 ellipses by checking the distance between any point on E2 and its projection on E1: if the distance between the centroid of E1 and the projected dot on E1 is larger than the distance between the centroid of E1 to a dot on E2 I'll assume an overlap. I reckon this solution has never been tried (or I haven't search enough) and should work fine. But before working I needed to get those angles right.
I have found a way to avoid using angles and projected dots, by checking the foci:
the sum of the distance between the focus A and B to any point around an axis is constant (let's call it DE1 for E1).
I then check the distance between my foci and any point on E2. If that distance becomes less than DE1, I'll assume a connection.
So far it seems to work fine :)
I'll put that here for anyone in need.
Flo

Qt 5.5 draw filled pie

The image below shows the grey pie, I would like to draw this using Qt 5.5
X increases left to right
Y increases top to bottom
I have a start angle and an end angle which represents to the top and bottom of the arc, I am calculating the arc angle using:
double dblArcAngle = fmod(mcfltElevMaxLimit - mcfltElevMinLimit + 180.0, 360.0) - 180.0;
Where:
mcfltElevMaxLimit is 60 and mcfltElevMinLimit is -10
The call to drawPie looks like this:
objOffscrPainter.drawPie(QRect(rctGeom.left() + mcintElevLeftMargin
,rctGeom.top() + mcintElevBottomMargin
,rctGeom.width() - mcintElevLeftMargin
,rctGeom.height() - mcintElevBottomMargin)
,mcfltElevMaxLimit * 16, dblArcAngle * 16);
What I get is a very small polyline about midway up where the pie should be.
(edit), just read in the documentation that both startAngle and spanAngle parameters 2 and 3 should be multiplied by 16, which does produce a pie, not in the correct orientation and not filled to the center but its progress.
(edit 2), more progress, the image below now shows the results I'm getting, the rectangle I'm passing is the outer rectangle and includes the axis, yet for some reason the pie is offset???
What I want to accomplish is the pie tucked into the bottom left aligned with the white axis and filling the image.
It looks like the passed rectangle is used to determine the center point for the pie. If this is correct then the center of the rectangle must be adjusted to be the origin (bottom left) and the size also adjusted to fill the display.
The rectangle in a first parameter of QPainter::drawPie is a bounding box of a circle which contains your arc. So, to draw what you need try something like this:
objOffscrPainter.drawPie(QRect(center.x() - r, center.y() - r, 2 * r, 2 * r)
,16*mcfltElevMaxLimit, 16*dblArcAngle);
(where center is a center of your arc)
It seems that in your case center is a QPoint(0, 0), so you can use this code:
objOffscrPainter.drawPie(-r, -r, 2*r, 2*r, 16*mcfltElevMaxLimit, 16*dblArcAngle);
(we can call it without QRect too, see the documentation)

convert Cartesian width and height to Isometric

I'm trying to create an Isometric game in the SDL graphics library.
When you render in SDL you require a source rectangle and a destination rectangle. The source rectangle is the portion of the texture you have loaded that you wish to render and the destination rectangle is the area on the screen you are rendering to. Rectangles simply consist of 4 properties, X position, Y position, Width, and Height.
Now say I have a Cartesian destination rectangle with these coordinates:
X=20, Y=10, Width=16, Height=16
Now say I wanted to convert this to Isometric. To convert the X and Y coordinates I would use:
isometricX = cartX - cartY;
isometricY = (cartX + cartY) / 2;
Now what I don't understand is what I would do to convert the Cartesian Width and Height to Isometric Width and Height to create the illusion that the view port is being moved 45 degrees to one side and 30 degrees down, creating the Isometric look.
EDIT:
I'd like to clarify my question a little, so when I convert the Cartesian Coordinates to Isometric I change this:http://i.stack.imgur.com/I79yK.png
to this:http://i.stack.imgur.com/ZCJg1.png. So now I am trying to figure out how to rotate the tiles so they all fit together and how I need to adjust the Height and Width to get this to happen.
To start with you'll need these operations to convert to and from isometric coordinates:
isoX = carX + carY;
isoY = carY - carX / 2.0;
carX = (isoX - isoY) / 1.5;
carY = isoX / 3.0 + isoY / 1.5;
right-angled corners in the top-left and bottom-right become 120 degrees, the other two corners become 60 degrees. the bottom-right corner becomes the bottom corner, the top-left corner becomes the top. this also assumes that y increases going up, and x increases going right (if your system is different, flip the signs accordingly). you can verify via substitution that these operations are eachothers inverse.
for a rectangle you need 4 points transformed - the corners - as they will not be 'rectangular' for the purposes of SDL (it will be a parallelogram). this is easier to see numerically.
first, assign the corners names of some sort. i prefer clockwise starting with the bottom-left - this coordinate shall be known as C1, and has an associated X1 and Y1, the others will be C2-4.
C2 - C3
| |
C1 - C4
then compute their cartesian coordinates...
X1 = RECT.X;
Y1 = RECT.Y;
X2 = X1; // moving vertically
Y2 = RECT.Y + RECT.HEIGHT;
X3 = RECT.X + RECT.WIDTH;
Y3 = Y2; // moving horizontally
X4 = X3; // moving vertically
Y4 = RECT.Y;
and lastly apply the transform individually to each coordinate, to get I1, I2, I3, I4 coordinates...
iX1 = X1 + Y1;
iY1 = Y1 - X1 / 2.0;
// etc
and what you end up with is on-screen coordinates I1-4, that take this shape:
I2
/ \
I1 I3
\ /
I4
But unlike this shoddy depiction, the angles for I4 and I2 will be ~127 deg, and for I1 and I3 it should be ~53 deg. (this could be fine-tuned to be exactly 60/120, and depends on the 2.0 factor for carX when computing isoY - it should be sqrt(3) rather than 2.0 but meh, close enough)
if you use the inverse transform, you can turn back the I1-4 coordinates into C1-4, or locate a world coordinate from a screen coordinate etc.
implementing a camera / viewport gets a little tricky if only at first but it's beyond what was asked so i won't go there (without further prodding)...
(Edit) Regarding SDL...
SDL does not appear to "play nice" with generalized transforms. I haven't used it but its interface is remarkably similar to GDI (windows) which I've played around with for a game engine before and ran into this exact issue (rotating + scaling textures).
There is one (looks to be non-standard) SDL function that does both scaling and rotating of textures, but it does it in the wrong order so it always maintains the perspective of the image, and that's not what is needed here.
Basic geometry will be fine, as you've seen, because it's fills and lines which don't need to be scaled, only positioned. But for textures... You're going to have to either write code to render the texture one pixel at a time, or use a combination of transforms (scale after rotate), or layering (drawing an alpha-masked isometric square and rendering a pre-computed texture) and so on...
Or of course, if it's an option for you, use something suited for raw geometry and texture data like OpenGL / Direct3D. Personally I'd go with OpenGL / SFML for something like this.
Unfortunately I cannot comment to ask for clarification so I must answer with a question: can you not convert all four points then from those points calculate the width and height from the transformed points?
X=20, Y=10, Width=16, Height=16
as you've said
isometricX = cartX - cartY;
isometricY = (cartX + cartY) / 2;
so
isometricX1 = cartX1 - cartY1;
isometricY1 = (cartX1 + cartY1) / 2;
and
isometricWidth = std::abs(isometricY - isometricY1)
There's probably a more efficient route though but as I do not know Cartesian geometry I can't find that solution
EDITisometricWidth found the distance between the two points not the width and hieght
another note is you'd need opposite corners (yes I realize the other guy probably as a much better answer :P)

Precision problems in raycasting algorithm

I am programming a raycasting engine.
The starting position of a ray is given by the position of the player who is standing inside a 2D-grid.
When casting a ray into a direction, I have to determine the grids that the ray is intersecting.
(The in-depth description of the concept is here: http://www.permadi.com/tutorial/raycast/rayc7.html)
There is a small inaccuracy which causes some trouble. I believe that the problem is caused by an incorrect calculation of the grid steps.
However I lack the mathematical understanding to fix that problem.
Problem description:
When the ray is heading to the left, the grid intersection step size is slightly different than the step size when it's heading to the right.
(Why do I even care about this?: It's causing the problem that some horizontal grid intersections are further away from the player than vertical grid intersections when vertical and horizontal intersections are close to each other, specifically in corners. Since I am using different textures for vertical intersections, the look of horizontal walls are ruined, because a vertical texture is used on small parts of the wall, even though it's an horizontal wall.)
Is this problem caused by a flaw in my algorithm? This is how I calculate the first horizontal grid intersection and the grid stepsize:
Find first grid intersection:
if (current_angle > 180) {
first_grid_horizontal_y = ((int)p.pos_y / Field::width) * Field::width + Field::width;
} else {
first_grid_horizontal_y = ((int)p.pos_y / Field::width) * Field::width - 1;
}
first_grid_horizontal_x = p.pos_x + (p.pos_y - first_grid_horizontal_y) / tan( 180 - current_angle);
Calculate the step size:
if (current_angle > 180) {
grid_stepsize_horizontal_y = Field::width;
grid_stepsize_horizontal_x = Field::width / tan(current_angle - 180);
} else {
grid_stepsize_horizontal_y = -Field::width;
grid_stepsize_horizontal_x = Field::width / tan(180 - current_angle);
}
As you can see I am always using "180 - current angle" to determine the direction of the x-value. Does this cause the inaccurancy? Do I have to differentiate more between angles?
Trigonometric functions work with radians, not degrees. So
tan(180 - current_angle)
should look like
tan(Math.Pi - current_angle)
Note that it is equal to
- tan(current_angle)
If your current_angle is in degrees that:
current_angle_radians = current_angle_degrees * Math.Pi / 180
I think your inaccuracy comes from subracting 1 when going upwards.
The idea behind this is probably that you want to get the index of the last pixel of a block, but that is inneccessary: You are doing floating-point math here and the y value of the next horizontal intersection should represent the line between the grid cells. If you go downward, it represents to uppermost coordinate of a cell; if you go upwards it represents the lowermost coordinate.
(Aside: (int) y / w will round towards zero and will thus only work for non-negative numbers. You might consider using floor(x / w).)

When drawing an ellipse or circle with OpenGL, how many vertices should we use?

Should we just blindly use 360 vertices? 720 seems to work better, but where do we stop?
It depends on how much error you can tolerate (i.e. the visual quality) and the size of the circle (ellipse). A bigger circle will need more points to achieve the same quality. You can work out exactly how many points you need for a given error with a bit of maths.
If you consider the circle represented by a series of line segments, the end points of the line segments lie exactly on the circle (ignoring the pixel grid). The biggest deviation between the real circle and our line segment representation occurs in the center of each line segment, and this error is the same for all of the line segments.
Looking at the first segment from the x-axis going anti-clockwise, its two endpoints are:
A = (r, 0)
B = (r . cos(th), r . sin(th))
where r is the radius of the circle and th is the angle covered by each line segment (e.g. if we have 720 points then each line segment covers 0.5 degree so th would be 0.5 degrees).
The midpoint of this line segment is at
M = A + (B - A) / 2
= (r, 0) + (r (cos(th) - 1) / 2, r . sin(th) / 2)
= (r / 2) . (1 + cos(th), sin(th))
and the distance from the origin to the point is
l = (r / 2) . sqrt((1 + cos(th))^2 + (sin(th))^2)
= (r / 2) . sqrt(2) . sqrt(1 + cos(th))
If our line segment representation were perfect then this length should be equal to the radius (the midpoint of the line segment should fall on the circle). Normally there'll be some error and this point will be slightly less than the radius. The error is
e = r - l
= r . (1 - sqrt(2) . sqrt(1 + cos(th)) / 2)
Rearranging so we have th in terms of e and r
2 . e / r = 2 - sqrt(2) . sqrt(1 + cos(th))
sqrt(2) . sqrt(1 + cos(th)) = 2 . (1 - e / r)
1 + cos(th) = 2 . (1 - e / r)^2
th = arccos(2 . (1 - e / r)^2 - 1)
This lets us calculate the maximum angle we can have between each point to achieve a certain error. For example, say we're drawing a circle with a radius of 100 pixels and we want a maximum error of 0.5 pixels. We can calculate
th = arccos(2 . (1 - 0.5 / 100)^2 - 1))
= 11.46 degrees
This corresponds to ceil(360 / 11.46) = 32 points. So if we draw a circle of radius 100 using 32 points our worst pixel will be off by less than a half which should mean every pixel we draw will be in the correct place (ignoring aliasing).
This kind of analysis can be performed for ellipses too, but in the spirit of all good maths that is left as an exercise for the reader ;) (the only difference is determining where the maximum error occurs).
as many as the resolution you are using requires, or as many as the visual result requires an accurate representation. It's difficult to say, and mostly depends on what you want to achieve. In a CAD program, having a circle visually similar to an octagon could be annoying. On the other hand, if you are programming a game on the iphone, if the wheel of a car looks like an octagon it's not a big deal.
A possible strategy you could use is to evaluate the length of each segment with respect to the resolution of the current view, and if longer than, say, 3 pixels, increase the number of vertexes you use, but only for the visible segments. This way, you increase the resolution when zooming in, but you don't have to describe vertexes you are not going to draw.