POV-Ray : Newsgroups : povray.advanced-users : isosurface: Does the order of multiple functions matter? Server Time
16 Jun 2024 13:30:55 EDT (-0400)
  isosurface: Does the order of multiple functions matter? (Message 11 to 20 of 28)  
<<< Previous 10 Messages Goto Latest 10 Messages Next 8 Messages >>>
From: Cousin Ricky
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 28 Jan 2023 12:40:02
Message: <63d55df2$1@news.povray.org>
On 2023-01-28 06:25 (-4), Kenneth wrote:
> 
> isosurface{
> function{A + B + C + D}
> ....
> }
> 
> Only four, thankfully! :-)
> 
> Does the order of the functions matter when POV-ray evaluates them? Or are they
> evaluated 'all at once' regardless of the order?

<soapbox>
It is dangerous programming practice to rely on order of evaluation,
unless the language definition explicitly specifies it.  POV-Ray has no
such specification.
</soapbox>

In your context, it does not appear that the order makes any difference,
because functions in POV-Ray cannot have side effects.  Of course,
liberal use of parentheses will guarantee *precedence* of evaluation,
but this has limited effect on the *order* of evaluation.

> (BTW: #if and #switch/#case do work well in the body of the isosurface, within
> the main function, to at least eliminate certain parts. Probably not good code
> practice, but it causes no problems AFAIK.)

Directives (#if, #switch, etc.) in functions are parsed only once, when
the function is defined.  Directives in functions that are coded in-line
in an isosurface will be observed each time the object is parsed, but
directives in any pre-defined functions used in the isosurface will not
be re-parsed.


Post a reply to this message

From: Bald Eagle
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 28 Jan 2023 13:25:00
Message: <web.63d567619b90547b1f9dae3025979125@news.povray.org>
Cousin Ricky <ric### [at] yahoocom> wrote:
> On 2023-01-28 06:25 (-4), Kenneth wrote:

> > (BTW: #if and #switch/#case do work well in the body of the isosurface, within
> > the main function, to at least eliminate certain parts. Probably not good code
> > practice, but it causes no problems AFAIK.)
>
> Directives (#if, #switch, etc.) in functions are parsed only once, when
> the function is defined.  Directives in functions that are coded in-line
> in an isosurface will be observed each time the object is parsed, but
> directives in any pre-defined functions used in the isosurface will not
> be re-parsed.

Say what now?

I missed this in the first skim-through.

I'll be trying this out at some point - but I'd like to see a working code
example! (Considering that I had no idea this was possible)


Post a reply to this message

From: Kenneth
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 28 Jan 2023 22:20:00
Message: <web.63d5e5729b90547b9b4924336e066e29@news.povray.org>
"Bald Eagle" <cre### [at] netscapenet> wrote:
>
> The other thing that helps to work this all out in your head is to not delete
> code that doesn't work.   Otherwise you wind up coding the same mistakes over
> and over again.

Absolutely! My current real scene code is full of commented-out sections that
failed, along with notes as to why (well, at least at my current level of
understanding, ha.) Unfortunately, much of my 'knowledge' is currently spread
through hundreds of my previous files, so I have to think hard as to *where* I
might have put a particularly useful note.

Your own method of adding accumulated tidbits of knowledge into a spreadsheet--
like a 'library' of known facts-- is the envy of the modern world. ;-)  Alas, I
have not gotten around to implementing that major 'tuit.
>
> Do a #switch #case #break #end block, and copy-paste #case 1 into #case 2 and
> then edit case 2.  If that fails, work on case 3.   That way you have
> a running documentation of what you've tried, what the results look like,
> notes on why it failed, what worked, etc... What doesn't work is often more
> important to know than what does.

Exactly, Northern Cousin Walker. Excellent advice.

> If you have A which needs X, and B which needs Y, and C which needs Z,
> and D which needs W, then if you have A(B(C(D()))), your function A needs:  A
> (X, Y, Z, W) {}, and so on, as you go further down into the curly braces.

This is the concept that I have the most trouble understanding...although I'm
making slow progress.

My apologies for going a bit off-topic here, but I was recently going through
someone else's old code that uses this idea brilliantly-- it's very complex,
with nested functions-- and I *think* I came across a particular section that I
don't fully understand.

Using your example: When A(X,Y,Z,W){} is the major function, I would imagine
that *all* of these variables (arguments?) in the body of the function would, at
some point in the code, have to be replaced with some actual value or other
pre-#declared variable. In almost all of the instances of X,Y,Z and W, that is
so. But in one section, there appears to be just W-- used as-is and not with any
substitution. (Sorry if I'm not describing this understandably.) AFAICT, there
is nowhere in the entire scene code that has *something* pre-declared for W. So
in this case, what does that 'naked' W represent? The code *works* of course--
and I've tried substituting actual float values for that one mysterious instance
of W, in my usual fumbling trial-and-error way...but with always-fatal results.

If indeed that one W is 'naked' so to speak, and I haven't made a stupid error
in detecting a substitution for it, then how does it work successfully? If I
could figure *that* out, it would certainly expand my understanding of POV-ray's
function use.


Post a reply to this message

From: Kenneth
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 28 Jan 2023 23:00:00
Message: <web.63d5ee319b90547b9b4924336e066e29@news.povray.org>
"Kenneth" <kdw### [at] gmailcom> wrote:
> "Bald Eagle" <cre### [at] netscapenet> wrote:
>
> > If you have A which needs X, and B which needs Y, and C which needs Z,
> > and D which needs W, then if you have A(B(C(D()))), your
> > function A needs:  A(X, Y, Z, W) {}, and so on, as you go further down
> > into the curly braces.
>
> My apologies for going a bit off-topic here, but I was recently going through
> someone else's old code that uses this idea brilliantly-- it's very complex,
> with nested functions-- and I *think* I came across a particular section
> that I don't fully understand.
>

To be specific, it's in William Pokorny's "ObjectAslso" file from Sept. 19 2009,
if you would like to take a look (no pressure, of course.) In his file called
"ObjectAslso.inc", there is a function argument called 'S'.  I have a hard time
following the code because of its complexity, but I can't see where one (or
more) of the instances of that argument are assigned a value of any kind. I
could be wrong *of course*; maybe I'm just not seeing the forest for the trees--
or vice-versa!

https://news.povray.org/povray.binaries.scene-files/thread/%3Cweb.4aae23151285f23f15ee2c550%40news.povray.org%3E/


Post a reply to this message

From: Kenneth
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 28 Jan 2023 23:50:00
Message: <web.63d5fa0c9b90547b9b4924336e066e29@news.povray.org>
Cousin Ricky <ric### [at] yahoocom> wrote:
>
> <soapbox>
> It is dangerous programming practice to rely on order of evaluation,
> unless the language definition explicitly specifies it.  POV-Ray has no
> such specification.
> </soapbox>
>
> In your context, it does not appear that the order makes any difference,
> because functions in POV-Ray cannot have side effects.  Of course,
> liberal use of parentheses will guarantee *precedence* of evaluation,
> but this has limited effect on the *order* of evaluation.
>

I can't say that I really understand those distinctions, conceptually. In any
case, my attempts so far at sprinkling parentheses "here, there, and eveywhere"
in my simplistic example code has had no effect; the order of the isosurface's
inner functions still shows no visible difference when changed around.


Post a reply to this message

From: Kenneth
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 29 Jan 2023 01:30:00
Message: <web.63d6115f9b90547b9b4924336e066e29@news.povray.org>
"Bald Eagle" <cre### [at] netscapenet> wrote:
>
> The other thing that helps to work this all out in your head is to not delete
> code that doesn't work.   Otherwise you wind up coding the same mistakes over
> and over again.
>
>..That way you have a running documentation of what you've tried, ..., why it
> failed, what worked, etc. Heck, post it here, so
> that people struggling with the same issues can follow along and really
> see how one goes from fail to success...

Well, since you mentioned it... :-)

[more off-topic stuff, sorry!]
In my current real scene, I'm using the 'object pattern' to make an isosurface
shape, necessarily using functions. Here is a somewhat simplified version of a
problem I had:

#declare OBJ_PATTERN_FUNC = function{pigment{object{MY_OBJECT rgb 0 rgb 1}}}
#declare PIG_DISTORTION_FUNC = function{pigment{bumps scale .2}}
#declare MAG = 1.5; // the magnitude of the bumps distortion to be added

This is how I originally wrote my main function:

isosurface{function{
1 - (OBJ_PATTERN_FUNC(
x + MAG*PIG_DISTORTION_FUNC (x,y,z).x - .5*MAG,
y + MAG*PIG_DISTORTION_FUNC (x,y,z).y - .5*MAG,
z + MAG*PIG_DISTORTION_FUNC (x,y,z).z - .5*MAG
     ).gray)
     } ... }

This worked-- but the resulting bumps distortion had a spatial 'skew' aligned
along <1,1,1>, which *finally* became understandable because I was using full
(x,y,z) in all 3 inner functions. (I had to do that to solve other problems that
I ran into, up to this point.) But substituting 0.0 for the appropriate
'un-needed' x,y,z arguments resulted in *very* strange isosurface effects.

So I came up with this solution, which nicely fixed the skew--by subtracting the
'offending' arguments from 1.0. But I have NO idea why it works. I finally
arrived at it through pure chance and desperation:

isosurface{function{
1 - (OBJ_PATTERN_FUNC(
x + MAG*PIG_DISTORTION_FUNC (x,1-y,1-z).x - .5*MAG,
y + MAG*PIG_DISTORTION_FUNC (1-x,y,1-z).y - .5*MAG,
z + MAG*PIG_DISTORTION_FUNC (1-x,1-y,z).z - .5*MAG
     ).gray)
     } ...}

Maybe this is a special-case solution because of the object pattern scheme? I
don't know.


Post a reply to this message

From: Thomas de Groot
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 29 Jan 2023 02:21:20
Message: <63d61e70$1@news.povray.org>
Op 28/01/2023 om 19:20 schreef Bald Eagle:
> Cousin Ricky <ric### [at] yahoocom> wrote:
>> On 2023-01-28 06:25 (-4), Kenneth wrote:
> 
>>> (BTW: #if and #switch/#case do work well in the body of the isosurface, within
>>> the main function, to at least eliminate certain parts. Probably not good code
>>> practice, but it causes no problems AFAIK.)
>>
>> Directives (#if, #switch, etc.) in functions are parsed only once, when
>> the function is defined.  Directives in functions that are coded in-line
>> in an isosurface will be observed each time the object is parsed, but
>> directives in any pre-defined functions used in the isosurface will not
>> be re-parsed.
> 
> Say what now?
> 
> I missed this in the first skim-through.
> 
> I'll be trying this out at some point - but I'd like to see a working code
> example! (Considering that I had no idea this was possible)
> 

Hmmmmm.... this makes sense, somehow, but I guess this behaviour could 
produce unexpected results, with no clue of /where/ the diverging 
behaviour would come from (if unaware of this behaviour).

-- 
Thomas


Post a reply to this message

From: Tor Olav Kristensen
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 29 Jan 2023 08:20:00
Message: <web.63d671f79b90547b383c879289db30a9@news.povray.org>
"Kenneth" <kdw### [at] gmailcom> wrote:
>...
> [more off-topic stuff, sorry!]
> In my current real scene, I'm using the 'object pattern' to make an isosurface
> shape, necessarily using functions. Here is a somewhat simplified version of a
> problem I had:
>...
> #declare OBJ_PATTERN_FUNC = function{pigment{object{MY_OBJECT rgb 0 rgb 1}}}
> #declare PIG_DISTORTION_FUNC = function{pigment{bumps scale .2}}
> #declare MAG = 1.5; // the magnitude of the bumps distortion to be added
>
> This is how I originally wrote my main function:
>
> isosurface{function{
> 1 - (OBJ_PATTERN_FUNC(
> x + MAG*PIG_DISTORTION_FUNC (x,y,z).x - .5*MAG,
> y + MAG*PIG_DISTORTION_FUNC (x,y,z).y - .5*MAG,
> z + MAG*PIG_DISTORTION_FUNC (x,y,z).z - .5*MAG
>      ).gray)
>      } ... }
>
> This worked-- but the resulting bumps distortion had a spatial 'skew' aligned
> along <1,1,1>, which *finally* became understandable because I was using full
> (x,y,z) in all 3 inner functions. (I had to do that to solve other problems that
> I ran into, up to this point.) But substituting 0.0 for the appropriate
> 'un-needed' x,y,z arguments resulted in *very* strange isosurface effects.
>
> So I came up with this solution, which nicely fixed the skew--by subtracting the
> 'offending' arguments from 1.0. But I have NO idea why it works. I finally
> arrived at it through pure chance and desperation:
>
> isosurface{function{
> 1 - (OBJ_PATTERN_FUNC(
> x + MAG*PIG_DISTORTION_FUNC (x,1-y,1-z).x - .5*MAG,
> y + MAG*PIG_DISTORTION_FUNC (1-x,y,1-z).y - .5*MAG,
> z + MAG*PIG_DISTORTION_FUNC (1-x,1-y,z).z - .5*MAG
>      ).gray)
>      } ...}
>
> Maybe this is a special-case solution because of the object pattern scheme? I
> don't know.

I recommend that you use pattern functions here instead of pigment
functions. That is because the pigments you use here always has the
same values for the red, green and blue components (extracted with
...red .green .blue or .x .y .z) The gray component will then also
have the same value as the red, green and blue components.

You can then write your declared functions like this:

#declare OBJECT_FUNC =
    function {
        pattern {
            object { MY_OBJECT }
        }
    }
;
#declare DISTORTION_FUNC =
    function {
        pattern {
            bumps
            scale <0.2, 0.2, 0.2>
        }
    }
;

I suspect that the reason that the "skew" disappears when using your
"desperation" code is that there you are "sampling" the
PIG_DISTORTION_FUNC at different locations in 3D space for each of
the red, green and blue components (.x .y .z), This causes the 3
components to have different values. When you set some of the
arguments to the PIG_DISTORTION_FUNC function to zero, you where
either "sampling" this function in a plane, along a line or at a point.

With pattern functions you can rewrite your isosurface function like
this:

function {
    1 -
    OBJECT_FUNC(
        x + MAG*(DISTORTION_FUNC(    x, 1 - y, 1 - z) - 0.5),
        y + MAG*(DISTORTION_FUNC(1 - x,     y, 1 - z) - 0.5),
        z + MAG*(DISTORTION_FUNC(1 - x, 1 - y,     z) - 0.5)
    )
}

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


Post a reply to this message

From: Bald Eagle
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 29 Jan 2023 10:00:00
Message: <web.63d6896c9b90547b1f9dae3025979125@news.povray.org>
"Kenneth" <kdw### [at] gmailcom> wrote:

> Using your example: When A(X,Y,Z,W){} is the major function, I would imagine
> that *all* of these variables (arguments?) in the body of the function would, at
> some point in the code, have to be replaced with some actual value or other
> pre-#declared variable. In almost all of the instances of X,Y,Z and W, that is
> so. But in one section, there appears to be just W-- used as-is and not with any
> substitution. (Sorry if I'm not describing this understandably.) AFAICT, there
> is nowhere in the entire scene code that has *something* pre-declared for W. So
> in this case, what does that 'naked' W represent? The code *works* of course--
> and I've tried substituting actual float values for that one mysterious instance
> of W, in my usual fumbling trial-and-error way...but with always-fatal results.
>
> If indeed that one W is 'naked' so to speak, and I haven't made a stupid error
> in detecting a substitution for it, then how does it work successfully? If I
> could figure *that* out, it would certainly expand my understanding of POV-ray's
> function use.

"at some point in the code, have to be replaced with some actual value or other
pre-#declared variable"

Well, yes and no.
Absolutely NOT, first of all, because if _somewhere else_ in the code, PRIOR to
defining the function, you declare one of those identifiers to be a value, then
POV-Ray will give you an error.

#declare N = 3;
#declare Q = function (N) {N}

Parse Error: Expected 'parameter identifier', float function 'float identifier'
found instead

Whereas

#declare N = 3;
#declare Q = function (Placeholder) {N*3}
#declare Res = Q (0);
#debug concat ("Res = ", str (Res, 0, 3), "\n")

Works just fine.
The Placeholder argument is there just because declaring a function like
#declare Q = function {N*3}
automatically defines it to be dependent on (x, y, z), and I don't know how to
work around that ATM.

So, now we get to the most practical issue, IMO - using function in things like
pigments and isosurfaces.

Let's say that I want to make an onion pattern.  That would simply be the
distance from the origin.
#declare Onion = function {sqrt (x*x + y*y + z*z)}

And that would likely work fine.

But now let's say that you wanted to evaluate that function and send a result to
the #debug stream.

You can't just plug in x, y, or z, because outside of the function those are
vectors, and functions operate on scalar inputs.

But what you CAN do is rewrite your function like this:

#declare Onion = function (X, Y, Z) {sqrt (X*X + Y*Y + Z*Z)}

and it will work in EITHER/BOTH case(s).   Because then X will take on the
immediate scalar value of x when POV-Ray is going through rendering each pixel,
and passing that x value into the function for each pixel evaluation.
AND you can also write a loop where you can pass in your own values for X so
that you can evaluate and graph the function.

It's easy to lose track, get confused, and just start coding on autopilot in the
wrong paradigm, namespace, macro/function syntax.

I make these mistakes, and Heck, even Cousin Ricky makes these mistakes:

https://news.povray.org/povray.newusers/thread/%3C5ef11205%40news.povray.org%3E/



"Yes", because those function arguments get 'replaced' (I'd say assigned) at the
instant that you make the function call from somewhere else.  Look atsomewhere
that you're invoking the function, and you are assigning some concrete value to
be passed into it.

In some ways, it functions exactly like you understand macros to work.

So let's say you wrote a macro like:

#macro PerformTaskRegarding (Kenneth)
     ExplainCodeTo (Kenneth)
#end

Then you can invoke the macro code using:

PerformTaskRegarding (TdG) or PerformTaskRegarding (jr) or PerformTaskRegarding
(Bill) or EVEN PerformTaskRegarding (Kenneth)

The "Kenneth" written in the #macro definition is a phantom Kenneth that gets
created when the macro is invoked, assigned whatever value you put in
parentheses when you call the macro from later in code, and then gets undefined
when the macro exits.
So in your head, you can imagine that you're doing something like
PerformTaskRegarding (#local Kenneth = Bill)
And then I become your Evil Twin for the duration of the macro operations, or
vice-versa however you like to see it ;) .

With a macro, you can even masquerade as yourself, because the "Kenneth" in the
main code and the "Kenneth" in the macro block are in two different namespaces.
One acts as a proxy for the other.

Now for functions, this is similar, but more limited, as I explained above,
regarding a function parameter being declared as a float identifier prior to
declaring the function.   That's either a bug, or the function parser "has a
bug", or it's working exactly as it was designed to, depending on who you ask.

Now, every time you invoke a function, the things in parentheses take on new,
temporary values, until you hit that closing } and the function is fully
evaluated.

You can still use named float identifiers in your function.
The sort of "dumb" way to do it is the way I described above, but the more
useful way is when you want to use a function with a float identifier, and for
whatever reason you want to use that in the function's terms rather than passing
it in as an argument.  Then you can write a macro that makes your function using
that float identifier.  But why this is useful is because you #undef the
function before you #declare it, so that for every macro call, you get a fresh
function with whatever value that "static" float identifier holds, and then you
can use it in a loop.
This is the kind of thing TOK taught me to do with control points for Bezier
splines and surfaces.  The control points are "static" for any given spline or
patch, but if I wanted to animate it, then I'd be changing the control points,
and I'd want an automatic way to re-write my function(s).

And finally (I think), nested functions.

Let's say that you have three functions.

#declare Function1 = function (A) {A*2}
#declare Function2 = function (B) {B/2}
#declare Function3 = function (A, B, C) {pow (Function1 (A) + Function2 (B), C)}

Function1 and Function2 are easy to understand - you just plug some value or
float identifier into the function call and whatever value A takes on gets
processed according to what's inside the {} curly brackets.

Function3 is a nested function.  It uses values returned by those functions to
calculate its own result.   And so even though Function3 only uses C in its own
calculation, it needs to pass on both A and B to its nested functions so that
they have their required parameter identifiers to do THEIR calculations with.

Now, you can _pass-in_ float identifiers, numerical scalar values, render-time
scalar values like x, y, and z, but the key is that those values get assigned to
the parameter names in the _parentheses of the function_.  When that function
then evaluates what's in the curly braces, it needs to know which of the
parameters is being used where, and most importantly, what gets passed _through_
to the nested functions.

I hope that is clear enough and helps.

- Walker, New Hampshire Ranger


Post a reply to this message

From: Bald Eagle
Subject: Re: isosurface: Does the order of multiple functions matter?
Date: 29 Jan 2023 11:20:00
Message: <web.63d69bff9b90547b1f9dae3025979125@news.povray.org>
"Bald Eagle" <cre### [at] netscapenet> wrote:

> And then I become your Evil Twin for the duration of the macro operations, or
> vice-versa however you like to see it ;) .

OK. I admit it.  I'm your Evil Twin.  But oh so happy to be so.  :D


Post a reply to this message


Attachments:
Download 'photo_2023-01-28_17-39-04.jpg' (41 KB)

Preview of image 'photo_2023-01-28_17-39-04.jpg'
photo_2023-01-28_17-39-04.jpg


 

<<< Previous 10 Messages Goto Latest 10 Messages Next 8 Messages >>>

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