A Tutorial on Krpano Action Arguments (pt 1)

  • Hi ,
    i try to understand this for a while but i get crazy with tha %1 etc thing..
    what does that exactely mean?..

    Hi Tuur,

    Ah. I'd like to have an argument, please -- Michael Palin, The Argument Clinic

    As you know krpano actions are of the form:

    Code
    <action name="action_name">
    	body
    </action>


    where body is the action code.

    The actions may be called in the following manner:

    Code
    action_name(arg1,arg2,arg3,...,argN);

    or

    Code
    action(action_name,arg1,arg2,arg3,...,argN);


    where N is 99 or less. In other words, you may have up to 99 arguments (krpano versions less than 1.0.8.10 supported only 9 arguments).

    krpano actions allow you to access arguments from within the body of your code via the %N reference, where N is a number from 1 to 99. %0 may be used to reference the name of the action from within the action's body.

    In other words:

    • %0 is the name of the action that is being called
    • %1 is the first argument passed to that function, null if no first argument
    • %2 is the second argument passed to that action, null if no second argument

    ....

    • %9 is the ninth argument passed to that action, null if no ninth argument

    For the new version 1.0.8.10 of krpano you can now have up to 99 arguments.

    • %10 is the tenth argument passed to that action, null if no tenth argument
    • %11 is the tenth argument passed to that action, null if no eleventh argument

    ....

    First, a simple example:


    produces:

    Quote

    INFO: hello world
    INFO: hello kitty
    INFO: hello Klaus
    INFO: hello null


    This code snippet includes two actions: hello which prints the string hello followed by whatever the user passes in as the first argument, and main which calls the hello function four times with different arguments. Note that the fourth call passes no arguments: hello();. This produces the output INFO: hello null.

    ARGUMENT SUBSTITUTION
    Krpano's action language is different from other programming languages in that the arguments do not become local variables within the code body, rather arguments are substituted into the code prior to the execution of the code body. You can think of it as if you are using a text editor on the body of the code, and doing a search and replace for each argument, replacing all occurrences of %1 with the string that is the first argument, replacing all occurrences of %2 with the string that is the second argument, etc. If no argument has been provided for a given %N, then a global replace of null is performed for that particular %N.

    This substitution occurs after the action is called, but before the action is executed. Consider this example:


    In the main action, the first call to test is test(abc,def,123,456);. When this line of the program is reached, then the arguments are substituted into the body of test. That is, before the test action is executed,

    • the name of the action "test" replaces all instances of %0
    • the first argument "abc" replaces all instances of %1
    • the second argument "def" replaces all instances of %2
    • the third argument "123" replaces all instances of %3
    • and the fourth argument"456" replaces all instances of %4

    Thus the original test body code

    Code
    trace("action ",%0," arg1 is ",%1,", arg2 is ",%2,", args 3 and 4 are %3%4");

    becomes

    Code
    trace("action ",test," arg1 is ",abc,", arg2 is ",def,", args 3 and 4 are 123456");

    . After the substitution, the body is executed and produces the output:

    Quote

    action test arg1 is abc, arg2 is def, args 3 and 4 are 123456

    .
    We then proceed to the next line in main where test is called again with different arguments: test(krpano,is,#,1). Here the action name "test" replaces %0, "krpano" replaces %1, "is" replaces %2, "#" replaces %3, and "1" replaces %4. Thus, before execution, the code body

    Code
    trace("action ",%0," arg1 is ",%1,", arg2 is ",%2,", args 3 and 4 are %3%4");

    is converted to

    Code
    trace("action ",test," arg1 is ",krpano,", arg2 is ",is,", args 3 and 4 are #1");

    Once executed, this produces the output

    Quote

    action test arg1 is krpano, arg2 is is, args 3 and 4 are #1

    One interesting thing to note, is that in this example, the replacement of arguments %3 and %4 occur within a quoted string. Krpano's handles argument through a simple substitution mechanism, without regard for syntax. The resulting code of course must be syntactically correct, but substitutions can occur within quoted strings, in or out of argument lists, in function names, really anywhere within the body of the code. In fact, entire code segments may be passed through the argument list as in the following example.

    Suppose we want to add debugging code which is only executed when the global variable debug is set to true. We can create such an action, let's call it dbg, through which we can pass a code fragment that can be conditionally executed:


    When this is executed, what happens? The only output produced is:

    Quote

    INFO: a is 2000

    If we walk step by step through the main code, first we see the variable a is set to the value 123 and then the variable b is set to the value 345. The dbg action is then called with the argument

    Code
    trace("hello world");
    trace("a is ",get(a)," b is ",get(b));


    This multi-line, multi command argument (complete with quoted strings) get's substituted into every %1 found in the body of dbg. Thus

    Code
    if(debug == true, %1);

    becomes

    Code
    if(debug == true,
    	trace("hello world");
    	trace("a is ",get(a)," b is ",get(b));
    );


    When this is executed, the variable debug has not been set, so the if test fails and the traces do not get executed.

    The next line in main sets the variable debug to true. Then the variable a is changed to 2000 and b is changed to 1000. The dbg action is called with the following argument:

    Code
    if (a GT b,
    	trace("a is ",a);
    <!--else-->,
    	trace("b is ",b);
    );


    This is substituted into the dbg function wherever there is a %1, thus

    Code
    if(debug == true, %1);

    is converted to

    Code
    if(debug == true,
    	if (a GT b,
    		trace("a is ",a);
    	<!--else-->,
    		trace("b is ",b);
    	);
    );


    This code now executes, and since debug has previously been set to true the nested if statement is executed where a is compared to b and is found to be greater resulting in the trace("a is ",a) code being executed which produces the (only) output a is 2000.

    ...more in Part 2

    Edited once, last by pinsane (May 22, 2010 at 8:15 AM).

  • ...continued from Part 1

    USING VARIABLES AS ARGUMENTS

    To use a variable's value as an argument, you must extract the value using the get() command. The get() command is one of the few krpano commands that returns a value, and it can only be used as an argument or (for version 1.0.8.10) in an index array.

    Here's an example of using a variable as an argument:


    this produces the output

    Quote


    INFO: called test(a,b,c);
    INFO: called test(1,2,3);
    INFO: called test(100,200,300);
    INFO: called test(d,null,null);


    In the main action, we first set a to 1, b to 2, and c to 3. We then call test(a,b,c) which prints called test(a,b,c). Note that we did not use the get function, so the value of the variables are not passed to the test action. In this case, we pass the strings "a" "b" and "c" rather than their values 1 2 and 3.

    In the second call, test(get(a),get(b),get(c)); we dereference the variables a,b and c using the get() command. Thus in this case the values of the variables are passed through the arguments and the output is called test(1,2,3). It is important to note that the get function dereferences the variables before the argument substitutions are made. So the call

    Code
    test(get(a),get(b),get(c));

    first gets converted to

    Code
    test(1,2,3);

    next the argument substitions are made to the body of test causing

    Code
    trace("called %0(%1,%2,%3)");

    to be converted to

    Code
    trace("called test(1,2,3)");

    and lastly the body is executed producing the output

    Quote

    INFO: called test(1,2,3);


    In the third call, test(100,200,300);, we pass the scalar values 100, 200 and 300 as arguments and we get the predictable result called test(100,200,300).

    In the fourth call, test(d,get(d)); we pass only two arguments. This means the third argument will be passed as null. The first argument d is passed as the string "d". The second argument is get(d). Since d is not a defined variable, this gets passed as null. So the output is called test(d,null,null).

    RETURNING VALUES THROUGH ARGUMENT VARIABLES

    krpano actions do not return values via a return(value); call. As such, the only way to pass values computed within an action back to the calling action is either through global variables (krpano does not support local variables) or "by reference" (through variables whose names are provided as arguments). While global variables would work, the use of globals is considered a bad programming practice and can lead to un-maintainable code. So it is preferable to pass values back through arguments.

    Built in functions such as add, sub, etc use this argument passing method:

    Code
    add(x,1,2);
    trace("result is ",x);


    produces:

    Quote


    INFO: result is 3

    Note here that the name of a variable, x is the first argument passed to add. The second and third arguments are the numbers to be added, 1 and 2. The add function sums 1 and 2 and sets the variable x to the result, in this case 3.
    We can use variables with add in the following way:

    Code
    set(a,10);
    set(b,20);
    add(sum,get(a),get(b));

    Here we pass the variable name sum to the add function, but we pass the values of the addend variables a and b to the function. (Actually, add is one of only a handful functions that doesn't require the get() to extract the value of it's arguments. The other math functions, copy(), as well as trace() and the predicates for if statements automatically dereference variables passed to them. Nonetheless, user defined actions must always use get() to pass the values of variables.)

    We can use the same approach as add uses to return values in a simple action of our own creation:

    In this example we have an action named coord_string which takes one argument--the name of a variable that will be set to a string containing the coordinates. The action calls the txtadd function which composes a string in the format "(h,v)". The first argument is the variable name xyz, which means that the coord_string action sets the variable xyz to the string "(0,0)". Note that we do not use the get function on xyz because that would pass xyz's value, we want to pass its name so its value can be changed within the action.

    In the following slightly more complicated example we calculate an (approximation) of the distance between two points.


    First we look at the absolute value action abs. This function takes the name of a variable which contains a value, computes the absolute value and sets the variable to the result. Thus, it uses the only one argument both to provide input and return output. The action checks to see if the value for the variable is negative, and if so it subtracts the value from zero and writes the result back into the variable. If the value was positive, then the contents of the variable are unchanged.

    The dist function uses the octagonal approximation to compute the distance between the two points. We use this approximation because krpano does not provide a square root function. The dist action takes five arguments, the first is the variable name into which to return the result, followed by the x y coordinates for the first point and finally the x and y coordinates for the second point. It computes the differences for the x's and y's and then applies the absolute value function to each difference. It then multiplies the greater of these two differences by the magic number 0.941246 and the lesser by 0.41. These are then added and the result is copied into the variable whose name was provided as the first argument.

    AN IMPORTANT (SUBTLE) DIFFERENCE IN BEHAVIOR BETWEEN 1.0.8.10 AND PREVIOUS VERSIONS
    Previous to krpano 1.0.8.10, the number of actions were limited to 9. With 1.0.8.10 the number of arguments was extended to 99. While this is a great improvement, it does create a small backward compatibility problem.

    Consider the code:

    Prior to 1.0.8.10 this code would produce the output: a0. With 1.0.8.10 the output would be j. This is because krpano now looks at two digits past the % rather than one, so in the pre 1.0.8.10 case it substitutes the first argument "a" for %1 and in the 1.0.8.10 case "j" is substituted for %10.

    Hope this helps

    steve
    edited 5/30/10--corrected an error in the prose found by Michel, thanks Michel!

    Edited 2 times, last by pinsane (May 31, 2010 at 3:00 AM).

  • *love*

    and then he writes... hope this helps *g*

    YES!!!

    very much..

    So much time u put in it..
    i'm sure a lot of people gonna like your "tut".

    i understand now..
    my next steps:

    - try and error
    - use it
    - see where for to use it..

    their must be tons of posibilities with it...

    maybe it's nice to make some more examples that work in tours...
    Let's see..
    or get some examples from the field... that show us for what exactely we can use it... and for what not..

    gonna check Klaus code from the new update in the examples...


    Study time *wacko*

    Steve
    Thanx very much!

    Tuur *thumbsup*

  • he is really insane *g*


    "...some people say that he types his code with pints full of beer.."
    " ...some people say that he drives with a heineken house draft on the lap.."

    we better know him as THE Pinsane.


    *thumbsup*

  • Great tutorial! I have been wishing for a clear explaination for a long time. Thank you for putting it in easy to understand terms.

    Now onto putting it into use. *thumbsup*

    Jarred

  • Hi,

    Is it possible to make multiple operation in one instance,

    For example :

    Code
    add(plugin[background].width, get(area.pixelwidth), mul(pano_bg_thick, 2) );

    I've tried, but I'm not sure the operation can be combined ... thanks !

    Unfortunately, get() is the only function that returns a value in line, and thus can be used to pass a value into an argument list. Klaus recently introduced the ability to provide two arguments to a math function (as you did with mul in your example), but what this does is multiply the contents of the first variable argument by the second argument. The result is returned in the first variable argument.

    Steve

  • Klaus, "recursive" calls could be great, and avoid unnecessary line of codes !

    Hi Nelk,
    Recursive calls are supported. Anytime you see someone doing anything that requires looping (resetting an attribute on every hotspot for instance) they are using recursion.

    One limitation for recursion, is that Klaus prevents the call stack from getting too long, so if you're doing something that requires deep recursion (such as looping across a large number of plugins or traversing a large complex array) then you can get an action overflow error. I believe Klaus limits the number of single actions that can be called at any given time to 2000. You can get around this restriction by having your recursive function occasionally call wait(0); which internally breaks the action queue. Don't call wait(0); too many times however, as it will noticeably slow down the performance of your action.

    steve

  • Hi Pinsane,

    Recursive was probably not the good word, I didn't want to mean function like if/then or for(...) or switch /case.

    The thing I'd like is, if a function like this is call :

    Code
    add(plugin[background].width, get(area.pixelwidth), mul(pano_bg_thick, 2) );

    then, the

    Code
    mul(pano_bg_thick, 2)

    return the computed value.

    or maybe with something like

    Code
    get(mul(pano_bg_thick, 2))
  • Hi to everyone, this is one of my first post here, but the first of a big list I think,

    I'm coming from flashpanoramas, so I know how the xml is working in order to configure the pano engine.

    But with krpano, I have seen there are much more possibilities, and it seems to me that also there's much more performance, and so I have a lot of work to study the way of organizing files projects declaring actions, ...

    Is there any post talking about the best architecture / workflow / files order....??

    The other question would be, what this code would be used for in the real world ??

    I have to build a similar project to this http://rcdespanyol.visita3d.com/en.html but in krpano, any suggestions ?? is better to have an xml file for every pano or is better to use scenes .....??

    Any suugestion would be much aprecciated

    thanks in advance


  • But with krpano, I have seen there are much more possibilities, and it seems to me that also there's much more performance, and so I have a lot of work to study the way of organizing files projects declaring actions, ...
    Is there any post talking about the best architecture / workflow / files order....??
    The other question would be, what this code would be used for in the real world ??
    I have to build a similar project to this http://rcdespanyol.visita3d.com/en.html but in krpano, any suggestions ?? is better to have an xml file for every pano or is better to use scenes .....??


    Quite impressive scenery in that RCD tour (but sadly the image quality is somewhat missing).

    As for one file per panorama or using separate files for each panorama, it really depends on the amount of different panoramas you will have at the tour. If there's tens of scenes in the same file, it can really slow down loading time at least in some older iOS devices so dividing things to different files improves at least initial loading performance quite a bit. Also, have tried using bunch of scenes in multiple files but got to trouble with it. Depending on what you do with things (and how you handle navigation), you could actually use both separate files and bunch of scenes inside them.

    Still, you have to note that even while krpano has quite bit of documentation, there are usage/implementation cases that aren't always as clear as they could be. Documentation is improving slowly but you should invest some of your time on browsing forum as there's quite bit of interesting ideas and tips around here. :) Welcome to the game.

    Edited once, last by autiomaa (March 9, 2011 at 12:32 PM).

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!