

On 5/11/21 4:50 AM, William F Pokorny wrote:
> OK. Been running both approaches within the c++ 'vectors.h' framework of
> POVRay. Initial idea is both forms could be in one inbuilt function and
> therefore "munction" too.
>
> // Original macro approach:
>
> #macro VAngle(V1, V2)
> acos(max(1,min(1,vdot(vnormalize(V1),vnormalize(V2)))))
> #end
>
>
> // Tor Olav's proposal for better accuracy:
> #macro AngleBetweenVectors(v1, v2)
>
> #local v1n = vnormalize(v1);
> #local v2n = vnormalize(v2);
>
> (2*atan2(vlength(v1n  v2n), vlength(v1n + v2n)))
>
> #end // macro AngleBetweenVectors
>
...
>
> I have still a few other tests in mind(1), but busy for a couple days 
> starting now.
>
> (1)  Different optimizations, etc.
...
All more complicated than expected and I'm not normally concise, but
I'll try...
I now have an inbuilt f_vangle() which supports both the old and Tor
Olav's method  and where both are faster when used within VAngle making
it now a "munction." I'll post more about f_vangle later in povray
unofficial patches.

Using more targeted random vectors near the noisy parts:
Largest diff 2 ver 0.00000003650005986 ~ 1e7
Max dot prod sum +1.00000000000000067 (3x DBL_EPSILON)
Min dot prod sum 1.00000000000000067
Min abs dot prod sum 0.00000000000000000

With the results above, one might ask how did we not have domain errors
on the 1 side with all those acos(dot(... based macros we have?
Ans: It turns out the parser version of acos tests the inputs, clamps if
need be and issues a warning on each baddomain call. This means any
math.inc clamping is really there to suppress the warning messages.
Why not a parse error over a warning is a good question  given warnings
can be ignored. It would be better for the user to learn and correct code.

The raw acos result and so the vm version of acos returns nan (not a
number) for bad inputs. Peripherally related compiler optimizations
related to the ffastmath optimizations cause internal routines like
POV_ISINF, POV_ISNAN (std::isnan and the like too) to always return
false. And, POV_ISFINITE and IsFinite() to always return true. These are
not much used, but this caught me up as I was trying to use them to pick
up domain errors initially.

Related to this VAngle look we have in math.inc a macro called
VCos_Angle which I believe should be clamped and it now is in my version.
// Cosine of angle between V1 and V2
// #macro VCos_Angle(V1, V2) vdot(vnormalize(V1), vnormalize(V2)) #end
// +14% over million calls to clamp, but we should clamp.
#macro VCos_Angle(V1, V2)
max(1,min(1,vdot(vnormalize(V1),vnormalize(V2))))
#end

By experiment only it looks like the old method gives us about 7
significant digits, Tor Olav's 12 significant digits. This is at
extremes, however.

I ran both approaches posted at top against over a million random vector
calls to measure the performance impact of the more accurate method.
One million calls to VAngle
9.65user 5.52system 0:15.70elapsed < Original acos(dot..)macro.
10.01user 5.24system 0:15.79elapsed with both min and max clip.
9.69user 5.35system 0:15.58elapsed
9.69user 5.36system 0:15.58elapsed
"9.65 9.69 9.69"
One million calls to AngleBetweenVectors
11.21user 5.31system 0:17.05elapsed
11.35user 5.26system 0:17.14elapsed
11.57user 5.48system 0:17.57elapsed
11.43user 5.45system 0:17.42elapsed
"11.21 11.35 11.43" 29.03 > 33.99 > +17.09%
Expect similar performance differences in any v3.8 based branch.
Bill P.
Post a reply to this message

