[ 
https://issues.apache.org/jira/browse/GEOMETRY-119?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17313613#comment-17313613
 ] 

Alex Herbert commented on GEOMETRY-119:
---------------------------------------

{quote}Note that this PR also addresses a normalization edge case that we 
weren't handling before, where the norm is real and non-zero but its inverse is 
not.
{quote}
Q. Do you want this behaviour?

Given that the input vector contains all finite values then it is possible to 
normalise it. It cannot be done by multiplying by the inverse of the norm since 
that is infinite. This is an edge case where the norm (vector length) is a 
sub-normal number.

Two solutions:
 # Divide by the norm
 # Scale the input vector and then call normalise

In either case this requires detecting that the inverse norm is infinite but 
the norm is finite (hence a solution exists). For example adding the extra 
check shown below using `elseĀ if (Vectors.isRealNonZero(norm))`:
{code:java}
private static Unit tryCreateNormalized(final double x, final double y, final 
double z,
        final boolean throwOnFailure) {
    final double norm = Vectors.norm(x, y, z);
    final double normInv = 1.0 / norm;

    if (Vectors.isRealNonZero(normInv)) {
        return new Unit(x * normInv, y * normInv, z * normInv);

    // NEW CODE
    } else if (Vectors.isRealNonZero(norm)) {
        // The norm is finite but the inverse is infinite,
        // thus the norm must be sub-normal (and XYZ are sub-normal) 
        // and we can re-scale the input.
        // The scale is a power of 2 to avoid loss of precision.
        final double scale = 0x1.0p1024;
        return tryCreateNormalized(x * scale, y * scale, z * scale, 
throwOnFailure);

        // Or (possible precision issues using divide)
        // return new Unit(x / norm, y / norm, z / norm);

    } else if (throwOnFailure) {
        // throw the most specific exception we can
        if (Vectors.isRealNonZero(norm)) {
            throw Vectors.illegalNormInverse(normInv);
        }
        throw Vectors.illegalNorm(norm);
    }
    return null;
}
{code}

> Vector normalizeOrDefault() method
> ----------------------------------
>
>                 Key: GEOMETRY-119
>                 URL: https://issues.apache.org/jira/browse/GEOMETRY-119
>             Project: Apache Commons Geometry
>          Issue Type: Improvement
>            Reporter: Matt Juntunen
>            Priority: Major
>
> A frequent use case when working with vectors, especially vectors coming from 
> external data, is attempting to normalize the vector, and if this is not 
> possible, to use an alternative value. For example, the 
> {{QuaternionRotation}} code 
> [here|https://github.com/apache/commons-geometry/blob/master/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/rotation/QuaternionRotation.java#L86]
>  does exactly this; it attempts to normalize a vector and failing that (ie, 
> if the vector is exactly zero), it returns a substitute value. The 
> {{QuaternionRotation}} class is able to take advantage of our internal 
> {{Vectors.tryNormalize()}} but callers outside of the library are not able to 
> do so and so are left with 2 choices:
> 1. wrap the {{normalize()}} call in a try-catch and handle the exception 
> thrown on illegal norm values, or
> 2. compute and test the norm prior to calling {{normalize()}} to ensure that 
> the call won't fail, resulting in 2 computations of the norm.
> Neither of these options are very good.
> I propose adding a new method to the Euclidean Vector classes to handle this 
> situation: {{normalizeOrDefault()}}. The method would accept a default value 
> (possibly null) to return if the vector cannot be normalized. The normal 
> would then only need to be computed once and an exception would not need to 
> be thrown in case of failure. The behavior of the current {{normalize}} 
> method would be the same.
> Examples:
> {code:java}
> // get some kind of normal, preferably vec but +z will also do
> Vector3D.Unit norm = vec.normalizeOrDefault(Vector3D.Unit.PLUS_Z);
> {code}
> {code:java}
> // throw a very use-case specific error message
> Vector3D norm = vec.normalizeOrDefault(null);
> if (norm == null) {
>     throw new Exception("Invalid triangle at index " + i + ": cannot compute 
> normal.");
> } 
> {code}



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to