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.