How are angles on QPainterPath::arcTo interpreted? - qt

I'm working on feature of a graphic editor where I'm editing arcs, and QPainterPath::arcTo is not behaving as I expected when the shape is an ellipse; it works as expected when it's a circle.
The two images below show the results. In the first case, I've created a circle, which I then convert to an arc with an initial start angle of 45 and span angle of 270. The scene coordinate space is square. The diagonal lines are at 45 degrees. As expected, the circular arc's end points are exactly on the diagonal lines.
In the second case, I have an ellipse, which is converted to an arc in exactly the same way with 45 and 270 degree angles respectively. The end points of the arc no longer fall on the diagonal lines, which is not what I expect.
In both cases, the drawing code is:
painter.arcTo (rect, 45, 270);
Zero degrees is at the 3 o'clock position, and I had believed that the specified angle was measured between that and a line from the center point to the point on the arc edge. Clearly, something else is going on that I don't understand and doesn't appear to be documented in the QPainter::arcTo description.
This is an issue because I'm writing code to reshape the arc, and I need to be able to work backgrounds when all I have is the current mouse position and the center point of the encompassing rectangle. Right now, as I reshape the arc, the angle that I'm calculating is only accurate at 0, 90, 180, and 270. The closer I get to the intervening 45 degree angles, the further off my angle is.
I'm getting that angle by:
QLineF (rect.center(), mouse_pos).angle ();
Again, for circles, this works perfectly. For non-circular ellipses, it doesn't.
After writing this up, I found this beautiful illustration, which exactly demonstrates what I'm dealing with. Unfortunately, the Postscript solution isn't helpful for me. I need to know what to do to calculate the correct angles.

I've found my answer here. As I expected, my understanding of the angles was incorrect. To perform my mouse tracking to reshape the arc, I need to find the intersection of a line segment with an ellipse and work backward from the parametric ellipse equations to find the correct angle.

Thanks to #goug I was able to fix a similar issue with QPainterPath::arcTo.
In my case I need an elliptical arc that behaves like a normal arc. Where start and end angles are controlled by a user. The start angle doesn't require any corrections, but the end angle does.
Here is a code that shows how to bypass this issue.
qreal startAngle = 10;
qreal endAngle = 60;
qreal radius1 = 30; // X-axis
qreal radius2 = 60; // Y-axis
QPointF center;
QRectF boundingRect(center.x() - radius1, center.y() - radius2, radius1*2, radius2*2);
if (!qFuzzyIsNull(endAngle) &&
!VFuzzyComparePossibleNulls(endAngle, 90) &&
!VFuzzyComparePossibleNulls(endAngle, 180) &&
!VFuzzyComparePossibleNulls(endAngle, 270) &&
!VFuzzyComparePossibleNulls(endAngle, 360))
{
// Calculating correct end angle
qreal endAngleRad = qDegreesToRadians(endAngle);
endAngle = qRadiansToDegrees(qAtan2(radius1 * qSin(endAngleRad),
radius2 * qCos(endAngleRad)));
}
QLineF startLine(center.x(), center.y(), center.x() + radius1, center.y());
QLineF endLine = startLine;
startLine.setAngle(startAngle);
endLine.setAngle(endAngle);
qreal sweepAngle = startLine.angleTo(endLine);
QPainterPath myPath;
myPath.arcTo(boundingRect, startAngle, sweepLength);

Related

Calculating new angle for player to look at some 3d world object

I am working on a Unity game where the Euler angles of the player display some weird behaviour that I cannot understand. There seem to be two full 360 degrees rotations, one positive and one negative. Depending on the direction you go when you are at 0 degrees, it will either take the negative path or the positive path. This means the player can have totally different yaw values depending on if it takes the green or red path. See the following image to get an idea of what is happening:
The issue now comes when I want to calculate the new angle for the player to look at some specific object in the 3d world space. I calculate this angle using some simple math:
// make player the origin, so the target is relative to the player
Vector3 delta = player.angles - target.angles;
float magnitude = delta.Length();
float pitch = asin((delta.y) / magnitude) * (180 / M_PI);
float yaw = -atan2(delta.x, -delta.z) * (180 / M_PI);
This will give me back correct angles, but the yaw is from 0 to 180, then -180 to 0 again. I correct for this doing:
// this makes sure the calculated angle is from 0-360
if (yaw < 0) {
yaw += 360; // normalize yaw
}
So, right now this is aligned with the red rotation line illustrated in the picture above. If I would set the players rotation to the new calculated angle, it would look correctly at the target object directly. But ONLY when the player is on the red rotation path.
If the player for example is on the green rotation path the calculated angle doesn't fit for the current users rotation. If I would set the rotation now, things get quirky. Somehow I need to compensate the calculated new player angle.
What is going on here? and is it possible to manipulate the new calculated angle (which ranges always from 0-360) so it is based on the current players rotation path (either green or red)?
I hope my question makes sense, I found it quite hard to follow what is going on. I hope someone could explain me the situation and ultimately help me out to fix the new calculated angle so it adjusts to the current player rotation path!
Any help is appreciated, thanks in advance!
EDIT
So I came up with the following to make sure the player is always rotated the shortest amount, taking both green and red rotation paths into consideration:
// normalize angles for when using negative rotation
if (localAngles[1] < 0)
{
angles[1] -= 360;
}
double diffPitch = (angles[0] - localAngles[0]);
double diffYaw = (angles[1] - localAngles[1]);
// this makes sure we always take the shortest amount of degrees to target
if (diffYaw < -180)
{
diffYaw += 360;
}
if (diffYaw > 180)
{
diffYaw -= 360;
}
I will have to test some more to be sure this is the solution.

Create a formula for velocity based on rotation in 2d?

So I have a bit of a math problem. Here are the pieces.
Input:
Rot = Rotation (degrees). This is the rotation of the "player". This is also the yaw.
Vel.X = This is the left/rightward movement that would be happening if it weren't rotated
Vel.Z = Same as last except its up/down movement
Output:
Result.X = This is the actual movement that should be happening along the x axis considering rotation
Result.Z = Same as last
Basically the scenario is that a player is standing on a platform with "Rot" rotation. When directional keys are pressed velocity is added accordingly to the "Vel" value. However if rotation isn't 0 this wont produce the right result because when the player rotates moving left becomes relative.
Could you please tell me a formula that would find the proper x and y movement that would result in the player moving around relative to its rotation?
This problem is probably the most basic rotation question in game programming.
Using your Vel.X and Vel.Z values, you have what you might think of as the vector you wish to rotate in the x/z plane (instead of x/y - but same idea). Whether velocity or position, the approach is the same. With a simple google search we find that for 2D vector rotation, the formula is:
Result.X = Vel.X * cos(Rot) - Vel.Z * sin(Rot);
Result.Z = Vel.X * sin(Rot) + Vel.Z * cos(Rot);

Dynamic Shape Creation(Processing)

I'm trying to create a program that creates a custom pattern. I have it so that if sides = 3, it's a triangle 4 = rect and anything else above that has a formula so that, if you really wanted, you could have 25 sides. I'm using lines, rotation and rotation to plant, turn, draw repeat.
angleMeasure = (180 * (sides-2) ) /sides;
println(angleMeasure);
println(radians(angleMeasure));
//creating the 5+ shape
pushMatrix();
translate(width/2, height/2); //translating the whole shape/while loop
while(counter < sides){
line(0,0,170,0);
translate(170,0);//THIS translate is what makes the lines go in the direction they need too.
rotate(angleMeasure);
counter = counter + 1;
This works almost correctly. The last and first lines don't connect. Suggestions? Maybe it's a problem with the math, but println reveals a correct angle measure in degrees. Here's what it looks like: http://i.stack.imgur.com/TwYMj.png
EDIT: Changed the rotate from rotate(angleMeasure) to rotate(angleMeasure * -1). This rotated the whole shape and made it clear that the angle on the very first line is off. See:http://i.stack.imgur.com/Z1KmY.png
You actually need to turn by angle=360°/sides. And convert this angle into radians.
Thus for a pentagram you need angle=72°. The number you computed is 108, which interpreted as radians is 34 full turns plus an angle of about 67°. This falls 5° short of the correct angle, so that you obtain a somewhat correct picture with slightly too wide inner angles, resulting in the gap (and not a crossing as when the angle were larger than the correct angle).

How do I calculate pixel shader depth to render a circle drawn on a point sprite as a sphere that will intersect with other objects?

I am writing a shader to render spheres on point sprites, by drawing shaded circles, and need to write a depth component as well as colour in order that spheres near each other will intersect correctly.
I am using code similar to that written by Johna Holwerda:
void PS_ShowDepth(VS_OUTPUT input, out float4 color: COLOR0,out float depth : DEPTH)
{
float dist = length (input.uv - float2 (0.5f, 0.5f)); //get the distance form the center of the point-sprite
float alpha = saturate(sign (0.5f - dist));
sphereDepth = cos (dist * 3.14159) * sphereThickness * particleSize; //calculate how thick the sphere should be; sphereThickness is a variable.
depth = saturate (sphereDepth + input.color.w); //input.color.w represents the depth value of the pixel on the point-sprite
color = float4 (depth.xxx ,alpha ); //or anything else you might need in future passes
}
The video at that link gives a good idea of the effect I'm after: those spheres drawn on point sprites intersect correctly. I've added images below to illustrate too.
I can calculate the depth of the point sprite itself fine. However, I am not sure show to calculate the thickness of the sphere at a pixel in order to add it to the sprite's depth, to give a final depth value. (The above code uses a variable rather than calculating it.)
I've been working on this on and off for several weeks but haven't figured it out - I'm sure it's simple, but it's something my brain hasn't twigged.
Direct3D 9's point sprite sizes are calculated in pixels, and my sprites have several sizes - both by falloff due to distance (I implemented the same algorithm the old fixed-function pipeline used for point size computations in my vertex shader) and also due to what the sprite represents.
How do I go from the data I have in a pixel shader (sprite location, sprite depth, original world-space radius, radius in pixels onscreen, normalised distance of the pixel in question from the centre of the sprite) to a depth value? A partial solution simply of sprite size to sphere thickness in depth coordinates would be fine - that can be scaled by the normalised distance from the centre to get the thickness of the sphere at a pixel.
I am using Direct3D 9 and HLSL with shader model 3 as the upper SM limit.
In pictures
To demonstrate the technique, and the point at which I'm having trouble:
Start with two point sprites, and in the pixel shader draw a circle on each, using clip to remove fragments outside the circle's boundary:
One will render above the other, since after all they are flat surfaces.
Now, make the shader more advanced, and draw the circle as though it was a sphere, with lighting. Note that even though the flat sprites look 3D, they still draw with one fully in front of the other since it's an illusion: they are still flat.
(The above is easy; it's the final step I am having trouble with and am asking how to achieve.)
Now, instead of the pixel shader writing only colour values, it should write the depth as well:
void SpherePS (...any parameters...
out float4 oBackBuffer : COLOR0,
out float oDepth : DEPTH0 <- now also writing depth
)
{
Note that now the spheres intersect when the distance between them is smaller than their radiuses:
How do I calculate the correct depth value in order to achieve this final step?
Edit / Notes
Several people have commented that a real sphere will distort due to perspective, which may be especially visible at the edges of the screen, and so I should use a different technique. First, thanks for pointing that out, it's not necessarily obvious and is good for future readers! Second, my aim is not to render a perspective-correct sphere, but to render millions of data points fast, and visually I think a sphere-like object looks nicer than a flat sprite, and shows the spatial position better too. Slight distortion or lack of distortion does not matter. If you watch the demo video, you can see how it is a useful visual tool. I don't want to render actual sphere meshes because of the large number of triangles compared to a simple hardware-generated point sprite. I really do want to use the technique of point sprites, and I simply want to extend the extant demo technique in order to calculate the correct depth value, which in the demo was passed in as a variable with no source for how it was derived.
I came up with a solution yesterday, which which works well and and produces the desired result of a sphere drawn on the sprite, with a correct depth value which intersects with other objects and spheres in the scene. It may be less efficient than it needs to be (it calculates and projects two vertices per sprite, for example) and is probably not fully correct mathematically (it takes shortcuts), but it produces visually good results.
The technique
In order to write out the depth of the 'sphere', you need to calculate the radius of the sphere in depth coordinates - i.e., how thick half the sphere is. This amount can then be scaled as you write out each pixel on the sphere by how far from the centre of the sphere you are.
To calculate the radius in depth coordinates:
Vertex shader: in unprojected scene coordinates cast a ray from the eye through the sphere centre (that is, the vertex that represents the point sprite) and add the radius of the sphere. This gives you a point lying on the surface of the sphere. Project both the sprite vertex and your new sphere surface vertex, and calculate depth (z/w) for each. The different is the depth value you need.
Pixel Shader: to draw a circle you already calculate a normalised distance from the centre of the sprite, using clip to not draw pixels outside the circle. Since it's normalised (0-1), multiply this by the sphere depth (which is the depth value of the radius, i.e. the pixel at the centre of the sphere) and add to the depth of the flat sprite itself. This gives a depth thickest at the sphere centre to 0 and the edge, following the surface of the sphere. (Depending on how accurate you need it, use a cosine to get a curved thickness. I found linear gave perfectly fine-looking results.)
Code
This is not full code since my effects are for my company, but the code here is rewritten from my actual effect file omitting unnecessary / proprietary stuff, and should be complete enough to demonstrate the technique.
Vertex shader
void SphereVS(float4 vPos // Input vertex,
float fPointRadius, // Radius of circle / sphere in world coords
out float fDXScale, // Result of DirectX algorithm to scale the sprite size
out float fDepth, // Flat sprite depth
out float4 oPos : POSITION0, // Projected sprite position
out float fDiameter : PSIZE, // Sprite size in pixels (DX point sprites are sized in px)
out float fSphereRadiusDepth : TEXCOORDn // Radius of the sphere in depth coords
{
...
// Normal projection
oPos = mul(vPos, g_mWorldViewProj);
// DX depth (of the flat billboarded point sprite)
fDepth = oPos.z / oPos.w;
// Also scale the sprite size - DX specifies a point sprite's size in pixels.
// One (old) algorithm is in http://msdn.microsoft.com/en-us/library/windows/desktop/bb147281(v=vs.85).aspx
fDXScale = ...;
fDiameter = fDXScale * fPointRadius;
// Finally, the key: what's the depth coord to use for the thickness of the sphere?
fSphereRadiusDepth = CalculateSphereDepth(vPos, fPointRadius, fDepth, fDXScale);
...
}
All standard stuff, but I include it to show how it's used.
The key method and the answer to the question is:
float CalculateSphereDepth(float4 vPos, float fPointRadius, float fSphereCenterDepth, float fDXScale) {
// Calculate sphere depth. Do this by calculating a point on the
// far side of the sphere, ie cast a ray from the eye, through the
// point sprite vertex (the sphere center) and extend it by the radius
// of the sphere
// The difference in depths between the sphere center and the sphere
// edge is then used to write out sphere 'depth' on the sprite.
float4 vRayDir = vPos - g_vecEyePos;
float fLength = length(vRayDir);
vRayDir = normalize(vRayDir);
fLength = fLength + vPointRadius; // Distance from eye through sphere center to edge of sphere
float4 oSphereEdgePos = g_vecEyePos + (fLength * vRayDir); // Point on the edge of the sphere
oSphereEdgePos.w = 1.0;
oSphereEdgePos = mul(oSphereEdgePos, g_mWorldViewProj); // Project it
// DX depth calculation of the projected sphere-edge point
const float fSphereEdgeDepth = oSphereEdgePos.z / oSphereEdgePos.w;
float fSphereRadiusDepth = fSphereCenterDepth - fSphereEdgeDepth; // Difference between center and edge of sphere
fSphereRadiusDepth *= fDXScale; // Account for sphere scaling
return fSphereRadiusDepth;
}
Pixel shader
void SpherePS(
...
float fSpriteDepth : TEXCOORD0,
float fSphereRadiusDepth : TEXCOORD1,
out float4 oFragment : COLOR0,
out float fSphereDepth : DEPTH0
)
{
float fCircleDist = ...; // See example code in the question
// 0-1 value from the center of the sprite, use clip to form the sprite into a circle
clip(fCircleDist);
fSphereDepth = fSpriteDepth + (fCircleDist * fSphereRadiusDepth);
// And calculate a pixel color
oFragment = ...; // Add lighting etc here
}
This code omits lighting etc. To calculate how far the pixel is from the centre of the sprite (to get fCircleDist) see the example code in the question (calculates 'float dist = ...') which already drew a circle.
The end result is...
Result
Voila, point sprites drawing spheres.
Notes
The scaling algorithm for the sprites may require the depth to be
scaled, too. I am not sure that line is correct.
It is not fully mathematically correct (takes shortcuts)
but as you can see the result is visually correct
When using millions of sprites, I still get a good rendering speed (<10ms per frame for 3 million sprites, on a VMWare Fusion emulated Direct3D device)
The first big mistake is that a real 3d sphere will not project to a circle under perspective 3d projection.
This is very non intuitive, but look at some pictures, especially with a large field of view and off center spheres.
Second, I would recommend against using point sprites in the beginning, it might make things harder than necessary, especially considering the first point. Just draw a generous bounding quad around your sphere and go from there.
In your shader you should have the screen space position as an input. From that, the view transform, and your projection matrix you can get to a line in eye space. You need to intersect this line with the sphere in eye space (raytracing), get the eye space intersection point, and transform that back to screen space. Then output 1/w as depth. I am not doing the math for you here because I am a bit drunk and lazy and I don't think that's what you really want to do anyway. It's a great exercise in linear algebra though, so maybe you should try it. :)
The effect you are probably trying to do is called Depth Sprites and is usually used only with an orthographic projection and with the depth of a sprite stored in a texture. Just store the depth along with your color for example in the alpha channel and just output
eye.z+(storeddepth-.5)*depthofsprite.
Sphere will not project into a circle in general case. Here is the solution.
This technique is called spherical billboards. An in-depth description can be found in this paper:
Spherical Billboards and their Application to Rendering Explosions
You draw point sprites as quads and then sample a depth texture in order to find the distance between per-pixel Z-value and your current Z-coordinate. The distance between the sampled Z-value and current Z affects the opacity of the pixel to make it look like a sphere while intersecting underlying geometry. Authors of the paper suggest the following code to compute opacity:
float Opacity(float3 P, float3 Q, float r, float2 scr)
{
float alpha = 0;
float d = length(P.xy - Q.xy);
if(d < r) {
float w = sqrt(r*r - d*d);
float F = P.z - w;
float B = P.z + w;
float Zs = tex2D(Depth, scr);
float ds = min(Zs, B) - max(f, F);
alpha = 1 - exp(-tau * (1-d/r) * ds);
}
return alpha;
}
This will prevent sharp intersections of your billboards with the scene geometry.
In case point-sprites pipeline is difficult to control (i can say only about OpenGL and not DirectX) it is better to use GPU-accelerated billboarding: you supply 4 equal 3D vertices that match the center of the particle. Then you move them into the appropriate billboard corners in a vertex shader, i.e:
if ( idx == 0 ) ParticlePos += (-X - Y);
if ( idx == 1 ) ParticlePos += (+X - Y);
if ( idx == 2 ) ParticlePos += (+X + Y);
if ( idx == 3 ) ParticlePos += (-X + Y);
This is more oriented to the modern GPU pipeline and of coarse will work with any nondegenerate perspective projection.

Drawing objects in aligned to face normal in OpenGL

I am trying to draw normal handle (a tall rectangle shape up to now) on geometry faces, in the center of face and pointing along normal.
I am doing it with the code below:
Vec3 up(0.0, 1.0, 0.0);
Vec3 angle_to_rotate = up.cross(face_normal);
double dot = up.dot(normal);
float rotate_angle = std::acos(dot);
Mat4 matrix;
matrix.translate(face_center.x, face_center.y, face_center.z);
matrix.rotate(angle_to_rotate, rotate_angle);
glMultMatrixd(matrix.copyGL());
then I am drawing a tall cube in world space (y up).
This works ok sometimes, but sometimes (45 angles in two dimension) it fails, or the angle direction is correct, but the rectangle looks like rotated on z-axis and is not aligned right.
Is this correct way of achieving drawing objects aligned with (maybe in future perpendicular to too) normal in OpenGL or is there a better way?
The cross-product of two normalized vectors is not normalized itself. The length of the vector is actually the sinus of the rotation angle.
You could use that to get a more accurate value for the angle:
Vec3 axis = up.cross(face_normal);
float angle = std::atan2(axis.length(), up.dot(face_normal));
axis = axis.normalize();

Resources