POV-Ray : Newsgroups : povray.advanced-users : isosurface: Does the order of multiple functions matter? : Re: isosurface: Does the order of multiple functions matter? Server Time
25 Apr 2024 18:08:36 EDT (-0400)
  Re: isosurface: Does the order of multiple functions matter?  
From: Bald Eagle
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

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