dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.27k stars 4.73k forks source link

Plane.D gives a negative distance for the Z = 10 Plane?! #24758

Closed ghost closed 4 years ago

ghost commented 6 years ago

If this is not a bug, at least it is a good example showing why I keep asking the implementers about their conventions and why not putting them in the documentation:

var P = Plane.CreateFromVertices(
     new Vector3(0, 0, 10), 
     new Vector3(6, 0, 10), 
     new Vector3(3, 4, 10));
Console.WriteLine(P.D);

D is -10. Why? This is the Z = 10 plane, so where the negative sign came from? Documents says:

The distance of the plane along its normal from the origin

This should give a positive distance ! Above all, distance should be an absolute value, because the normal vector holds the direction.

EDIT: In this example, the normal vectors for the planes P1 and P2 are identical, although the two planes are parallel and in oposite directions of the Z axis.

            var P1 = Plane.CreateFromVertices(
                            new Vector3(0, 0, 10), 
                            new Vector3(6, 0, 10), 
                            new Vector3(3, 4, 10));
            Console.WriteLine(P1.Normal.ToString()); // <0, 0, 1>

            var P2 = Plane.CreateFromVertices(
                           new Vector3(0, 0, -10), 
                           new Vector3(6, 0, -10), 
                           new Vector3(3, 4, -10));
            Console.WriteLine(P2.Normal.ToString()); // <0, 0, 1>

This indicates that the origin is not always the start point of the vector, which is strang and confusing! What is the rule used here if this is not a bug? It is always a good thing to state the basic conventions in documents. At least we can report bugs when we know they are bugs!!

EDIT: D shounldn't be refered to as "the distance in documentation". Distance is a scaler value that is always positive. Abs(D) is the distance! If the normal vector (a, b, c) is normalized, then the plane equation is: ax + by + cz + D = 0 By the way, this equation is mentioned in DirectX documents. It is important to put this equation in the documents of the Plane constructor. Now it is clear why D has a sign, and why the field is called D not Distance. It is a coefficient not just the absolute distance. May be things be less confusing if you added a Distance property besides the D field. This equation give meaning to some plane methods like Plane.DotCoordinate which can be used to determine if a point belongs to the plane (if it returns 0). The problem with this method is that plane must have a normalized normal vector to give the desired result, but the plane can be constructed with a vector that is not normalized! Maybe Plane.Normal should always return the normalized normal vector. another problem of cource is that it may not return 0 because single. I think adding such notes to documentation would make programmers life happier. Programmers don't care about math. They want a method to determine if a point is on the plane surface or not, to detect a collision or something. Maybe it's better to add a Plane.ContainsPoint method and make sure to avoid the single round problem. Maybe something like this:

    public bool ContainsPoint(Vector3 point)
    {
        var n = this.Normal;
        if( n.Length != 1)
             n = Vector3.Normalize(n);
        return Math.Round(Vector3.Dot(point, n) + this.D) == 0;
    }
tannergooding commented 6 years ago

You are creating a plane that contains 3 specified locations.

The plane is defined as a point and normal vector.

The normal of the vector ends up being:

n = ((6, 0, 10) - (0, 0, 10)) cross ((3, 4, 10) - (0, 0, 10))
  = (6, 0, 0) cross (3, 4, 0)
  = (0, 0, 24)

When normalized, this becomes:

n = n / n.length
  = (0, 0, 24) / sqrt((0 * 0) + (0 * 0) + (24 * 24))
  = (0, 0, 24) / sqrt(0 + 0 + 576)
  = (0, 0, 24) / sqrt(576)
  = (0, 0, 24) / 24
  = (0, 0, 1)

d is then defined as:

d = -((0, 0, 1) dot (6, 0, 10))
  = -((0 * 6) + (0 * 0) + (1 * 10))
  = -(0 + 0 + 10)
  = -10
tannergooding commented 6 years ago

As stated on the other thread, many of these types require some knowledge of linear algebra to understand correctly.

All of the behaviors are (to my knowledge) following the standard rules for defining the corresponding geometric type.

ghost commented 6 years ago

@tannergooding If you look at the tanh method documentation here: https://msdn.microsoft.com/en-us/library/system.math.tanh(v=vs.110).aspx You will see these notes:

Return Value Type: System.Double The hyperbolic tangent of value. If value is equal to NegativeInfinity, this method returns -1. If value is equal to PositiveInfinity, this method returns 1. If value is equal to NaN, this method returns NaN. Remarks The angle, value, must be in radians. Multiply by Math.PI/180 to convert degrees to radians

Writing important remarks about input and output values is familiar in. Net documents. Negative space is unusual thing and can cause unexpected results if used in other calculations. A note about the sign related to where the point resides from the plane can save a lot of time. You can find others asking about this sign in other forums.

tannergooding commented 6 years ago

If you look at the tanh method documentation here:

The tanh documentation is describing the behavior for special inputs which may behave differently or which are handled specifically due to the underlying format of the float and double types (which are the IEEE 754 binary32 and binary64 types, respectively).

Negative space is unusual thing and can cause unexpected results if used in other calculations.

Negative coordinates are not unusual. In Euclidean space, when using a Cartesian Coordinate system:

All of the trigonometric functions, which you mentioned the documentation on, deal with both positive and negative coordinates. Yet the documentation makes no mention of that, because you are expected to have a basic understanding of the functions already. The documentation only explicitly lists the special cases.

tannergooding commented 6 years ago

Also, to be clear, I am not saying that the documentation couldn't be improved. It definitely could be more explicit stating that these are Euclidean types and operating in a Cartesian coordinate system (I believe some of our functions assume a Right-Handed system, but I don't recall off the top of my head).

However, going in depth in the underlying mathematics behind the functions is clearly out of scope (IMO).

tannergooding commented 6 years ago

CC. @eerhardt

ghost commented 6 years ago

A distance is an absolute value resulting from the difference betwwn two points in the coordinates. D in my example resides in the positive side of the z axes. The confusion comes from using the origin point in calculating D, so there are two references: the oeriginal axis and the plane surface!.. This is unusual! Negative distance seems like a quantum mechanics concept or something. Anyway, a small hint wouldn't heart.

tannergooding commented 6 years ago

A distance is an absolute value resulting from the difference betwwn two points in the coordinates.

I think a more accurate description might be: A distance, when no direction is provided, is an absolute value resulting from the difference between two points in the coordinates.

Think of it like this:

In the example you provided, we are facing (0, 0, 1) and we moved 10 steps backwards. What you see when looking forward and moving 10 steps backwards is completely different from what you see if you turn around and move 10 steps forward, even though you ultimately start and end at the same locations.

ghost commented 6 years ago

@tannergooding There is another confusion: If the sign of D is -ve if the point (which is the origin here) resides on the opposite side of the normal vector, so why the Z = -10 plane has (D = 10) in this example, while the origin is also at the opposite side of its normal vector?

var P = Plane.CreateFromVertices(
     new Vector3(0, 0, -10), 
     new Vector3(6, 0, -10), 
     new Vector3(3, 4, -10));
Console.WriteLine(P.D);

What is the concept used here? How can I know the right sign when I send the distance to the Plane's constructor?.. Why can't I send an absolute value? This ambiguity can produce undesired results.

tannergooding commented 6 years ago

The normals of the two planes are different and face in opposite directions: image

ghost commented 6 years ago

Yes, and this makes the origin resides on the -ve face of each planes, so it should be a -ve destsnce as I understand! Do I miss something here?

ghost commented 6 years ago

I tested the two cases. The Normal of the two planes, as given by P.Normal property, is: <0, 0, 1> This is the reason of the sign of D in each case. But, Why are the two planes have the same normal vector? This is why I keep asking the implementors about the start and end points of the vector!

ghost commented 6 years ago

I'm not comfortable with the implementation of the vector as a single point. In Plane.CreateFromVertices method, Vector3 parameters are treated as points!! I would prefer to define a vector with start and end points and have properties or methods to calculate angles. If you have a vector AB not passing through the origin, you will have to do some calculations to find the correct point to define Vector2 ar vector3 in current implementaion! Or at least add a constructor to Vector structures thar accepts two points, so the subtraction of the two points to get the vectors end point can be optimized as SIMD operations.

tannergooding commented 6 years ago

Vectors should be more easier, realistic. and less confusing.

This is exactly how many other libraries have been doing all of this for years:

I don't believe our implementation is any less correct or more confusing than any other major vector math library in production use today.

In Plane.CreateFromVertices method, Vector3 parameters are treated as points

Yes. This is also a common practice in these types of libraries. A 3-dimensional vector and a 3-dimensional point each have the same properties, functions, etc. The primary difference between the two is what you call it and whether you treat the data as a point or a vector.

ghost commented 6 years ago

@tannergooding Thanks, but I'm still confused. What is the defense between these 2 planes: var P1 = new Plane(0, 0, 1, 10); var P2 = new Plane(0, 0, -1, -10); P1 == P2 returns false also, what is the defense between these 2 planes: var P3 = new Plane(0, 0, 1, -10); var P4 = new Plane(0, 0, -1, 10); P3 == P4 returns false

I tested other equalities. I found that P1, P2, P3 and P4 are considered 4 deferent planes! I can only imagine two of them, so what are the other two? Do you feel that the -ve distance have a useful meaning?

Thaina commented 6 years ago

@MohammadHamdyGhanem You can imagine only 2 because you don't consider that plane facing opposite direction is not the same plane

But facing is a property that could be used in some logic. So it is a difference plane

Think about integrate, there was an "signed area under the curve" and facing side is matter

ufcpp commented 6 years ago

@MohammadHamdyGhanem You should learn math before do programming.

Thaina commented 6 years ago

@ufcpp Don't be so harsh. I think this is just a problem of concept and definition. In some sense plane facing opposite direction could theoretically be considered as a same plane. It just definition in math library is not

Anyway @MohammadHamdyGhanem actually you should not and could not compare float and product of floats calculation with ==

ghost commented 6 years ago

@Thaina

actually you should not and could not compare float and product of floats calculation with ==

If you refer to decimal accuracy problem, I think the Equality Operator of the Plane Structure should handle it. But when I looked at source code, it seems not!

         public static bool operator ==(Plane value1, Plane value2) 
         { 
             return (value1.Normal.X == value2.Normal.X && 
                     value1.Normal.Y == value2.Normal.Y && 
                     value1.Normal.Z == value2.Normal.Z && 
                     value1.D == value2.D); 
         } 

So, can we trust the equality operations of the SIMD-Enabled types?

Thaina commented 6 years ago

@MohammadHamdyGhanem You cannot rely on equality operator anywhere in float operation. It does what the float does for equality. And it trustable that it would not be equal if the float came out with the same inequality for precision error

In fact it logically correct for equality function to not try to approximate precision error. Which is why you should not use it after any operation. Same go for every Math and Numerics struct related to float. That was the reason we have Vector3.Distance(l,r) function

The most logical reason is, we don't really know what each people and each application would consider how large the precision error would be acceptable

ghost commented 6 years ago

@Thaina Thanks for the warning :) I examind the sample of the CreateFromVertices. Changing the 3 points order changes the Normal direction an D sign. It seems there is some right-hand or left-hand rule.

            var P1 = Plane.CreateFromVertices(new Vector3(0, 0, 10),
                             new Vector3(3, 4, 10), new Vector3(6, 0, 10));
            MessageBox.Show(P1.Normal.ToString()); // <0, 0, -1>
            MessageBox.Show(P1.D.ToString()); // 10

            var P2 = Plane.CreateFromVertices(new Vector3(0, 0, 10),
                 new Vector3(6, 0, 10), new Vector3(3, 4, 10));
            MessageBox.Show(P2.Normal.ToString()); // <0, 0, 1>
            MessageBox.Show(P2.D.ToString()); // -10

This deserves a warning not in documentaions, beacuse the same three points can give two different planes, just by changing the order you write them! Thanks.

tannergooding commented 6 years ago

This deserves a warning not in documentaions

Agreed that the documentation could be updated for these types of things. As per my comment above (https://github.com/dotnet/corefx/issues/26497#issuecomment-359273640), we could definitely be more explicit about which coordinate space we are operating in and when the ordering of parameters matters.

beacuse the same three points can give two different planes, just by changing the order you write them!

This is standard behavior for this algorithm because the normal is defined as (p2 - p1) cross (p3 - p1) (https://github.com/dotnet/corefx/issues/26497#issuecomment-359267925)

tannergooding commented 6 years ago

@eerhardt, I think we should see if we can reuse the existing DirectX Math documentation, where applicable.

ghost commented 6 years ago

If the normal vector (a, b, c) is normalized, then the plane equation is: ax + by + cz + D = 0 By the way, this equation is mentioned in DirectX documents. It is important to put this equation in the documents of the Plane constructor. Now it is clear why D has a sign, and why the field is called D not Distance. It is a coefficient not just the absolute distance. May be things be less confusing if you added a Distance property besides the D field. This equation give meaning to some plane methods like Plane.DotCoordinate which can be used to determine if a point belongs to the plane (if it returns 0). The problem with this method is that plane must have a normalized normal vector to give the desired result, but the plane can be constructed with a vector that is not normalized! Maybe Plane.Normal should always return the normalized normal vector. another problem of cource is that it may not return 0 because single. I think adding such notes to documentation would make programmers life happier. Programmers don't care about math. They want a method to determine if a point is on the plane surface or not, to detect a collision or something. Maybe it's better to add a Plane.ContainsPoint method and make sure to avoid the single round problem. Maybe something like this:```

    public bool ContainsPoint(Vector3 point)
    {
        var n = this.Normal;
        if( n.Length != 1)
             n = Vector3.Normalize(n);
        return Math.Round(Vector3.Dot(point, n) + this.D) == 0;
    }
Thaina commented 6 years ago

@MohammadHamdyGhanem First thing first, NO. Programmers do care about math and they need to do. They must do. Programmer must care about math. In fact humanity should care about math

Even if/else is a math. It is boolean algebra

Second. Statistically, given arbitrary point and plane there would be ZERO chance that the point would be on the plane Hypothetical plane is infinitesimal thin. A random point will have infinitesimal chance to land on it. Mathematically way to do a point-on-plane checking is not to make an equation but using inequation to have it as near as the acceptable range, which, as I said, it varied between application. Astronomical application would accept range in Earth's diameter, and quantum mechanic application would accept range less than Planck's length

Third. It might be counter intuitive but having normal vector not normalized is applicable in case we want the product that scale. Plane with a size is like tensor field. It could encode not only direction but also tension. And calculate with it give you the scaled distance relative to the plane. Its about relativity. So it was more useful to leave all math function as plain as it is and programmer is responsible to normalize it if it need to, even if it is the most case, but it not leave the edge case be impossible

Lastly, all in all, everything I said is came from math and this proven that programming is math and programmer need to care about math. Go learn math. We need to learn math and we should learn math for whole life. We can learn math until we die

ghost commented 6 years ago

@Thaina You seem as a math fan :) I speak from RAD point of view. You love math, so put it in a good liberrary for us :).. Computers are invented to do the math. Let it do. Chicking a point on a plain is not that random. A plane is infinit surface that splits space into halfs. Any moving object will hit it eventually. Checking a point was just an example. Getting intersection point with a vector, ijntersection line with another plane, and defining areas are good things to do.

leave all math function as plain as it is

This is why I wrot the ContainsPoint function thst normalizes the normal vector first.

thanks.

Thaina commented 6 years ago

@MohammadHamdyGhanem I became a math fan AFTER I became programmer and knowing that programming, in fact computing, IS ALL about Math. It's unavoidable to love math as being programmer or else you can't go anywhere

Everything about research and development is also math

Computer made for computing, doing calculation. But that's not Math. Math is how to compute. You need to learn the difference between "doing calculation" and "doing equation to calculate", in short you need to learn math

You write equation for computer, computer don't write thing for you. You using library is SOMEONE help write it for you, not a Computer. No computer write equation for you unless there are AI that clever than human. Which is not exist yet

A plane is infinit surface that splits space into halfs. Any moving object will hit it eventually

This is why you need to learn math. There was infinite number of trajectory parallel to the plane in one more dimension than the infinite trajectory on the plane. If you take limit you will get zero

And if you talk about moving then mathematically that point is not a point but a line segment in four dimension. So the line-plane intersection would be another equation, not a point-plane distance equation

This is why you need to learn math as being programmer

ghost commented 6 years ago

@Thaina I have already learned math in fuculty of engineering before becoming a programner. I know probability as will. Math From an engineer's point of view is practical not theoretical. Each point we deal with exists on some surface. When you represent a plane in programming or physical problem you have some boundary conditions. In games things can have a random motion that can't be represented with a line or a curve. What you do if you want to check if the next move hits one of boundary planes? Anywsy, this is not our concern here. Plane equation exists to tell us about each point, line and area on this plane, which indicats intersections with other objects. I say it will be nice if Plane structure do these calculations for us. Yes of cource, a brilliant math programmer should do it for poor programmers like me :) Thanks for this good discussion.

Thaina commented 6 years ago

@MohammadHamdyGhanem In game programming we need to do sweep test in time dimension, which is as I said it would be line-plane intersection. You have the point of previous frame and point on current frame so it is line segment

Plane equation exists along with inequation which is the similar formula. You trying to use equation where you need inequation is flawed logic. No need to account precision error, just statistical, it not practical to do equality test with point which has no size

It like a math problem that ask, what is a chance to land a dart on the center of dart board, and answer is zero

That's what theoretical math could told you. It can tell you what really practical and what really impossible

eerhardt commented 6 years ago

I think the questions have been answered, and this issue can be closed. Please re-open if I am incorrect.