LScript | LSIDE | Expressions

LScript v2.6 Release Notes

Features | Behaviors | Bugs

| New Features | (top)

      C plug-ins can now take advantage of the LScript Universal Requester mechanism.
      This service, provided by the LCore subsystem, allows C plug-ins to use LScript's
      requester mechanism to create their user interfaces using LScript code.  This
      system allows C plug-ins to retrieve the result of the requester (i.e., whether
      "Ok" or "Cancel" was press), as well as set the initial control values and
      retrieve the resulting values when the requester is dismissed.

      The LScript code that implements the requester, written either by hand or
      generated by the LSIDE Interface Designer, is stored in a NULL-terminated array
      of character strings.  This array is then passed to the Universal Requester
      mechanism for compilation:

                const char *ui_script[] = {
                                            "options",
                                            "{",
                                                "reqbegin(\"LScript Universal Requester test\");",

                                                "c1 = ctlnumber(\"Number\",lsur_num);",

                                                "if(reqpost())",
                                                    "lsur_num = getvalue(c1);",

                                                "reqend();",
                                            "}",
                                            NULL
                                          };
                ...

                int             err,ok;
                char            *messages[10];
                LSURFuncs       *lsurFunc;
                LWLSURID        script;

                double          lsur_num;
                void            *vars[1];

                lsurFunc = (*global)(LWLSUR_GLOBAL,GFUSE_TRANSIENT);

                script = (*lsurFunc->compile)(ui_script,messages,10);

      The Universal Requester mechanism will compile the provided script code, and
      return an opaque pointer that represents the compiled code.  If there were
      problems with your script code, any messages generated during the compile will
      be placed into the provided receptical.  If they exist, these messages should
      be processed and freed afterward:

                ...
                for(x = 0,err = 0;x < 10;x++)
                {
                    if(!messages[x]) break;

                    if(!strncmp(messages[x],"e#",2))
                    {
                        ++err;
                        (*msgFuncs->error)(&messages[x][2],"");
                    }

                    free(messages[x]);
                }

                if(err) return(AFUNC_OK);
                ...

      Messages generated will be prefixed with a meta-code that indicates their
      severity.  Informational messages will be prefixed by "i#", warning messages by
      "w#", and errors by "e#".  If errors occurred during the compile, you should
      gracefully exit (the returned script pointer will be NULL in any case).

      The Universal Requester mechanism takes care of making sure your initial values
      are where they should be, as well as seeing that the resulting values are returned
      to you.  In order to accomplish this, however, there are some design requirements
      that must be adhered to.

        1.  controls must be initialized with variables in order to have data exchange
            occur between the C plug-in and the LScript code.
        2.  variables used for data exchange must be prefixed with the letters "lsur_"
            (see the above code example)
        3.  a call to getvalue() must be made on a specific variable in order to retrieve
            the resulting control value for that specific variable back into the appropriate
            receptical in the C plug-in

      A list of (void *) are provided to the Universal Requester mechanism in order to
      provide both initial values and a location to receive the result.  Each (void *)
      should point to a variable type appropriate to the control with which it is
      associated.  For instance, a ctlnumber() pointer should point to a (double) value.

                ...
                lsur_num = 34.54;

                vars[0] = (void *)&lsur_num;

                ok = (*lsurFunc->post)(script,vars);
                ...

      When the requester is dismissed, any variables that have had an explicit getvalue()
      call assigned to them will be automtically updated in the C plug-in.

                ...
                if(ok)
                {
                    // user pressed "Ok", and values have been updated
                    ...
                }
                ...

      Values can be changed again and the requester re-posted as many times as necessary.
      When you are through with your requester, it should be passed to the release
      function to free up the memory it consumes.

                ...
                ok = (*lsurFunc->post)(script,vars);
                (*lsurFunc->release)(script);

                if(ok)
                {
                ...

      Any control type supported by LScript can be used in the requester, however, only
      the following control types are supported for data exchange:

            (char *)
                ctlstring()
                ctltext()
                ctlfilename()
                ctlimage()
                ctlsurface()
                ctlfont()

            (integer)
                ctlinteger()
                ctlchoice()
                ctlpopup()
                ctlcheckbox()
                ctlslider()
                ctlminislider()
                ctlstate()

            (double)
                ctlnumber()
                ctldistance()
                ctlpercent()
                ctlangle()

            (double[3])
                ctlvector()
                ctlcolor()
                ctlrgb()
                ctlhsv()

            (LWItemID)
                ctlallitems()
                ctlmeshitems()
                ctlboneitems()
                ctlcameraitems()
                ctllightitems()

            (LWImageID)
                ctlimageitems()

            (LWChannelID)
                ctlchannel()

      The header file required to use this mechanism (lwlcore.h) can be
      found in the LightWave 7.5b SDK distribution.  Also included in the
      SDK is a sample project, called "lsur", that illustrates the usage
      of the LScript Universal Requester mechanism.
      A keys() method can be applied to an associative array to extract all keys contained
      within the array.  This allows a script to access all data elements contained in the
      associative array in a linear fashion.
      A contains() methods is now available for integral data types that will, given a data
      type and value, search through indexable types (arrays, strings, etc.) to determine if
      that type and value exists.  It returns a Boolean true/false value to indicate the
      presence or absence, respectively, of the provided value.

            ...
            str = "Now is the time";
            info(str.contains("is the"));   // displays true (1)
            info(str.contains("Bob"));      // displays false (0)
            ...

            ...
            t = @15.87,"Blowfish",<1,9,0.5>@;
            info(t.contains(<1,9,0.5>));    // displays true (1)
            info(t.contains(15.87));        // displays true (1)
            info(t.contains("Blow"));       // displays false (0)
            ...

      LScripts now have additional modes of saving that can be selected using the new @save
      pragma.  The original mode stores the full path name of the script into the scene or
      object file.  This mode is the default, and does not have to be explicitly selected.

      Scripts can also be saved in relative mode.  This saves only the name of the script
      in the scene or object file.  Upon reload, a script with that name should exist in the
      default LScript directory \NewTek\LScripts.  This is handy for situations like batch
      rendering, allowing scripts to be flexibly relocated.

      Lastly, scripts can be directly embedded within, and carried along with, the scene or
      object file.  This ensures that the script will always be available no matter where the
      file goes, or when or where it is reloaded.

            @save   original
            @save   relative
            @save   embedded

      To employ these settings, the script must first be activated from a disc file.  Subsequent
      saving and loading of scene or object files will permanently maintain these settings.
      Scripts must be deactivated and the scene or object re-saved to disable the storage mode.

      Debugging should not be attempted with embedded scripts.
      Debug your scripts first before you activate this saving
      mode.
      A new control type called ctlviewport() is available to script requesters.  This control
      is similar to ctlinfo(), however, it functions as a viewport into a larger canvass area.
      The canvass can be much larger than the viewport window, and can be navigated using
      included vertical and horizontal scroll bars.

      The arguments to ctlviewport() are identical to that of ctlinfo(), save for an additional
      callback that reports the dimensions of the virtual canvass.  This size function will always
      be called immediately before the redraw function, so you can dynamically resize the canvass
      to fit the situation.

            c1 = ctlviewport(200,200,"vp_redraw","vp_size");

      This size callback should return the width and height of the canvass upon which the
      viewport (whose size is set in the initial function call) will traverse.  The size function
      could, for instance, return a fixed size or it could calculate the bounding area of objects
      positioned upon the canvass.  It accepts the control identifier to which the dimensions should
      apply.

            vp_size: ctl
            {
                return(800,600);
            }

      Drawing on the canvass should be performed without regard to viewport size.  Your drawing
      function should behave as though the user can see the entire canvass at once.  It accepts the
      control identifier for which drawing functions will apply.

            vp_redraw: ctl
            {
                drawbox(<80,80,80>,0,0,800,600);

                drawbox(<200,200,200>,50,50,20,20);
                drawbox(<0,200,0>,750,450,20,20);
            }

      In addition, the Control Object Agent class now exports two new data members called xoffset
      and yoffset.  These data members are currently a constant zero (0) for all types except for
      the viewport control.  The values in these data members contain the current X (left) and Y
      (top) offsets of the control's viewport.  These values can be used to accurately calculate
      things such as the virtual location of mouse events on the canvass.

            reqmousedown: mouse_x, mouse_y, ctrl
            {
                x = mouse_x - ctrl.x + ctrl.xoffset;
                y = mouse_y - ctrl.y + ctrl.yoffset;
                ...
            }

      Or they can be assigned values to position the viewport at a specific location on the
      virtual canvass.  The range of allowable values runs from zero (0) to
      (canvass_width - viewport_width) on the horizontal, and zero (0) to
      (canvass_height - viewport_height) on the vertical.  LScript will automatically clamp
      assigned values to these ranges if you exceed them.
      The Requester mechanism now provides four new primitive drawing functions, drawcircle(),
      drawelipse(), drawfillcircle() and drawfillelipse().  As their names imply, the first two draw
      unfilled circular shapes, while the latter two draw filled circular shapes, on the panel or in
      any other valid drawing region (e.g., ctlviewport()).

      drawcircle() takes four arguments, being (1) color, (2) X center point, (3) Y center point,
      and (4) radius.  drawfillcircle() takes five, (1) border color, (2) fill color, (3) X center
      point, (4) Y center point, and (5) radius.  All numeric values are interpreted as integers.

      drawelipse() takes five arguments, being (1) color, (2) X center point, (3) Y center point,
      (4) X radius, and (5) Y radius.  drawfillelipse() takes six, (1) border color, (2) fill color,
      (3) X center point, (4) Y center point, (5) X radius, and (6) Y radius.  All numeric values are
      interpreted as integers.
      A Requester panel's position can now be set and queried using the new reqposition() command.
      The command takes two optional arguments that specify the screen X and Y position for the
      panel.  These values can be set at anytime between reqbegin() and reqend().  If the panel is
      open on the screen when the call is made, the panel's position is immediately altered.

      Whether called with or without arguments, reqposition() will return the current position of the
      panel at the time the call is made.  This makes it possible for a script to perform its own
      between-runs management of the panel's position on the screen (LScript does this automatically
      since v2.5, but there are only a limited number of panels it will remember).

            generic
            {
                reqbegin("Position Test");
                ...
                req_x = recall("req_x",0);
                req_y = recall("req_y",0);
                reqposition(req_x,req_y);
                ...
                return if !reqpost();
                ...
                (x,y) = reqposition();
                store("req_x",x);
                store("req_y",y);

                reqend();
                ...
            }

      Because reqposition() will immediately update an open panel, on-screen positioning can be
      performed in real time:

            req_x,req_y;

            generic
            {
                reqbegin("Position Test");
                ...
                return if !reqpost("idle",500);
                ...
                reqend();
                ...
            }

            idle
            {
                ++req_x;
                ++req_y;
                reqposition(req_x,req_y);
            }
      New I/O functions are available to the I/O Object Agent passed to the save() and load()
      pre-defined script functions.  These new methods provide finer granularity when dealing
      with numeric data types in OBJECT I/O mode.

      readDouble() will pull in a double-sized numeric value (typically eight bytes) from the
      source file.  writeDouble() will store a numeric value of the same size.  The existing
      readNumber()/writeNumber() methods continue to perform their operations using the
      float-sized numeric value.

      readShort()/writeShort() will work with short-sized numeric values (typically two bytes).
      The readInt()/writeInt() functions continue to work with integer-sized values (typically
      four bytes).

      New File Object Agent methods are available that provide finer granularity when dealing
      with integral numeric data types.

      readDouble() will pull in a double-sized numeric value (typically eight bytes) from the
      source file.  writeDouble() will store a numeric value of the same size.  The existing
      readNumber()/writeNumber() methods will perform their operations using the
      float-sized numeric value.

      readShort()/writeShort() will work with short-sized numeric values (typically two bytes).
      The readInt()/writeInt() functions continue to work with integer-sized values (typically
      four bytes).

      Please see the note in the Behavioral Changes section regarding
      the new, optional argument that all File Object Agent binary-mode
      methods accept for managing byte ordering.
      The currently selected Weight, Texture or Morph vertex map can now be acquired in Modeler by
      providing an index value of zero (0) to the VMap() constructor when specifying one of the three
      vertex map types:

            vmap = VMap(VMWEIGHT,0) || error("Select a Weight map so I have something to do!");
      A new Layout function is available called visitnodes().  This function attempts to simplify
      the process of iterating down through object parenting hierarchies.

      The function requires two arguments.  The first argument is an Object Agent reference
      for an object type capable of containing children--Mesh, Light, Camera or Bone.

      The second argument is a character string that identifies a UDF in the script that will be
      called by LScript for each child object found in the hierarchy.  The UDF must accept two
      arguments, the parent Object Agent and the child Object Agent.  Be aware that multiple calls
      may be made with the same parent identity if that parent manages more than one child.

            generic
            {
                visitnodes(Mesh("MasterObject"),"process_node");
            }

            process_node: parent, child
            {
                info(parent.name," -> ",child.name);
            }
      A new Layout Object Agent method called keyExists() can be used to identify all of an object's
      channels that contain a keyframe at an indicated time index.  Each channel that contains a
      keyframe at the indicated time will be returned by the method.

            generic
            {
                if((channels = Mesh("Null").keyExists(2.0)) != nil)
                {
                    foreach(c,channels)
                        info("Null." + c.name);
                }
            }
      A binary() function is now available that will convert integer values into their binary
      equivalents in string form.  The function takes an integer value, and an optional size
      value (whose maximum on any platform is [sizeof(int) * 8] bits).  The function will remove
      leading zeros, unless an explict number of bits are indicated:

            generic
            {
                info(binary(32));       // displays "100000"
                info(binary(32,32));    // displays "00000000000000000000000000100000"
                info(binary(130,8));    // displays "10000010"
                info(binary(130,10));   // displays "0010000010"
            }
      A new Object Agent class has been defined in LScript called a Glyph.  This class is a container
      for color images, whose intended purpose is to act as color icons that can be drawn into display
      contexts such as the Requester redraw, ctlinfo() and ctlviewport().

      Glyphs can be constructed from a disc file by providing the filename as argument to the Glyph
      constructor.  Alternately, the name of an embedded binary block can also be specified as the
      source of the image data:

            ...
            cursor_img = Glyph(cursorGlyph);
            ...
            @data cursorGlyph 500
            000 000 002 000 000 000 000 000 000 000 000 000 012 000 012 000 024 000 080 080
            ...

      A 'nil' is returned if the glyph construction fails for some reason.

      The Glyph Object Agent currently exports no public methods.  The following public data members
      are available:

            w               the width of the glyph image (read-only)

            h               the height of the glyph image (read-only)

            pixel[col,row]  array of pixel color data for the glyph image
                            (write-only in default mode, read-write in dual-mode)

      Once constructed, the glyph can be drawn into display contexts using the new drawglyph()
      function.  This function takes the Glyph Object Agent, along with the X and Y position
      within the context where the glyph should be drawn:

            ...
            // draw the cursor glyph
            drawglyph(cursor_img,
                      cursor_pos.x - integer(cursor_img.w / 2),
                      cursor_pos.y - integer(cursor_img.h / 2));
            ...

      Two additional arguments, both optional, can be provided to the Glyph() constructor.  By default,
      Glyphs are generated in a way that allows them to be drawn to the display context with the
      utmost speed.  What is traded for such drawing speed is the ability to directly access individual
      pixels in the image.  The second argument you can provide to the Glyph() constructor is a
      Boolean flag that tells LScript that you'll need pixel-based access to the image data in the
      Glyph (dual-mode).  You'll need to specify this mode, for instance, when you plan to overlay the
      Glyph onto Image Filter data (Image Filter data is not a valid display context, so the high-speed
      form of the Glyph cannot be directly drawn onto it).

      The third optional argument is a transparency color mask to be used when drawing the Glyph.
      Because the use of such a mask requires per-pixel access to the image data, specifying a
      transparency mask automatically enables this mode in the Glyph object.

      Because Glyph image data can be of any type supported by any Image Loader plug-in active in
      the host application (see notes regarding this in the Behavioral Changes section), you may
      need to specify a file-type hint when you embed the image data into your script via a binary
      block.  By default, LScript will append the extension ".tga" to image data in a binary block
      before attempting to process it.  Sometimes this will work with non-Targa image data, and
      sometimes it won't.  Your file-type hint should take the form of the appropriate file
      extension, and be appended to the name of the binary block, separated by an underscore
      character.

            @data cursorGlyph_jpg 500
            000 000 002 000 000 000 000 000 000 000 000 000 012 000 012 000 024 000 080 080
            080 080 080 080 080 080 080 080 080 080 080 080 080 080 080 080 080 080 080 080
            ...
            085 069 086 073 083 073 079 078 045 088 070 073 076 069 046 000
            @end
      Variables local to a user-defined function can now be made static (i.e., their values persist
      across invocations of the function) by prefacing the name of the variable with the characters
      "st_".

            generic
            {
                for(x = 0;x < 5;x++)
                    docount();
            }

            docount
            {
                if(!st_value)
                    st_value = 0;

                info(++st_value);
            }
      Five new Command Sequence functions have been added to Modeler LScript:

            revert([<filename>])

                Causes the current object to revert to the mesh stored in the specified disc
                file.  If no file is provided, LScript attempts to revert using the object file's
                current filename (of course, if the file hasn't been saved yet, an error will
                result).

            selectvmap(<type>,<name>)

                Selects the specified VMap type and name for editing.  The <type> can only be
                one of Morph, Spot, Weight, Subpatch weight or Texture UV.  These types can be
                specified using the same type values provided to the VMap() Object Agent
                Constructor.

            meshedit(<name>)

                Invokes a MeshEdit plug-in of the specified name.

            smoothscale(<distance>)

                Performs a Smooth Scale operation on the selected mesh, offsetting the
                mesh by the specified <distance>.

            changepart(<name>)

                (Re)assigns the part name of the currently selected polygons.
      The Mesh Object Agent exports two new public methods:

            layerName(<layernum>)

                Returns the assigned name for the specified layer of the object.  If the layer
                has yet to be assigned a name, then the method returns 'nil'.

            layerVisible(<layernum>)

                Returns a Boolean true or false to indicate the visible state of the indicated
                layer.
      New character classification methods have been added to the system.  Each of these methods maps
      directly to their ANSI C counterparts, and can be applied to integer values, or string values
      where only the first character in the string will be regarded.

      None of the methods accept arguments, and each returns a Boolean true/false value.

            isPrint()

                Test the character for printability.

            isAlpha()

                Test the character for membership in the alphabetic class (i.e., a-z or A-Z).

            isAlnum()

                Test the character for membership in the alpha-numeric class.

            isAscii()

                Test the character for membership in the ASCII character set.

            isCntrl()

                Test the character for membership in the control code character set.

            isDigit()

                Test the character for membership in the numeric character set (i.e., 0-9).

            isPunct()

                Test the character for membership in the punctuation character set
                (i.e., any printable character that is not a space character, or a character
                for which isAlnum() returns true).

            isSpace()

                Test the character as whitespace (i.e., a space character, or character in the
                range 0x09–0x0D).

            isUpper()

                Test the character for membership in the upper-case alphabetic character set.

            isLower()

                Test the character for membership in the lower-case alphabetic character set.

            isXDigit()

                Test the character for membership in the hexidecimal character set (i.e.,
                one of "0123456789ABCDEF").
      The existing LScript log() function calculates the natural logarithm (base-e) of the provided
      value.  This can be a bit misleading for those seeking base-10 functionality, so a new function
      has been added, log10(), to address this.

            generic
            {
                x = log(23);
                y = log10(23);
                z = log(23) / log(10);  // same as log10(23)
            }
      The LScript language has a new power operator, "^^".  This operator can be used directly in the
      the language as a substitute for the pow() function.

            t = 5^^2;       // 25
            i = 10;
            info(i ^^ 3);   // displays 1000
      Image Filter LScripts can use a new function called overlayglyph() to blend a Glyph Object
      Agent onto the image data cache.  This function takes a Glyph Object Agent reference and an
      X and Y offset position where the Glyph will be stamped onto the image cache.  An optional
      fourth argument allows the Glyph image to be composited onto the image cache with a
      transparency (alpha) value, ranging from 0.0, which renders the Glyph without transparency
      (the default value), to 1.0, which renders the Glyph with 100% transparency (the Glyph will
      not be visible).

            bug;

            create
            {
                bug = Glyph(vt4000,true);
                setdesc("Bug");
            }

            process: width, height, frame, starttime, endtime
            {
                overlayglyph(bug,1,1);
                overlayglyph(bug,1,height - bug.h,0.25);
                overlayglyph(bug,width - bug.w,height - bug.h,0.5);
                overlayglyph(bug,width - bug.w,1,0.75);
                overlayglyph(bug,(width - bug.w) / 2,(height - bug.h) / 2,0.25);
            }

            @data vt4000 67800
            000 000 002 000 000 000 000 000 000 000 000 000 188 000 120 000 024 000 026 026
            026 025 025 025 025 025 025 025 025 025 026 026 026 026 026 026 025 025 025 025
            ...
            000 000 000 000 000 000 084 082 085 069 086 073 083 073 079 078 045 088 070 073
            076 069 046 000
            @end

    The result:
      Channel Object Agents now provide a data member called parent that contains the LightWave
      Object Agent to whom the channel belongs.
      The Scene Object Agent's generalopts[] array now has a seventh [7] array element that
      indicates the current state of the "Auto Key" button on the interface -- Boolean true
      for "on", and Boolean false for "off".
      The Scene Object Agent now provides the following additional data members:

            alertlevel

                indicates the state of the Alert Level setting in Layout.  Will be one of
                ALERT_BEGINNER, ALERT_INTERMEDIATE or ALERT_EXPERT.

            boxthreshold

                an integer value indicating the current bounding-box threshold setting.

            autokeycreate

                indicates the state of the Alert Key Create setting in Layout.  Will be
                one of AKC_OFF, AKC_MODIFIED or AKC_ALL.

            numthreads

                the number of threads that will be spawned during rendering.

            animfilename

                the name of the currently selected animation file.  'nil' if none.

            rgbprefix

                the name of the RGB file-saving prefix.  'nil' if none.

            alphaprefix

                the name of the Alpha channel file-saving prefix.  'nil' if none.
      LightWave Object Agents now export an array called axislocks[].  This array contains
      nine elements, where each triplet corresponds to the object's Position, Rotation and
      Scaling channels.  Each triplet element corresponds to the appropriate axis for the
      transformational category, and is a Boolean value that indicates the lock status of
      that particular channel/axis element (true == locked).
      An Icon Object Agent has been added to LScript.  This object type is designed to construct
      and house a bitmapped pattern of pixels.  These pattern of pixels represent a complete user-
      defined text character (or "dingbat").  Icon characters are active only during an active
      Requester panel.

      The maximum dimension of each character is 16 pixels wide by 14 tall.  Characters are defined
      by placing character strings into an array.  The width of each character string should not
      exceed the maximum width, while the number of strings in each array should not exceed the
      maximum height.  In each character string a period character ('.') indicates a zero-pixel
      value (off), while any non-period character will represent a one-pixel value (on).  Each array
      is provided to the Icon() constructor for construction and storage of the monochrome character
      image.

            folder_icon = @ "..1111..........",
                            ".1....111111....",
                            ".1..........1...",
                            ".1..........1...",
                            ".1...1111111111.",
                            ".1..1..........1",
                            ".1.1..........1.",
                            ".11..........1..",
                            ".111111111111..."
                          @;

            eyes_icon = @ "................",
                          "................",
                          "...mmm...mmm....",
                          "......m.m.......",
                          "....mm...mm.....",
                          "...m..m.m..m....",
                          "..m.mm.m.mm.m...",
                          "..m.m..m.m..m...",
                          "..m....m....m...",
                          "...m..m.m..m....",
                          "....mm...mm.....",
                          "................",
                          "................"
                        @;

            @define FOLDER      1
            @define EYES        2

            generic
            {
                icon[FOLDER] = Icon(folder_icon);
                icon[EYES] = Icon(eyes_icon);

                reqbegin("Testing Icons");

                c1 = ctlbutton("Open File " + icon[FOLDER],75,"bfunc1");
                c2 = ctlstring(icon[EYES].asStr() + " Browse","testing");

                reqpost();
            }

            bfunc1
            {
                ...
            }

      Icons can then be embedded in any character string that will be processed and displayed by
      LScript (button or control label text, listbox entries, etc.).
      In addition to SCHEMA, the Custom Object flags() function can now also return the
      following values:

            VPINDEX

                indicates that the value in the view data member should correspond to
                the viewport number instead of its type.

            NODEPTH

                causes drawing of the object to occur in front of all other OpenGL elements,
                regardless of Z position.
      The getdir() command now recognizes the SCRIPTSDIR constant, or the "scripts" string, and
      will return a path that points to the NewTek\LScripts installation directory on the local
      system.
      A getsep() function has been added that will return the platform-specific path separator
      character.

            ...
            info(getsep());     // displays "\", "/" or ":" depending on the operating system
            ...
      The Displacement Access Object Agent, provided to the process() function of Displacement
      LScripts, now exports a point data member.  This data member holds a Point Object Agent
      for the mesh point currently being processed.
      LScript now provides support for hexidecimal-formatted numbers. These numbers are prefixed by
      the "0x" character sequence, and can contain 1-8 hexidecimal digits (A-F, a-f, 0-9).

            ...
            t = 5 * 0xa0;       // equivalent to 5 * 160
            ...

      In addition, the integer() function has been enhanced to recognize and process hexidecimal
      numbers in string form.

            ...
            t = integer("0x0a");                        // t will hold 10
            t = integer("Now is the 0xF000 time");      // t will hold 61440
            ...
      LScript now provides support for binary-formatted numbers. These numbers are prefixed by the
      "0b" character sequence, and can contain 1-32 binary digits (0 or 1).

            ...
            t = 0b1001;         // t holds 9
            ...

      In addition, the integer() function has been enhanced to recognize and process binary
      numbers in string form.

            ...
            t = integer("0b110");                       // t will hold 6
            t = integer("Now is the 0b10000 time");     // t will hold 16
            ...
      A new function called lscriptVersion() returns information about the version of the LScript
      system that is executing the script.  It returns four elements in this order:  a string
      representation of the LScript version (identical to that displayed by LScript in the title
      bar of the script-select file dialog); the major component of the version as an integer;
      the minor component of the version as an integer; the patch level of the version as an
      integer.
      The Layout LScript Compiler provides a new mode for generating compiled scripts.  This new
      compiled type is a "library", and is intended to be a collection of functions (potentially
      unrelated) that are not associated with any single plug-in architecture.  Once compiled, this
      "library" script can be used by any LScript through the use of the library command.

      Functions defined within the "library" file can be referenced from a script as though they
      were built into LScript.

            @version 2.6
            @warnings
            @script generic

            // the 'functions.lsc' library contains the gimmeStringFrom() function

            library "functions.lsc";   // no path, so file should be in \NewTek\LScripts

            generic
            {
                t = 104;
                info(gimmeStringFrom(t));
            }
      A new callback named reqkeyboard() can be defined to intercept keyboard activity on the active
      Requester panel.  This function takes a single argument which represents the raw key pressed.
      It should return a Boolean false or true value indicating that the key should or should not be
      further processed by the system, respectively.

            @version 2.6
            @warnings
            @script generic

            generic
            {
                reqbegin("Testing reqkeyboard()");

                c1 = ctlstring("String","value");

                if(reqpost())
                    info("You pressed Ok");
                else
                    info("You pressed Cancel");

                reqend();
            }

            reqkeyboard: key
            {
                if(key == 13)       // enter
                {
                    reqabort(true);
                    return(true);
                }
                else if(key == 27)  // escape
                {
                    reqabort();
                    return(true);
                }

                return(false);
            }

      The following pre-defined constants have been added to the environment in order to help process
      key events:

            REQKB_F1                REQKB_KB0               REQKB_KP0
            REQKB_F2                REQKB_KB1               REQKB_KP1
            REQKB_F3                REQKB_KB2               REQKB_KP2
            REQKB_F4                REQKB_KB3               REQKB_KP3
            REQKB_F5                REQKB_KB4               REQKB_KP4
            REQKB_F6                REQKB_KB5               REQKB_KP5
            REQKB_F7                REQKB_KB6               REQKB_KP6
            REQKB_F8                REQKB_KB7               REQKB_KP7
            REQKB_F9                REQKB_KB8               REQKB_KP8
            REQKB_F10               REQKB_KB9               REQKB_KP9
            REQKB_F11
            REQKB_F12               REQKB_ALT               REQKB_RETURN
                                    REQKB_SHIFT             REQKB_INSERT
            REQKB_LEFT              REQKB_CTRL              REQKB_HOME
            REQKB_RIGHT                                     REQKB_END
            REQKB_UP                REQKB_DELETE            REQKB_PAGEUP
            REQKB_DOWN              REQKB_HELP              REQKB_PAGEDOWN
      A new indexOf() method can be applied to string, linear array and binary block data types.  This
      method can be used to locate data within these indexable types.  The method returns the index
      where the specified data is found.  If it is not found, then a value of zero (0) is returned.

      In the case of character strings, the provided search value is treated as a displayable character:

            ...
            s = "val1:15:3.14";
            ndx = s.indexOf(':');   // returns 5
            ...

      For linear arrays, the value provided must match an element in both type and value in order
      to be considered a successful match.  Not all data types are supported for searching.

      Searches through binary data will treat the provided integer value as an unsigned character,
      and should be in the range 0 to 255.

      By default, searches begin at the initial index offset of one (1).  You can specify a beginning
      offset other than one by providing the integer offset before the search value.

            ...
            s = "val1:15:3.14";
            ndx = s.indexOf(':');           // returns 5
            ndx = s.indexOf(ndx + 1,':');   // returns 8
            ...
      The Surface Object Agent's getValue() method now has a companion method called setValue().  By
      providing the channel name and an appropriate value, the value of the Surface's channel can be
      altered.

            ...
            (obj) = Scene().getSelect();
            (firstsrf) = Surface(obj);
            srf = Surface(firstsrf);

            srf.setValue(SURFCOLR,<0,255,255>);

            translucency = srf.getValue(SURFTRNL);
            srf.setValue(SURFTRNL,translucency * 2);
            ...

      All channels supported by getValue() can be altered using setValue() except for those involving
      Image Object Agents.
      The LScript pre-processor recognizes a new compile-time pragma named @sequence.  This pragma
      allows you to define a collection of names whose values are sequential increments.  This pragma
      type is similar to the C enum function.

      A collection of sequence names is enclosed with open and close braces:

            ...
            @sequence  { ... }
            ...

      Names in the collection are separated from one another by commas:

            ...
            @sequence  { VIEWPORT_CTL, COPY_CTL, PASTE_CTL }
            ...

      Like lines of binary data, entries in the sequence collection can span multiple lines:

            ...
            @sequence  { VIEWPORT_CTL,
                         COPY_CTL,
                         PASTE_CTL }
            ...

      By default, sequences begin at one (1) and increment by one thereafter.  That is, in the example
      above, VIEWPORT_CTL would have the value 1, COPY_CTL would equate to 2, and so on.  The sequence
      value can be overridden at any point by assigning a new sequence value to a collection entry:

            ...
            @sequence  { VIEWPORT_CTL,
                         COPY_CTL = 5,
                         PASTE_CTL }
            ...

      In this case, VIEWPORT_CTL would still have the value of 1, but COPY_CTL would have the value
      5 and PASTE_CTL would be assigned the value 6.

      An increment value can also be specified by separating the sequence value from the new increment
      value using a colon:

            ...
            @sequence  { VIEWPORT_CTL = 1:2,
                         COPY_CTL,
                         PASTE_CTL }
            ...

      With this code, VIEWPORT_CTL would still have the value of 1, but COPY_CTL would have the value
      3 and PASTE_CTL would equate to 5.

      Once declared, you can use these sequence entries anyplace in your script you would otherwise
      use a @define'd value.
      The ctlbutton() function now accepts an optional fourth parameter that constitutes an argument
      list that will be passed to the button's designated callback function.  This argument list is
      wrapped in quotation marks (i.e., passed as a string), and contains one or more argument values
      each separated by a comma.

            ...
            reqbegin("Testing");
            c1 = ctlbutton("press me",70,"press_me","10,r1,r2");
            reqpost();
            ...

      The arguments indicated can be of a constant value (numeric or character string, with the
      latter enclosed in double quotation marks), or they can be variable name references that
      are either global or local to the function that invoked ctlbutton().

            r1 = "bob";

            generic
            {
                r2 = "hood";

                reqbegin("Testing");
                c1 = ctlbutton("press me",70,"press_me","10,r1,r2");
                reqpost();
                reqend();
            }

            press_me: arg1, arg2, arg3
            {
                info(arg1," ",arg2," ",arg3);
            }

(lscript) | Behavioral Changes | (top)

      LScript's execution mechanism has been re-engineered to remove all global contexts.
      A script's context is now passed from function to function as is the standard for LightWave
      plug-ins.

      Due to the redesign of LScript's execution mechanism, the interface for User-Defined
      Object Agents and DLL functions has been altered to include an opaque context pointer
      in the LSFunc structure.  This opaque pointer must be included in all calls to LScirpt
      functions defined within that structure.  The interface version has been increased
      as a result to 1.4, and all new Object Agent or DLL development must return this
      value to LScript to ensure the correct structure pointer is provided to callbacks.

      Existing binary Object Agent or DLL files should continue to function with LScript at
      the previous (1.3) interface version, although it is highly recommended that the
      Object Agent code be updated for the v1.4 interface, if possible.  The v1.3 interface
      is no longer governed by the LScript memory manager.

      The new LScript header file can be found in the include directory
      of the LightWave 7.5b SDK distribution, and is now called
      "lwlscript.h".
      The LScript Debugger's variable watch system has been redesigned.  The current function's
      arguments and local variables are now automatically displayed in the watch window as long
      as the function is active.  As each new function is entered (or returned to), it's variables
      replace those of the previous function.  This behavior is more consistent with popular
      debuggers.  Informational displays have also been greatly enhanced.

      Global variables are not automatically added to the watch window.  In order to add global
      variables to the watch, you must now highlight the variable name in the script by double-
      clicking with the left mouse button, and then select "Add Watch" from the Debug menu (or
      press the F3) key.  If the selected text matches the name of a global variable in the script,
      it will be added to the watch window and will remain in the watch window as you move in and
      out of functions.
      The Modeler new() command, upon success, now returns a Mesh Object Agent for the new
      object created.

            main
            {
                m1 = Mesh(0);
                info(m1.id);

                m2 = new();
                if(!m2.isInt())
                    info(m2.id);
            }
      The Mesh() constructor now returns 'nil' for an index value that is out of range instead
      of generating an error message and halting execution of the script.
      LScript now automatically updates any persistent Object Agent references (i.e., stored in
      variables or arrays anywhere in the script) with new object id's whenever they are
      altered by Layout.  This action occurs transparently, with no script intervention required.

      It can result in some disconcerting behavior in your script unless you program with an
      awareness that it can occur.  For example, a script that stores Mesh Object Agents in an
      array for processing might look like the following:

        ...
        curObj = Mesh();
        while(curObj)
        {
            objList[++x] = curObj;
            curObj = curObj.next();
        }
        ...

      Later in the script, a particular object whose Agent is stored in the array is cleared
      from the Layout scene using the ClearSelected() Command Sequence function:

        ...
        objList[2].select();
        ClearSelected();
        ...

      When ClearSelected() is called, Layout removes the selected object from the scene.  This
      action typically causes a chain reaction in the current scene that alters all the internal
      identifiers of the remaining objects.  This event is now handled by LScript transparently.
      However, you can see the result by looking at the array contents before the deletion:

      and then after:

      Notice that it looks as though the array itself has been altered by the change in the
      scene.  In actual fact, the underlying object identifiers of each Mesh Object Agent
      have been updated, and their (new) corresponding object names now appear (in this case,
      in the debugger watch window).

      The fifth element in the array, which referenced the "(5)" Null object in the scene, now
      identifies itself as "(none)".  This is an indication that the object identifier it
      contains is no longer valid in the current scene.

      You should be aware of actions in your script that might generate such behaviors if you
      have stored Object Agents of this type.  From the example above, if you are deleting
      objects from the scene based upon Object Agents stored in an array, you need to avoid
      order dependency in the code that processes the array.

      The Mouse functions now take a single Object Agent argument that wraps all available parameters
      into a single package.  This Object Agent exports the following data members:

            ctl         the Control Object Agent involved in the event ('nil' if none)
            x           X position of the event
            y           Y position of the event
            button      which mouse button triggered the event; 1=LMB, 2=MMB, 3=RMB
            count       the click count; 1=single-click, 2=double-click
            keys[3]     indicates key active modifiers; [1]=CTRL, [2]=SHIFT, [3]=ALT

      The Object Agent currently exports no public methods.

            ...
            reqmousemove: md
            {
                if(md.ctl)
                {
                    vp_x = md.x - md.ctl.x + md.ctl.xoffset;
                    ...

      This change will cause problems for compiled LScripts that use the
      old form of the mouse-handling functions.  These scripts must be
      updated and recompiled in order to function correctly with
      this release of LScript.
      The File Object Agent methods that deal with binary file modes now accept an optional Boolean
      argument that indicates whether or not the read numeric value should have its byte ordering
      swapped.  By default, byte ordering will remain as it was read in from the file.  Passing a
      Boolean true will cause the byte ordering to be swapped before the value is returned.
      The constant LINUX has been added to the environment, and will be returned by functions like
      platform().  It can also be tested for using the pre-processor's conditional build @if system.
      Any tests against this value that are true indicate that the script is running under the Linux
      version of Screamernet (i.e., runningUnder() is guaranteed to return SCREAMERNET).
      The pre-processor now performs multi-pass processing on the script in order to deal with
      situations such as pragma directives appearing in multi-line comments:

           /*
           @define FOO  100
           @autoerror
           */
      Image-loading activities, such as those performed by ctlimage() or Glyph(), now channel through
      any Image I/O plug-ins that might be active in the host application (Layout or Modeler).  This
      means that a much broader range of image file formats can now be processed besides just Targa.
      The ctlimage() function now returns a 'nil' value if the attempt to load an indicated image file
      fails.  This will usually be an indication that the required ImageLoader plug-in has not been
      installed into the application (an error message will still be displayed to the user if the
      indicated image file simply doesn't exist).  Scripts using non-Targa image files should check
      this return value to ensure that the control was properly created.
      The ChannelGroup() constructor now accepts two ChannelGroup Object Agents as arguments.  Passing
      one will return the first sub-group found under the specified channel group, however, two are
      needed in order to iterate through all sub-groups found under a specified channel group.  The
      second ChannelGroup Object Agent is considered the "current" channel, and any sub-group defined
      following it will be returned (or 'nil', if no further sub-groups exist).
      The setvalue() function now first attempts to match a list value for ctlpopup() controls if a
      string value is provided.
      In order to be able to process discontinuous UV values, several of the VMap Object Agent's
      methods have had their argument lists augmented.

            isMapped(<point>[,<polygon>])

                isMapped() now accepts an optional second argument which should be the
                polygon for which the discontinuous UV values for the indicated point
                should be checked.

            getValue(<point>[,<polygon>][,<index>])

                getValue() processes a second argument that indicates the polygon for
                which discontinuous UV values should be retrieved.  an <index> value can
                also be specified to limit the retrieval to a specific value index.

            setValue(<point>,<value>|<array>[,<polygon>][,<index>])     (MODELER ONLY)

                a <polygon> can now be included in the setValue() argument list to allow
                values to be assigned to a polygon's per-vertex (discontinuous) UV.
      The loadimage() and clearimage() functions are now global, and can be used in either Layout or
      Modeler.
      The Mesh(), Camera(), Light() and Image() constructors have had their scanning code enhanced.
      When an object name is provided, these constructors will match the first object found with that
      name, regardless of alphabetic case.  However, if two or more objects of that type exist in the
      system with the same name, then one must match the name provided exactly in order for a sucessful
      match to occur.

      For example, an object called "Cow.lwo" is loaded into the application.  The following code will
      successfully match that object:

            ...
            obj = Mesh("cow");
            ...

      However, subsequently an object called "COW.lwo" is loaded, leaving "Cow" and "COW" in the
      application.  The previous code would fail (returning 'nil'), and must be altered to
      unambiguously match one of the loaded objects:

            ...
            obj = Mesh("Cow");
            ...

      Object names returned to the script by the application will contain their respective case
      settings, and so should always match the correct objects when used.
      All pragma directives must now begin in the first column of the row in order to be
      recognized.  This is to remove confusion when an initblock token ('@') appears
      as the first character on the line (at a column other than the first).
      The ctlslider() function now accepts an optional fifth integer argument that can
      be used to specify the absolute width of the slider component of the control.  This
      width is independent of the range of the slider itself.
      The LScript Debugger's watch and message windows can now be interactively resized.  By clicking
      and dragging in the empty area just above either of these windows, you can resize the viewable
      area to within some predefined limits.
      The count() method can now be provided a character argument when applied to string-type
      objects.  When provided, the number of occurances of that character will be returned
      to the caller as an integer.

            ...
            s = "1:2:5:38";
            info(s.count(':'));   // displays 3
            ...
      The asStr() method can now be applied to linear arrays.  Supported data types will be
      concatenated into a single string value.  Elements of 'nil' value are ignored.  You can
      provide an optional character value that will be used to separate values added to the
      string.

            ...
            a[1] = "val1";
            a[2] = 15;
            a[3] = nil;
            a[4] = 3.14;
            info(a.asStr(':'));     // displays "val1:15:3.14"
            ...
      The CommandInput() function has been enabled for use in Modeler LScripts.  It can be used
      to invoke any CommandSequence functions specific to Modeler or common to both applications
      that have not specifically been addressed within LScript.

            ...
            CommandInput("Surf_SetWindowPos 100 50");
            ...

      You should continue to use any available LScript-specific functions that map to CommandSequence
      functions to provide argument type and value checking within your script.
      The compiler now correctly recognizes and processes numeric values in scientific
      notation without the need for trailing decimal values.

            ...
            val = 2e2;
            ...

(lscript) | Bug Fixes | (top)

      Due to the re-design of the execution mechanism, Procedural Texture LScript's can now
      function properly when rendering is performed with multiple threads.
      If the Mesh(0) constructor call was made on an unsaved object, it would return an
      Agent for an object in the workspace that had been saved to disc already.
      The Mesh(0) call was activating code that caused invalid indexing to occur under
      Layout.  When problems arose, they most often manifested as the incorrect object
      being proxied.
      The requpdate() function was not refreshing the panel when no arguments were
      provided.
      The setvalue() function was not correctly accepting empty string values.
      Functions that did not return a value as expected could wreak havoc with LScript's
      internal stack, causing a crash if they were used directly in comparisons, e.g.:

             if(NotDone() == true)
                ...

             NotDone
             {
                 if(x == 0)
                     return(true);

                 // nothing returned on this path...
             }
      The server() method contained an inverted state test that prevented it from returning
      anything but 'nil'.
      The Modeler makecone() command was incorrectly converting floating-point arguments
      into integers.
      ctlinfo() controls were not being correctly rendered when they were attached to tab
      pages.
      SelectItem() was not properly processing bone name references.
      Pre- and post-increment and pre- and post-decrement operators were not functioning
      properly when applied directly to array elements.
      Pragma directives inside comments were not being properly ignored by the pre-processor.
      The setvalue() and getvalue() functions were not working properly with Tab controls.
      The foreach() operator would not process any Object Agent type except Mesh.
      The foreach() operator would not recognize Layout Object Agents in an array
      provided for processing.
      The filecopy() function contained a nasty bug that would destroy the contents of
      the source file.
      A rare case could occur with the foreach() operator where the variable used for the
      iteration could retain a memory pointer that was subsequently reused by LScript's
      memory manager for assignment to another variable.  This situation could lead to a
      crash when a function's local memory was reclaimed by the garbage collector -- the
      cross-link situation would clear two variables when one was released, leaving the
      second in an undefined state when it too was reclaimed.
      Several constants defining the type of a Light were missing from the environment.
      The points[] data member of the Polygon Object Agent was not being correctly bound
      to its object type, and as a result, was inaccessible.
      The measurements of the getWorldRotation() method were not being correctly converted
      into degrees before return to the script.
      The ShadowColor() function did not have the correct number of arguments defined.
      When a whole number was used to initialized a ctlangle() control, the control value
      would only increase regardless of which direction the scroller was dragged.
      A memory overrun could occur in the Requester's panel position mechanism when all
      available slots were filled.  Panel positions are aged, with the oldest being
      replaced by the current Requester, but the actual position in the list was not being
      used.  This overrun could manifest, among other ways, as a crash of the application
      when the mouse pointer entered the Requester panel.
      A control event ocurring in a modal Requester that had spawned another modal
      Requester could cause LScript to become confused as to which was the currently
      active panel.  This typically manifests as the child panel being inaccessible,
      and crashes possible with mouse activity.
      The Scene fogColor() method was assigning all color values to the vector X element.
      The ctlchannel() code had a subtle problem that prevented it from correctly
      locating the specified initial channel in the listbox.  This left things in
      an unstable state, leading to crashing on subsequent invocations.
      File line counting was not accurate when the last line did not contain a newline
      sequence.
      Queue Object Agents were not being properly reclaimed by LScript's garbage collector,
      potentially leading to unsocial behavior (crash or lockup).
      The split() function would cause a memory overrun whenever the drive component of
      the provided path was loinger than about eight characters.  Since PC drive designators
      are almost always only two characters, this problem manifested more readily on the
      Mac.
      Several Command Sequence functions (like TargetItem() and ParentItem()) failed to consider
      bones as valid targets.
      Corrected a problem with setting listbox selections using the setvalue() function.
      Corrected a problem with listbox updates and the requpdate() function.
      Ownership of a Particle System Object Agent was not explicity identified in the system,
      leading to destruction of the Object Agent (and to the underlying particle system) when
      a variable that had attached to it was reclaimed by the garbage collector.
      The meshedit() function was calling the SelectVMap command.
      Requester separators were not erasing properly.
      The ctlactive() function was not handling all elements of a compound control.
      The @insert mechanism was not functioning correctly using the standard C file handling
      functions.  The pre-processor has been redesigned to process inserts in memory instead.
      The LScript debugger was not capturing script messages correctly.
      The LScript debugger was crashing upon exit due to some poor handling of the watch and
      message window entries.
      The Requester mouse functions were not covering the entire client area of the panel
      when the mode was non-modal.
      The LScript configuration module, responsible for managing data from functions like
      store() and recall(), contained some potential ambiguity problems in its use of the
      C strncmp() function when scanning for keys.  This potential problem only affected
      non-Windows platforms.
      The internal mechanism responsible for duplication of an active script (engaged
      in cases where you clone an item with a script enabled, for instance) was based on
      some fairly archaic code.  The means by which it performed the copy operation would
      often leave the original script at risk of destruction, and would never really
      activate the script on the new item.

      The new mechanism will correctly activate the source script on the target item.  It
      will also copy certain global values from the original script into the corresponding
      values in the new activation.  Due to their complexity, certain data types, such as
      Binary Blocks and internal Object Agents, are not duplicated in the target script.

      The copy of values occurs only after the script is activated on the duplicated
      object, and its create() function has successfully completed.  This may have some odd
      effects on your script, most obviously where the setdesc() function is concerned.
      If you call setdesc() in your create() function, the duplicated script may end up
      displaying values to the user that are not valid after the value copy has completed.

      In order to combat this problem, LScript will look for a pre-defined function called
      cloned() in the duplicated script.  This function, if it exists, will be invoked by
      LScript immediately following the duplication of global values from the original
      script.  The cloned() function takes no aguments, and should return no values.

      Activities involving data changes within your script should be centralized within this
      function.  For instance, you could place a single call to setdesc() in this function,
      and then invoke the function whereever appropriate from within your script:

            ...
            create
            {
                ...
                cloned();       // after initial operational values are established
            }

            cloned
            {
                // data values have changed...
                setdesc("...");
            }

            options
            {
                ...
                cloned();       // after user has changed operational values
            }
            ...
      The setvalue() function was not completely simulating user interaction with a control,
      so not all defined callbacks were being invoked when a control's value was changed
      programmatically.
      Vector and distance controls were not evenly sizing their input fields when modified
      using ctlposition().  Each now allocates the specified control width, less the
      width of the control's label, evenly among their three input fields.  In order to ensure
      their integrity, the widths of each are now clamped when they fall below a pre-defined
      minimum.
      Usage of the previewstart, previewend, and previewstep data members while running under
      Screamernet would cause a crash.  These values, associated with an interface that doesn't
      exist under the network renderer, are now mapped to renderstart, renderend, and renderstep
      under Screamernet.

LSIDE Editor v1.3 Release Notes

| New Features | (top)

      The LightWave LCore subsystem (which houses, among other things, LScript) has a new
      interface mechanism that allows LScript to be embedded within applications that link
      to it (currently, only internal LightWave applications can take advantage of this).

      The LSIDE Editor is the first client to use the new Embedded LScript mechanism of
      LCore.  A new option under the "Tools" menu called "Macro..." provides an interface
      to the management system of the Editor macros.  Selecting this menu entry opens
      a window that can be used to manage Editor macros.  Macro files have the file
      extension "els".

      A third pop-up menu has been added to the Editor's on the top-right side of the
      interface.  It contains currently loaded macros, and is used for execution.

      The Editor supports two kinds of macros, run-once or key-based.  Run-once macros are much
      like Generic scripts in LightWave.  They execute once when invoked, and complete their
      processing when they exit.  Key-based macros (hereafter referred to as "filter" macros)
      are similar to other LightWave scripts, such as Master or Motion, in that they sit idle
      in the background when activated until either called upon to process a keystroke, or
      explicitly deactivated.

      Because the Editor's macro system is hosted by LScript, all non-LightWave-specific
      functionality offered by LScript is also available to any application using Embedded
      LScript.  This extends not only to language features, but also to certain generic built-
      in functions.

      However, an application employing Embedded LScript can define its own set of context-
      specific functions for use in its scripts.  Below is a complete list of the context
      functions defined by the LSIDE Editor.

            <doc> newDoc([<filename>])

                This function creates a new document in the editor.  If a valid
                filename is provided, then that file will be loaded into the new
                document.  Otherwise, and empty document is generated.

            <doc> currentDoc([<doc>])

                A handle to the current document is returned by this function.
                If a handle is provided as an argument, then that document will
                become the focus in the editor.

            <doc> nextDoc(<doc>)

                This function will return a handle to the document in the system
                that follows the provided document handle (based on load order).

            saveDoc(<doc>[,<filename>])

                This function will return a handle to the document in the system
                that follows the provided document handle (based on load order).

            <integer> lineCount(<doc>)

                Returns the number of the lines in the provided document handle.

            <string> getLine(<doc>,<line>)

                Returns the content of the indicated line number in the specified
                document handle.

            <color[]> getColor(<doc>,<line>)

                Returns the color representation of indicated line number in the
                specified document handle.  The returned value is an array of RGB
                color values in vector form.

            addLine(<doc>,<string>[,<after>])

                Adds the provided string text to the indicated document handle.
                If the optional line number is included, then the line will be
                added after that line in the document.  Otherwise, the line will
                be appended to the end of the document.

            deleteLine(<doc>,<line>)

                Removes the indicated line number from the specified document
                handle.

            deleteSelection(<doc>)

                Removes any currently selected text in the specified document
                handle.

            replaceLine(<doc>,<line>,<string>)

                Replaces the contents of the indicated line number with the
                provided text.

            replaceColor(<doc>,<line>,<color[]>)

                Replaces the color contents of the indicated line number with
                the provided array of color vectors.

            markLine(<doc>,<line>)

                Alters the representation of the document in the editor to
                place marker to the left of the indicated line number.  This
                is identical in effect to the "Mark All Matches" setting of
                the Search dialog.

            <Boolean> isDirty(<doc>)

                Allows the script to check the indicated document handle to
                see if it has been modified (i.e., is in need of saving).

            <type> docType(<doc>[,<type>])

                Returns the type of the document handle.  It will be one of
                DOC_SCRIPT, DOC_CSOURCE, DOC_CHEADER, or DOC_OTHER.
                Additionally, you can provide a type designation to alter the
                type of the document.  (Type designation is largely important
                for syntax hightlighting.)

            <string> docName(<doc>[,<fullpath>])

                Returns the name of the indicated document.  If the document
                has no name (i.e, it has not yet been saved to disc), 'nil'
                will be returned.  You can also retrieve the full path name
                of the document if you provide a Boolean true as the second
                argument.

            highlightSyntax(<doc>)

                Activates syntax highlighting for the specified document.

            (<row>,<col>) getCursor(<doc>)

                Returns the cursor's current row and column position in the
                specified document.

            setCursor(<doc>,<row>,<col>)

                Positions the cursor at the specified row and column in the
                specified document.

            <Boolean> selection(<doc>)

                Queries the specified document to see if it has an active text
                selection.

            (<row>,<col>) getSelect(<doc>)

                Returns the selection's current row and column position.  When
                a selection is active, its range is increased or decreased by
                movement of the cursor.  Thus, the values returned by this
                function are the "anchor point" of the selection.

            (<start>,<end>) getLineSelect(<doc>,<line>)

                Returns the starting and ending column of the portion of the
                indicated line that is included in the document's current
                selection.  This range can encompass the entire line, or just
                a portion depending upon the position of the cursor and
                selection anchor.

            setSelect(<doc>,<row>,<col>)

                Establishes the row/column position of the selection anchor
                point in the indicated document.

            <Boolean> selected(<doc>,<line>)

                Queries indicated line in the specified document to see if it
                is included (whether wholly or in part) in the document's current
                selection.

            <integer> countChar(<doc>,<char>[,<include>])

                Scans the indicated document, counting the occurances of the
                provided character.  If the optional include argument is a
                Boolean true, then character strings (i.e., sequences of
                characters enclosed in quotation marks) are also scanned and
                counted.

            message(<string>)

                Allows the script to display a text message in the message area
                of the editor's interface.

            <Boolean> inString(<doc>,<row>,<col>)

                Indicates whether or not the specified row and column position in
                the document falls within a character string (i.e., a sequence of
                characters enclosed in quotation marks).

            <Boolean> overwrite(<doc>[,<overwrite>])

                Returns the read-only status of the indicated document, where a
                Boolean true indicated read-write.  The read-only status of the
                document can be set if you provide a Boolean false as an optional
                argument.

            <string[]> funcNames(<doc>)

                Returns the names of the functions that currently exist in the
                specified document.  The document must be of type DOC_SCRIPT and
                functions must have been defined, or 'nil' is returned.

            <Boolean> funcIsCollapsed(<doc>,<name>)

                Returns the collapse status of the named function in the specified
                document.  A Boolean true indicates a collapsed state.

            toggleCollapse(<doc>,<name>)

                Expands or collapses the function body identified by the named
                function in the specified document.  Use funcIsCollapsed() to check
                the function state before calling this funciton.

            <integer> funcLine(<doc>,<name>)

                Returns the line number of the named function in the specified
                document.  If the named function does not exist, then 'nil' is
                returned.

      The following functions are available only to filter macros:

            replaceKey(<key>)

                Substitutes the provided key value for the one currently being
                processed.  All subsequent key filters will receive this
                replacement key value when they are invoked instead of the
                original.  This new key will also be the value passed on to
                the editor, if indicated.

      All macro types have a central point of entry called "macro".  This is where execution of
      the Editor macro begins, and, in the case of run-once macros, terminates when the macro()
      function completes.

            macro
            {
                ...
            }

      Filter macros provide an additional user-defined function, called filter(), that serves
      two purposes.  First, its presence identifies the macro as a key-based filter.  Second,
      it acts like a LightWave LScript flags() function and returns one or more characters
      for which it should be activated.  For this latter functionality, the filter() function
      can return a combination of character strings and integer values that will map to
      the required key values:

            filter
            {
                return("\ta",13);    // trap tabs, lower-case 'a', and the Return key
            }

      The filter() function will be invoked by the Editor's macro system each time the
      macro is activated.  This can occur when the macro is first loaded at startup (if
      it was active during your last session), or when you explicitly activate the macro
      from the interface.  For this reason, it serves as a good location to initialize
      the state of the macro, if needed.

            filter
            {
                pastPartials = nil;  // reset for a new run...
                return("\ta",13);    // trap tabs, lower-case 'a', and the Return key
            }

      The macro() function of a filter macro can also accept one or two optional arguments which
      represent the active document and the key to be processed.  In filter macros where only a
      single key is being trapped, this second argument is unnecessary.  And, because the currentDoc()
      function will return a handle to the active document, getting it in the argument list is
      also redundant.  However, in cases where more than one key is being intercepted, or in the
      case where a filter macro is trapping all printable characters, these arguments can be useful
      (for determining the key that caused the event in the former case, and for execution speed
      in the latter).  The key argument provided is the integer value of the event key.

            filter
            {
                pastPartials = nil;  // reset for a new run...
                return("\ta",13);    // trap tabs, lower-case 'a', and the Return key
            }

            macro: doc, key
            {
                switch(key)
                {
                    case '\t':      // tab
                        ...
                        break;

                    case 'a':
                        ...
                        break;

                    case 13:        // Return
                        ...
                        break;
                }
            }

      Several LScript pragmas can be used by scripts excuted by Embedded LScript.  For instance,
      the @name pragma can (and must) be present in an Editor macro, and is used to set the name
      of the macro in the list on the user interface.

            @name Sort Ascending

            macro
            {
                ...
            }

      Any other LScript pragma not having to do directly with LightWave (@fpdepth, @define,
      @strict, etc.) can also be present in an Editor (Embedded LScript) macro.

      Here are some example macros that were created during the development of the Editor's
      macro sytem.  Some are practical, and some are useful only as examples of how to use
      some of the provided context functions:

            Name                Type            Description
            ------------------  --------------  --------------------------------------------
            autosave.els        filter          saves the current document after a specified
                                                number of keystrokes
            a_to_A.els          filter          uses the replaceKey() function to capatilize
                                                all typed lower-case 'a' characters
            cf_template.els     run-once        simple script that creates a new document and
                                                places a Channel Filter template into it
            changecase.els      run-once        alters the case of the characters in the document.
                                                adheres to any current selection.  employs the
                                                LScript requester mechanism.
            color_e.els         run-once        changes the color of all the 'e' or 'E' characters
                                                on the current line
            disablecode.els     run-once        places a single-line comment operator (//) at
                                                the start of every line in the current selection
            enablecode.els      run-once        removes any single-line comment operators from
                                                the start of every line in the selection
            keycomplete.els     filter          completes the keyword from a partial entry when
                                                the tab key is pressed
            reformat.els        run-once        re-formats a script to make it more readable
            smartindent.els     filter          automates code-based indentation in a smart
                                                fashion
            sort.els            run-once        sorts selected document lines in ascending order
      Document text can now be scrolled using the mouse wheel on mice that provide them.  In
      addition, the wheel provides acceleration scrolling.  Shorter times between individual
      wheel clicks will scroll more lines.
      File-open dialogs in the Editor are now multiple-select.

(LSIDE) | Behavioral Changes | (top)

      The Editor's undo system has been redesigned.  The redesign not only makes the undo
      mechanism function correctly, the addition of on-the-fly compression code now also
      allows for undo operations to be more numerous per document while consuming the same
      amount of memory.

      Instead of being limited to a specific number of events, the undo system now limits
      undo events to a cumulative memory size (500K per document in this release).  When this
      limit is exceeded by a change in the document, the oldest undo events are discarded
      until the cumulative memory consumption of undo events falls again below the specified
      maximum.  The actual number of events discarded will depend upon their particular
      memory footprint in this undo pool.
      The replace mechanism now functions strictly within a selection range if one has
      been applied to the document.

Expressions

| New Features | (top)

      Employing Embedded LScript, users can now write their own functions for use with LightWave
      Expressions.

      Expressions UDFs are used just like any built-in Expressions function.  Parameter passing
      is limited to simple data types--strings, numbers and vectors.  As long as an expression
      evaluates to one of these data types, it can be used as an argument.

      Expressions UDFs are stored in the \NewTek\LScripts directory within their own directory
      called "expressions".  A default library of functions can be maintained within this
      directory called "library.ls".  This library of functions is automatically loaded into the
      Expressions engine when LightWave is initialized, and its defined functions are consequently
      available to any LightWave expression or Expressions UDFs that references them.

      Additionally, individual Expressions UDFs can be stored in their own files in this same
      directory.  The name of the file containing the UDF must exactly match that of the function
      name being referenced.  The file may contain any number of other UDFs to support the main
      function, but must contain at least a UDF whose name and argument count matches that being
      referenced in the expression.

      Data exchange between UDFs is not limited in their types.  UDF-to-UDF calling is exactly
      the same as it is in LScript.

      By way of example, assume the following files exist in the required directory:

            \NewTek\LScripts\expressions\library.ls
            \NewTek\LScripts\expressions\channelValue.ls

      The "library.ls" file contains the following content:

            locate_channel: fullchannel
            {
                parts = parse(".",fullchannel);

                group = ChannelGroup(parts[1]);  // start with root channel group
                lastgroup = group;
                subgroup = nil;
                x = 2;

                while(group)
                {
                    // scan sub-groups to match parts[x]
                    // if a match can't be found, then it
                    // is probably the start of the channel
                    // name

                    subgroup = ChannelGroup(group,subgroup);
                    last if !subgroup;

                    if(subgroup.name == parts[x])
                    {
                        group = subgroup;
                        lastgroup = group;
                        subgroup = nil;
                        ++x;
                    }
                }

                if(!lastgroup) return(nil);

                // anything left in the parts[] array are the components
                // of the channel name itself.  put them together for
                // channel searching

                channelname = "";   // avoid creating an array
                psize = parts.size();

                while(x <= psize)
                {
                    channelname += parts[x];
                    if(x < psize) channelname += ".";
                    ++x;
                }

                // scan the defined channels in the final group to see
                // if we can match the channel name

                chchannel = lastgroup.firstChannel();
                while(chchannel)
                {
                    last if chchannel.name == channelname;
                    chchannel = lastgroup.nextChannel();
                }

                return(chchannel);
            }

            // replace the built-in clamp() function

            clamp: val, lower, upper
            {
                                     result = val;
                if(val < lower)      result = lower;
                else if(val > upper) result = upper;

                return(result);
            }

      While the "channelValue.ls" file contains the following content:

            chan;
            chanName;

            channelValue: channel, time
            {
                if(chanName != channel) chan = nil;

                if(!chan)
                {
                    // cache the channel for speed
                    chan = locate_channel(channel);
                    if(!chan) return(0);
                    chanName = channel;
                }

                return(chan.value(time));
            }

      In Layout, you might then enter an expression like:

            clamp(channelValue("WashLight.Intensity",Time),0.0,1.0)

      This will invoke the channelValue() UDF, which then invokes the locate_channel() UDF
      (defined in the default library file "library.ls") to resolve a string channel reference to
      an actual LScript Channel Object Agent.  The channelValue() UDF returns the value of the
      specified Light Object's intensity value at the current time.  This value is then passed to
      the (script) clamp() UDF (also defined in "library.ls") to keep it in a specified range.

      Alternately, you could use the Graph Editor's direct channel reference syntax with the
      UDF call:

            clamp([WashLight.Intensity,Time],0.0,1.0)

      UDF references that have been loaded into the Expressions engine are automatically updated
      the next time they are evaluated when their respective files have been modified.  For instance,
      if you have expressions referencing channelValue(), altering the last line of the function to
      read:

                return(chan.value(time) + 1.0);

      will instantly return new values the next time the expression is evaluated (e.g., on the
      next frame).
      The existing Expressions log() function calculates the natural logarithm (base-e) of the provided
      value.  This can be a bit misleading for those seeking base-10 functionality, so a new function
      has been added, log10(), to address this.