POV-Ray : Newsgroups : povray.general : Why only a +1 clamp with VAngle, VAngleD macros in math.inc? Server Time
28 Mar 2024 17:08:53 EDT (-0400)
  Why only a +1 clamp with VAngle, VAngleD macros in math.inc? (Message 1 to 10 of 15)  
Goto Latest 10 Messages Next 5 Messages >>>
From: William F Pokorny
Subject: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 6 May 2021 09:06:30
Message: <6093e9d6$1@news.povray.org>
In creating some testing for include file macros I came across code for 
the two VAngle* macros (and two similar rotation related macros):

#macro VAngle(V1, V2)
         acos(min(1, vdot(vnormalize(V1), vnormalize(V2))))
#end

The min(1,..) is obviously trying to protect acos from domain errors.

Why only the clamp to the positive side? Aren't we as exposed to <-1.0?

Often enough in code - including POV_Ray's own - I've seen code clamping 
to a [-1..1] range after a dot product into acos() for acos domain 
concerns.

I cannot come up with reasoning to clamp only the positive side. Anyone 
else? If valid to do, we might be able to slightly streamline some 
internal code.

Bill P.


Post a reply to this message

From: Alain Martel
Subject: Re: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 6 May 2021 10:28:33
Message: <6093fd11$1@news.povray.org>
Le 2021-05-06 à 09:06, William F Pokorny a écrit :
> In creating some testing for include file macros I came across code for 
> the two VAngle* macros (and two similar rotation related macros):
> 
> #macro VAngle(V1, V2)
>         acos(min(1, vdot(vnormalize(V1), vnormalize(V2))))
> #end
> 
> The min(1,..) is obviously trying to protect acos from domain errors.
> 
> Why only the clamp to the positive side? Aren't we as exposed to <-1.0?
> 
> Often enough in code - including POV_Ray's own - I've seen code clamping 
> to a [-1..1] range after a dot product into acos() for acos domain 
> concerns.
> 
> I cannot come up with reasoning to clamp only the positive side. Anyone 
> else? If valid to do, we might be able to slightly streamline some 
> internal code.
> 
> Bill P.
> 

The reason is quite simple : Normally, the vdot function of two 
normalized vectors can not return a value that is less than -1 nor more 
than 1.

So, the min() could probably be safely removed.

#macro VAngle(V1, V2)
	acos(vdot(vnormalize(V1), vnormalize(V2)))
#end


Post a reply to this message

From: Tor Olav Kristensen
Subject: Re: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 6 May 2021 11:00:00
Message: <web.6094040b3160e21b8e52cc8789db30a9@news.povray.org>
William F Pokorny <ano### [at] anonymousorg> wrote:
> In creating some testing for include file macros I came across code for
> the two VAngle* macros (and two similar rotation related macros):
>
> #macro VAngle(V1, V2)
>          acos(min(1, vdot(vnormalize(V1), vnormalize(V2))))
> #end
>
> The min(1,..) is obviously trying to protect acos from domain errors.
>
> Why only the clamp to the positive side? Aren't we as exposed to <-1.0?
>
> Often enough in code - including POV_Ray's own - I've seen code clamping
> to a [-1..1] range after a dot product into acos() for acos domain
> concerns.
>
> I cannot come up with reasoning to clamp only the positive side. Anyone
> else? If valid to do, we might be able to slightly streamline some
> internal code.

Perhaps the one that made that macro experienced a problem with at value from
the dot product being slightly higher than one so he inserted that min()
function to deal with it, without thinking through the problem thoroughly.

You are right there should also be a max() function there to discard values
below -1.

Anyway the way that macro calculates the angle is not the best numerically.
I would rather suggest something like this:


#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 not tested this macro yet, but the angle() function in my scikit-vectors
Python library seems to work ok. It works in is similar way as the macro above.

https://github.com/t-o-k/scikit-vectors/blob/master/skvectors/cartesian_vectors.py



"Computing Cross-Products and Rotations in 2- and 3-Dimensional Euclidian
Spaces"
https://people.eecs.berkeley.edu/~wkahan/MathH110/Cross.pdf

My opinion is that several of the other math related macros in the include files
need some rework.

--
Tor Olav
http://subcube.com
https://github.com/t-o-k


Post a reply to this message

From: Tor Olav Kristensen
Subject: Re: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 6 May 2021 11:05:00
Message: <web.609404fd3160e21b8e52cc8789db30a9@news.povray.org>
Alain Martel <kua### [at] videotronca> wrote:

> > In creating some testing for include file macros I came across code for
> > the two VAngle* macros (and two similar rotation related macros):
> >
> > #macro VAngle(V1, V2)

> > #end
> >
> > The min(1,..) is obviously trying to protect acos from domain errors.
> >
> > Why only the clamp to the positive side? Aren't we as exposed to <-1.0?
> >
> > Often enough in code - including POV_Ray's own - I've seen code clamping
> > to a [-1..1] range after a dot product into acos() for acos domain
> > concerns.
> >
> > I cannot come up with reasoning to clamp only the positive side. Anyone
> > else? If valid to do, we might be able to slightly streamline some
> > internal code.
> >
> > Bill P.
> >
>
> The reason is quite simple : Normally, the vdot function of two
> normalized vectors can not return a value that is less than -1 nor more
> than 1.

I think that this is only in theory. In practice there will sometimes be
roundoff errors that cause problems.


> So, the min() could probably be safely removed.
>
> #macro VAngle(V1, V2)
>  acos(vdot(vnormalize(V1), vnormalize(V2)))
> #end

--
Tor Olav
http://subcube.com
https://github.com/t-o-k


Post a reply to this message

From: William F Pokorny
Subject: Re: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 7 May 2021 07:13:18
Message: <609520ce$1@news.povray.org>
On 5/6/21 10:58 AM, Tor Olav Kristensen wrote:
> William F Pokorny <ano### [at] anonymousorg> wrote:
>> In creating some testing for include file macros I came across code for
>> the two VAngle* macros (and two similar rotation related macros):
>>
>> #macro VAngle(V1, V2)
>>           acos(min(1, vdot(vnormalize(V1), vnormalize(V2))))
>> #end
>>
>> The min(1,..) is obviously trying to protect acos from domain errors.
>>
>> Why only the clamp to the positive side? Aren't we as exposed to <-1.0?
>>
>> Often enough in code - including POV_Ray's own - I've seen code clamping
>> to a [-1..1] range after a dot product into acos() for acos domain
>> concerns.
>>
>> I cannot come up with reasoning to clamp only the positive side. Anyone
>> else? If valid to do, we might be able to slightly streamline some
>> internal code.
> 
> Perhaps the one that made that macro experienced a problem with at value from
> the dot product being slightly higher than one so he inserted that min()
> function to deal with it, without thinking through the problem thoroughly.
> 
> You are right there should also be a max() function there to discard values
> below -1.
> 

Thanks. What I suspected too, but I don't have near your expertise.

> Anyway the way that macro calculates the angle is not the best numerically.
> I would rather suggest something like this:
> 
> 
> #macro AngleBetweenVectors(v1, v2)
> 
>      #local v1n = vnormalize(v1);
>      #local v2n = vnormalize(v2);
> 
>      (2*atan2(vlength(v1n - v2n), vlength(v1n + v2n)))
> 
> #end // macro AngleBetweenVectors
> 

Interesting, thanks. I'll play with it. There will be four sqrt calls 
with this vs two so I expect it's a slower approach.

Reminds me I captured fast approximation atan2 code I was thinking of 
making an inbuilt function, but I've not gotten to it...

And that thought reminds me of my plans to turn many macros into 
"munctions"(1) calling new faster, inbuilt functions. Done only a tiny 
bit of that so far.

(1) - Yes, thinking about creating a munctions.inc file breaking out 
what are really macro wrappers to functions into that file to make clear 
what they are. Maybe all as M_ prefixed so my current F_dents() would 
become M_dents(). Angle perhaps then M_Angle.

> 
> I have not tested this macro yet, but the angle() function in my scikit-vectors
> Python library seems to work ok. It works in is similar way as the macro above.
> 
> https://github.com/t-o-k/scikit-vectors/blob/master/skvectors/cartesian_vectors.py
> 
> 

> "Computing Cross-Products and Rotations in 2- and 3-Dimensional Euclidian
> Spaces"
> https://people.eecs.berkeley.edu/~wkahan/MathH110/Cross.pdf
> 
> My opinion is that several of the other math related macros in the include files
> need some rework.
> 

I'd consider alternatives for my branch if you want to offer details. 
Suppose I could compare to forms in skvectors?

Something I've done too with the inbuilt function changes is allow 
alternative calculations for the same thing. Compiled such conditionals 
not that expensive. Most frequently single float options where those are 
much faster, but different methods too. For one thing, the alternatives 
I can bang against each other as a way to test!

Bill P.


Post a reply to this message

From: Tor Olav Kristensen
Subject: Re: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 8 May 2021 19:10:00
Message: <web.609719613160e21be719296489db30a9@news.povray.org>
William F Pokorny <ano### [at] anonymousorg> wrote:
> On 5/6/21 10:58 AM, Tor Olav Kristensen wrote:
> >...
> > My opinion is that several of the other math related macros in the include files
> > need some rework.
> >
>
> I'd consider alternatives for my branch if you want to offer details.

That sounds good.

Today I looked at the statistical macros in math.inc.

Here's how they could be rewritten:
(Please note that have not tested these thoroughly yet.)


// ===== 1 ======= 2 ======= 3 ======= 4 ======= 5 ======= 6 ======= 7

#version 3.8;


// Does also work for vectors
#macro Mean(Values)

   #local N = dimension_size(Values, 1);
   #local Sum = Values[0];
   #for (I, 1, N - 1)
      #local Sum = Sum + Values[I];
   #end // for

   (Sum/N)

#end // macro Mean


// Does also work for vectors
#macro Variance(Values)

    #local N = dimension_size(Values, 1);
    #local M = Mean(Values);
    #local D = Values[0] - M;
    #local SquareSum = D*D;
    #for (I, 1, N - 1)
        #local D = Values[I] - M;
        #local SquareSum = SquareSum + D*D;
    #end

    (SquareSum/N)

#end // macro Variance


#macro Minimum(Values)

   #local N = dimension_size(Values, 1);
   #local Min = Values[0];
   #for (I, 1, N - 1)
      #local Min = min(Min, Values[I]);
   #end // for

   Min

#end // macro Minimum


#macro Maximum(Values)

   #local N = dimension_size(Values, 1);
   #local Max = Values[0];
   #for (I, 1, N - 1)
      #local Max = max(Max, Values[I]);
   #end // for

   Max

#end // macro Maximum


// For population
#macro StandardDeviation(Values)

   sqrt(Variance(Values))

#end // macro StandardDeviation


#macro Statistics(Values)

    #local N = dimension_size(Values, 1);
    #local Value = Values[0];
    #local Min = Value;
    #local Max = Value;
    #local Sum = Value;
    #local SquareSum = pow(Value, 2);
    #for (I, 1,  N - 1)
        #local Value = Values[I];
        #local Min = min(Min, Value);
        #local Max = max(Max, Value);
        #local Sum = Sum + Value;
        #local SquareSum = SquareSum + pow(Value, 2);
    #end // for
    #local M = Sum/N;
    #local Var = SquareSum/N - pow(M, 2);

    dictionary {
        .Count: N,
        .Min: Min,
        .Max: Max,
        .Mean: M,
        .Var: Var,
        .StdDev: sqrt(Var)
    }

#end // macro Statistics


#macro PrintStatistics(Stats)

    #local S = array[6] { "Count", "Min", "Max", "Mean", "Var", "StdDev" };
    #debug "\n"
    #for (I, 0, 5)
        #debug concat(S[I], " = ", str(Stats[S[I]], 0, -1), "\n")
    #end // for
    #debug "\n"

#end // macro PrintStatistics


#macro Histogram(Values, NoOfBins)

   #local Stats = Statistics(Values);
   #local N = Stats.Count;
   #local Min = Stats.Min;
   #local Max = Stats.Max;
   #local BinWidth = (Max - Min)/NoOfBins;
   #local Bins = array[NoOfBins][3];
   #local BinLimitLow = Min;
   #for (I, 0, NoOfBins - 1)
      #local BinLimitHigh = BinLimitLow + BinWidth;
      #local Bins[I][0] = BinLimitLow;
      #local Bins[I][1] = BinLimitHigh;
      #local Bins[I][2] = 0; // Set count for bin to zero
      #local BinLimitLow = BinLimitHigh;
   #end // for
   #for (I, 0, N - 1)
      // Find the right bin for the value
      #local BinNo = int((Values[I] - Min)/BinWidth);
      // Make sure rounding errors do not lead to a non existent bin
      #local BinNo = min(max(0, BinNo), NoOfBins - 1);
      // Increase the count for the bin
      #local Bins[BinNo][2] = Bins[BinNo][2] + 1;
   #end // for

   Bins

#end // macro Histogram

// ===== 1 ======= 2 ======= 3 ======= 4 ======= 5 ======= 6 ======= 7


> Suppose I could compare to forms in skvectors?
>...

I'm not sure what you mean here.


I'll comment further on the math related macros in the include files later.

--
Tor Olav
http://subcube.com
https://github.com/t-o-k


Post a reply to this message

From: Tor Olav Kristensen
Subject: Re: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 9 May 2021 02:25:00
Message: <web.60977f983160e21b8e52cc8789db30a9@news.povray.org>
William F Pokorny <ano### [at] anonymousorg> wrote:
> On 5/6/21 10:58 AM, Tor Olav Kristensen wrote:
> >...
>...
> > Anyway the way that macro calculates the angle is not the best numerically.
> > I would rather suggest something like this:
> >
> >
> > #macro AngleBetweenVectors(v1, v2)
> >
> >      #local v1n = vnormalize(v1);
> >      #local v2n = vnormalize(v2);
> >
> >      (2*atan2(vlength(v1n - v2n), vlength(v1n + v2n)))
> >
> > #end // macro AngleBetweenVectors
> >
>
> Interesting, thanks. I'll play with it. There will be four sqrt calls
> with this vs two so I expect it's a slower approach.

I prefer better accuracy before speed. If we need this to go faster we could
compile it into POV-Ray and make it a new command; e.g. vangle().


> Reminds me I captured fast approximation atan2 code I was thinking of
> making an inbuilt function, but I've not gotten to it...
>
> And that thought reminds me of my plans to turn many macros into
> "munctions"(1) calling new faster, inbuilt functions. Done only a tiny
> bit of that so far.
>
> (1) - Yes, thinking about creating a munctions.inc file breaking out
> what are really macro wrappers to functions into that file to make clear
> what they are. Maybe all as M_ prefixed so my current F_dents() would
> become M_dents(). Angle perhaps then M_Angle.

This sounds interesting. Can you please explain more ?
Are there any benefits from using these instead of new built in commands ?


> Something I've done too with the inbuilt function changes is allow
> alternative calculations for the same thing. Compiled such conditionals
> not that expensive. Most frequently single float options where those are
> much faster, but different methods too. For one thing, the alternatives
> I can bang against each other as a way to test!

Are you using this functionality to perform speed tests ?


--
Tor Olav
http://subcube.com
https://github.com/t-o-k


Post a reply to this message

From: William F Pokorny
Subject: Re: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 9 May 2021 09:22:16
Message: <6097e208@news.povray.org>
On 5/8/21 7:06 PM, Tor Olav Kristensen wrote:
> William F Pokorny <ano### [at] anonymousorg> wrote:
>> On 5/6/21 10:58 AM, Tor Olav Kristensen wrote:
>>> ...
>>> My opinion is that several of the other math related macros in the include files
>>> need some rework.
>>>
>>
>> I'd consider alternatives for my branch if you want to offer details.
> 
> That sounds good.
> 
> Today I looked at the statistical macros in math.inc.
> 
> Here's how they could be rewritten:
> (Please note that have not tested these thoroughly yet.)
> 

Thank you. I'll have a look.

Aside: I've been working on adding self testing to all the includes 
(Ingo's suggestion and method) and in addition to macros I broke myself 
with povr branch changes, Histogram, was a macro long shipped which 
already wasn't working... ;-)

> 
> 
>> Suppose I could compare to forms in skvectors?
>> ...
> 
> I'm not sure what you mean here.

I was guessing what you have in your package would be what you consider 
solid/better implementations numerically. However, it helps me if you do 
the sorting of what might be better implementations of current code.

> 
> 

For reference I'm shipping only the following includes as 'core' ones in 
the povr branch.

arrays.inc
functions.inc
math.inc
rand.inc
shapes.inc
strings.inc
transforms.inc

Limit your look to these files for my participation. Further, I have on 
my list to move some of the macros in these to munctions.inc or other 
include files as 'not core' related / not commonly used.

arraycoupleddf3s.inc - This temporarily needed as it goes with some DF3 
related work I did years back which is in the povr branch. Some of it 
should end up in arrays.inc; some of it should go away. I've not sorted it.

Aside: My belief is future POV-Ray ray needs to move to a support 
structure which breaks things into core-code, scenes, includes, 
documentation, ini/configs, helper scripts / programs, (fonts?) and the 
web site (also the wiki?) as independent code control structures on 
github say ideally all with different primarys though perhaps still 
common POV-Ray owners like Chris. I think it would work better having 
multiple gate keepers to multiple POV-Ray domains. This model also 
matches to a degree what linux package developers are already going 
themselves breaking POV-Ray into multiple packages.

Bill P.


Post a reply to this message

From: William F Pokorny
Subject: Re: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 9 May 2021 09:31:42
Message: <6097e43e$1@news.povray.org>
On 5/9/21 2:22 AM, Tor Olav Kristensen wrote:
> William F Pokorny <ano### [at] anonymousorg> wrote:
>> On 5/6/21 10:58 AM, Tor Olav Kristensen wrote:
>>> ...
>> ...
>>> Anyway the way that macro calculates the angle is not the best numerically.
>>> I would rather suggest something like this:
>>>
>>>
>>> #macro AngleBetweenVectors(v1, v2)
>>>
>>>       #local v1n = vnormalize(v1);
>>>       #local v2n = vnormalize(v2);
>>>
>>>       (2*atan2(vlength(v1n - v2n), vlength(v1n + v2n)))
>>>
>>> #end // macro AngleBetweenVectors
>>>
>>
>> Interesting, thanks. I'll play with it. There will be four sqrt calls
>> with this vs two so I expect it's a slower approach.
> 
> I prefer better accuracy before speed. If we need this to go faster we could
> compile it into POV-Ray and make it a new command; e.g. vangle().
> 
> 
>> Reminds me I captured fast approximation atan2 code I was thinking of
>> making an inbuilt function, but I've not gotten to it...
>>
>> And that thought reminds me of my plans to turn many macros into
>> "munctions"(1) calling new faster, inbuilt functions. Done only a tiny
>> bit of that so far.
>>
>> (1) - Yes, thinking about creating a munctions.inc file breaking out
>> what are really macro wrappers to functions into that file to make clear
>> what they are. Maybe all as M_ prefixed so my current F_dents() would
>> become M_dents(). Angle perhaps then M_Angle.
> 
> This sounds interesting. Can you please explain more ?

Unsure how much you've been following my povr branch posts...

For starters, over the last year or two I've replaced most of the 
existing 'odd shape' inbuilt functions for which functions.inc defines 
the interface and documents the usage with more generic ones. I've also 
extended and cleaned up the function<->pattern interface as related 
work. New function related wave modifiers for example.

On the idea on munctions.
-------------------------
One old macro always a "munction" is the Supertorus in shapes.inc but in 
the povr branch it now calls a new inbuilt function f_supertorus() for 
backward compatibility. New users should call f_supertorus() directly.

---
We've long had simple "munctions" that look like inbuilt capability such as:

#declare clip = function (x,y,z) {min(z,max(x,y))}

which in povr I changed to

#declare F_clip = function (x,y,z) {min(z,max(x,y))}

and I'm leaning now toward calling it M_clip because I want to make 
clear it is today a munction and not an inbuilt 'clip' keyword.

Longer term F_clip/M_Clip is educational in being simple, and it will be 
left as is on the creation of a proper 'clip' implementation to create 
inbuilt include test cases comparing M_clip results to 'clip' ones.

> Are there any benefits from using these instead of new built in commands ?

Sometimes. What munctions can today do not at all doable with inbuilts 
of the straight keyword kind, or the inbuilt functions, is pre-condition 
a larger set of stuff. Stuff being data / partial solutions - or the 
particular configuration and inter operation of a collection of 
functions(1).

When you say inbuilt command, there are flavors. For performance inbuilt 
is 'almost' - but not always better.

I've gotten somewhat comfortable adding inbuilt functions, patterns and 
shapes and the associated keywords.

When you talk a something like 'acos' there is an implementation in the 
parser and there is a parallel implementation in the vm to create/run 
the acos compiled code. Not all things in the parser are in the vm and 
visa versa. Things only in the parser cannot as a rule be used with the 
vm. Where the vm things mostly (all?) work during parsing.

Though 'acos' like implementations might often be best, I don't 
understand the code well enough to pull these sorts of dual keywords at 
the moment. In any case, I'd probably go after only a few things in this 
way such as: the 'munctions' even, odd, max3, min3, sind,... (Adding the 
constant 'e' is another though that I think I can work through today - 
just not done).

I can implement inbuilt functions helping simplify/speed or replace many 
effective 'munctions.' I've been working largely in this direction of late.

---
(1) Aside: I want to get to the point where we can tell whether a 
function is being run from the parser (single thread) or during 
rendering so we can precondition functions too. I have some ideas/notes 
around thread ids and such, but I've not worked out how to really do it.

Even today in f_ridged_mf() there is an allocation of memory which I 
believe should be done only by pre-conditioning during parsing(1a). 
Function equivalents of ripples, waves, wood, etc need a parse time 
preconditioning / set up capability too, to best work.

(1a) Depending on how threaded rendering work hits f_ridged_mf() today, 
I suspect we are sometimes getting minor memory leaks. In this case to 
with respect to functionality / result I think it harmless.

> 
> 
>> Something I've done too with the inbuilt function changes is allow
>> alternative calculations for the same thing. Compiled such conditionals
>> not that expensive. Most frequently single float options where those are
>> much faster, but different methods too. For one thing, the alternatives
>> I can bang against each other as a way to test!
> 
> Are you using this functionality to perform speed tests ?
> 

Sometimes, yes.

Aside: I've had several false starts looking at integrating the work you 
and I beleive more Alain Ducharme did years ago with quaternions.inc - 
as inbuilt capability of some kind, but never gotten very far with the 
effort. Always so much on that todo list - and I'm not that smart, so it 
goes slowly. :-)

Bill P.


Post a reply to this message

From: William F Pokorny
Subject: Re: Why only a +1 clamp with VAngle, VAngleD macros in math.inc?
Date: 9 May 2021 10:04:28
Message: <6097ebec$1@news.povray.org>
On 5/9/21 9:31 AM, William F Pokorny wrote:
> Are there any benefits from using these instead of new built in commands ?

Dang..., one thing I was going to mention related to the 'munctions' 
concept and forgot.

I'm about 95% sure truly macro wrapped functions don't get 'compiled' by 
the vm until the macro is called. So, my thinking is there is a parse 
time reason to create 'macro wrapped' functions over pure function 
definitions of more complex math in our includes...

Not proven as in I haven't yet added throws to the vm code and tried 
functions defined both ways.

Bill P.


Post a reply to this message

Goto Latest 10 Messages Next 5 Messages >>>

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.