You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Matt Juntunen (Jira)" <ji...@apache.org> on 2021/04/01 11:59:00 UTC

[jira] [Commented] (GEOMETRY-119) Vector normalizeOrDefault() method

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

Matt Juntunen commented on GEOMETRY-119:
----------------------------------------

I just ran some JMH benchmarks and there is a definite gain when using the {{normalizeOrDefault}} method over an explicit check on the norm before calling {{normalize}}, and a very small gain over using a try-catch.

Benchmark method bodies:
{code:java}
// safeNormalize3D
// (should also check for NaN, etc in actual usage)
if (v.norm() != 0.0) {
    return v.normalize();
}
return v;

// catchNormalize3D
try {
    return v.normalize();
} catch (IllegalArgumentException exc) {
    return null;
}

// normalizeOrDefault
v.normalizeOrDefault(null);
{code}
Results:
||Benchmark||(size)||Mode||Cnt||Score||Error||Units||
|VectorPerformance.safeNormalize3D|100|avgt|5|2831.197|± 135.857|ns/op|
|VectorPerformance.safeNormalize3D|10000|avgt|5|293549.458|± 8534.388|ns/op|
|VectorPerformance.catchNormalize3D|100|avgt|5|2699.497|± 685.524|ns/op|
|VectorPerformance.catchNormalize3D|10000|avgt|5|257472.523|± 19955.218|ns/op|
|VectorPerformance.normalizeOrDefault|100|avgt|5|2491.177|± 541.369|ns/op|
|VectorPerformance.normalizeOrDefault|10000|avgt|5|256620.115|± 49329.504|ns/op|

In addition to the performance benefits, there is the benefit of not requiring the caller to handle possible NaN or infinite norms (or inverse norms) before calling {{normalize}}.

If the current method signature is the issue (and not the functionality), then I would consider signatures like {{normalizeOrNull()}} or {{tryNormalize()}}, neither of which accept an argument. Now, that I think about it, {{normalizeOrNull()}} sounds better to me than {{normalizeOrDefault()}}.

> 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)