# LScript v2.3 Release Notes

## New Features

      The Scene Object Agent now provides information pertaining to the state of the Layout's
      dynamic update setting.  This information is available in the dynamicupdate data member,
      and will be one of the following:

            DYNUP_OFF
            DYNUP_DELAYED
            DYNUP_INTERACTIVE
      A new method is available in the Scene Object Agent to determine the display state of
      an object in the scene.  Given either the name of an existing object, or that object's
      proxy, the visibility() method will return one of the following indicators:

            VIS_VISIBLE
            VIS_HIDDEN
            VIS_BOUNDINGBOX
            VIS_VERTICES
            VIS_WIREFRAME
            VIS_FFWIREFRAME
            VIS_SHADED
            VIS_TEXTURED
      A new method exists in the Scene Object Agent to retrieve the X and Y position of an
      object's entry in Layout's schematic view.  Provided a name or proxy, the schemaPosition()
      method will return two floating-point values representing this position.

      Coupled with the SchematicPosition() Command Sequence function, a script can manage
      the complete arrangement of Layout's schematic viewport.
      The displayopts[] data member, belonging to the Scene Object Agent, provides information
      regarding display-related settings in Layout.  The following are the specific elements
      in the array:

            displayopts[1]      Boolean 'true' if Motion Paths are displayed
            displayopts[2]      Boolean 'true' if Handles are displayed
            displayopts[3]      Boolean 'true' if IK Chains are displayed
            displayopts[4]      Boolean 'true' if Cages are displayed
            displayopts[5]      Boolean 'true' if Safe Areas are displayed
            displayopts[6]      Boolean 'true' if Field Charts are displayed
      The new generalopts[] Scene Object Agent data member provides information regarding
      general settings in Layout.  The following are the specific elements in the array:

            generalopts[1]      Boolean 'true' if Hide Tool Bar is enabled
            generalopts[2]      Boolean 'true' if Right Tool Bar is enabled
            generalopts[3]      Boolean 'true' if Parent In Place is enabled
            generalopts[4]      Boolean 'true' if Fractional Frame is enabled
            generalopts[5]      Boolean 'true' if Keys In Slider is enabled
            generalopts[6]      Boolean 'true' if Play Exact Rate is enabled
      Layout Object Agents export a new data member, visibility, that contains one of the
      values returned by the Scene Object Agent's visibility() method.
      A new schemaPosition() method has been added to Layout Object Agents that will return
      two floating-point values that represent the object's position in Layout's schematic
      viewport.
      Both the Scene and Layout Object Agents export a new method called server().  This method
      will return the name of an active plug-in applied to the object.  The first argument is
      the plug-in class to be queried.  The second is an optional index value for selecting
      a specific plug-in.

      The plug-in class can be a literal string (like "ItemMotionHandler"), or you can use
      one of the following pre-defined constants:

            SERVER_ANIMLOADER_H         SERVER_ANIMLOADER_I
            SERVER_ANIMSAVER_H          SERVER_ANIMSAVER_I
            SERVER_CHANNEL_H            SERVER_CHANNEL_I
            SERVER_CUSTOMOBJ_H          SERVER_CUSTOMOBJ_I
            SERVER_DISPLACEMENT_H       SERVER_DISPLACEMENT_I
            SERVER_ENVIRONMENT_H        SERVER_ENVIRONMENT_I
            SERVER_IMAGEFILTER_H        SERVER_IMAGEFILTER_I
            SERVER_PIXELFILTER_H        SERVER_PIXELFILTER_I
            SERVER_FRAMEBUFFER_H        SERVER_FRAMEBUFFER_I
            SERVER_MASTER_H             SERVER_MASTER_I
            SERVER_ITEMMOTION_H         SERVER_ITEMMOTION_I
            SERVER_OBJREPLACEMENT_H     SERVER_OBJREPLACEMENT_I
            SERVER_SHADER_H             SERVER_SHADER_I
            SERVER_TEXTURE_H            SERVER_TEXTURE_I
            SERVER_VOLUMETRIC_H         SERVER_VOLUMETRIC_I

      Where _H indicates a Handler-type server (i.e., "ItemMotionHandler") and _I indicates
      an Interface-type server (i.e., "ItemMotionInterface").

      Certain classes of plug-in are itemless, such as Image Filter and Master.  While you can
      provide these class types to a Layout Object, you will only get a valid return value if
      you use these with the Scene Object Agent (and, of course, plug-ins of that type are
      active).  Likewise for classes that are associated with objects; they will not
      return valid values with the Scene Object Agent.
      The sort methods (sortA() and sortD()) now support Point and Polygon identifiers when
      encountered in arrays.
      A new integral data type method called reduce() has been added.  This method will remove
      duplicate values from the data type (useful mostly for strings and arrays).  Duplicate values
      are expected to be in linear order--i.e., adjacent to one another.  You should probably
      employ one of the sort methods before invoking reduce().

      The following is an example of using the reduce() method.  A linear series of Polygon
      identifiers are gathered into an array in a situation where there is a high probablity of
      duplication.  Subsequent calls to sortA() and reduce() remove any duplicate entries from
      the resulting array:

            ...
            for(x = 1;x <= pointCnt;x++)
            {
                rawPolys = points[x].polygon();
                polyCnt = rawPolys.count();

                for(y = 1;y <= polyCnt;y++)
                    polySelect += rawPolys[y];
            }

            polySelect.sortA();
            polySelect.reduce();
            ...
      The Requester mechanism supports an optional idle-time callback.  The reqpost()/reqopen()
      functions will now accept up to two new arguments to enable idle-time processing.  The first
      argument is the name of the UDF to be invoked.  The UDF takes no arguments, and is enabled to
      allow for drawing on the Requester panel.  The second optional argument is the timeout interval
      (in milliseconds) to be used between calls to the idle-time UDF.  If ommitted, the default
      timeout value is 500 milliseconds (one-half of a second).

            generic
            {
                reqbegin("Testing Idle");

                c1 = ctlnumber("Number",1.0);

                reqpost("idleTest");   // called at 500-millisecond intervals
                reqend();
            }

            color = <0,255 * .5,255 * .75>;

            idleTest
            {
                drawline(color,0,2,100,2);

                color.x += 5;
                color.y += 5;
                color.z += 5;

                if(color.x > 255) color.x = 0;
                if(color.y > 255) color.y = 0;
                if(color.z > 255) color.z = 0;
            }

      Certain conditions may prevent the system from invoking the UDF at exact interval times.
      Thus, the timeout value specified should be considered only an approximation--in other words,
      you should not make the assumption that only that amount of time has elapsed when your
      idle-time UDF is invoked.
      The Requester control identifier value, returned by functions like ctlstring() and
      ctlnumber(), has been promoted to valid LScript data type status.  This ascension has
      enabled the new Control data type to sport its own set of object attributes.  Below
      are the initial set of methods and data members.  The attribute set may be expanded
      in future releases of LScript.

      The new methods exported by the new Control data type:

            active([<Boolean>])

                This method sets the immediate active state of the
                control.  An optional Boolean value indicates whether
                or not the control should be active.  If ommitted,
                an implicit 'true' value is assumed.

            visible([<Boolean>])

                This method sets the immediate visible state of the
                control.  An optional Boolean value indicates whether
                or not the control should be visible.  If ommitted,
                an implicit 'true' value is assumed.

            position(<column>,<row>)

                Sets the location of the control on the open Requester
                panel to the specified column/row.  The re-positioning
                action is immediate.  You should use the visible() method
                before and after this call to ensure a clean move of
                the control.

      Data members exported by the Control data type:

            value

                This data member holds the context value display
                by the control.  Reading from and writing to this
                value is equivalent to using the setvalue() and
                getvalue() functions, respectively, on the control
                identifier.

            active                              (read-only)

                Returns a Boolean value indicating the current
                active state of the control, where a 'true' value
                indicates that the control is currently active and
                can interact with the user.

            visible                             (read-only)

                Returns a Boolean value indicating the current
                visible state of the control, where a 'true' value
                indicates that the control is currently visible
                on the Requester panel.

            x                                   (read-only)

                Contains the control's current X position on the
                Requester panel.

            y                                   (read-only)

                Contains the control's current Y position on the
                Requester panel.

            w                                   (read-only)

                Contains the control's width

            h                                   (read-only)

                Contains the control's height
      A new function has been added to the Requester drawing toolbox.  The drawerase() function
      takes the the X and Y position, along with the width and height, of the area to be erased
      on the Requester panel.

            ...
            drawerase(10,10,50,25);
            ...
      Requester panels can now be interactively resized by the user if an internal resize callback
      is defined within the script using the new reqresize() function.  This function takes up to
      five arguments, the first, a required string value, should identify a UDF within the script.
      This UDF should accept two integer arguments, indicating the new width and height of the
      Requester panel, and should return nothing.

      Two optional pairs of numbers can also be provided to indicate the the minimum width and
      height, and maximum width and height, for the panel.  If ommitted, the minimum width and
      height becomes that of the initial window, and the maximum width and height is limited only
      by the screen resolution.

      The following Generic script illustrates the use of the new resize mechanism by locking a
      control to a specific location on the panel, regardless of its size:

            c1;

            generic
            {
                reqbegin("Resize Me!");

                c1 = ctlnumber("Number",1.0);

                reqresize("resize",,,640,480);  // maximum becomes 640x480

                reqpost();
                reqend();
            }

            resize: w,h
            {
                c1.position(w - c1.w - 5,h - c1.h - 35);
            }

      Resizing of Requester panels is only enabled when the reqresize() function successfully
      identifies a callback UDF.  If reqresize() is not used, then the panel is locked to its
      initial size.
      LScript sports a new plug-in class, Custom (Object).  Like Item Animation, Custom Object
      can take an optional argument to its create() UDF that is an Object Agent for the object
      to which this script is assigned.  Custom Object also supports the init(), newtime() and
      cleanup() UDFs.

      The Custom Object process() UDF takes a single argument which is a Custom Object Agent
      that provides an interface to the underlying Custom Object functionality.  It exports
      the following data and methods:

            view

                a read-only data member that indicates which
                view drawing will effect.  It will contain one
                of the following:

                    VIEW_ZY
                    VIEW_XZ
                    VIEW_XY
                    VIEW_PERSP
                    VIEW_LIGHT
                    VIEW_CAMERA
                    VIEW_SCHEMA

            flags[]

                read-only array of settings for the current Custom
                instance.  it only has a single element right
                now (flags[1]) which will be 'true' if the Custom
                object should be drawn in a selected state, or
                'false' otherwise.

            setColor(<r,g,b>|r,g,b[,a])

               sets the color for subsequent drawing actions.

            setPattern(pat)

               sets the drawing pattern.  solid by default, or
               can be one of the following:

                    PATTERN_SOLID    (or "SOLID")
                    PATTERN_DOT      (or "DOT")
                    PATTERN_DASH     (or "DASH")
                    PATTERN_LONGDOT  (or "LONGDOT")

            drawPoint(<pos>[,coord])

               draw a point into the current view at the vector
               location <pos> using the previously set color and
               line pattern.  The coord parameter is optional
               (SYS_OBJECT is the default), and can be one of
               the following:

                    SYS_WORLD        (or "WORLD" or "GLOBAL")
                    SYS_OBJECT       (or "OBJECT" or "LOCAL")
                    SYS_ICON         (or "ICON")

            drawLine(<pos1>,<pos2>[,coord])

               draws a line segment from vector <pos1> to
               vector <pos2>.

            drawTriangle(<pos1>,<pos2>,<pos3>[,coord])

               draws a triangle whose end points are vector <pos1>,
               vector <pos2>, and vector <pos3>.

            drawCircle(<pos>,rad[,coord])

               draws a circle whose center point is vector <pos>,
               with a radius of rad.

            drawText(<pos>,text[,coord,[align]])

               draws the provided text string at the vector
               location <pos>.  the align argument can be one
               of the following:

                    LEFT             (or "LEFT")
                    CENTER           (or "CENTER")
                    RIGHT            (or "RIGHT")

      The plug-in API does not maintain state information between evaluations, and neither does
      LScript.  This means that every time the process() UDF is called, the color is set back
      to that established for the object in the Layout interface, and the drawing pattern is
      always PATTERN_SOLID.

      Here is a short Custom Object LScript that duplicates the "barn" sample provided by
      Ernie in the LightWave SDK:

            @warnings
            @script custom

            vert = @ <0.0,  0.0,  0.0>,  <1.0,  0.0,  0.0>,  <1.0,  1.0,  0.0>,
                     <0.5,  1.5,  0.0>,  <0.0,  1.0,  0.0>,  <0.0,  0.0, -1.0>,
                     <1.0,  0.0, -1.0>,  <1.0,  1.0, -1.0>,  <0.5,  1.5, -1.0>,
                     <0.0,  1.0, -1.0> @;

            edge = @ 1, 2,  2, 3,  3, 4,   4, 5,  5, 1,  6, 7,
                     7, 8,  8, 9,  9, 10,  10, 6, 1, 6,  2, 7,
                     3, 8,  4, 9,  5, 10,  2, 5,  1, 3 @;

            process: ca
            {
               for(x = 1;x < 31;x += 2)
                    ca.drawLine(vert[edge[x]],vert[edge[x+1]]);

               ca.setPattern("dot");

               for(x = 31; x < 35;x += 2)
                    ca.drawLine(vert[edge[x]],vert[edge[x+1]]);
            }
      LScript now offers a new Object Agent for Layout scripts:  Particle.

      This Object Agent provides an interface to the underlying plug-in API Particle System
      services.  Specific documentation for that global service can be found in the LightWave
      SDK docs.

      A Particle Object Agent instance is created using the constructor Particle().  This
      constructor can be used to create a new instance of a Particle database, or it can be
      used to acquire an existing Particle System, depending upon the arguments provided.

      If a valid Layout Object Agent is provided to the constructor, then a list of the
      Particle systems active on that object are returned.  If no Particle systems are
      enabled for that object, then 'nil' is returned.

            ...
            obj = Mesh("Cow");
            part = Particle(obj) || error("No particle systems active!");
            ...

      When creating a new Particle system, you must specify the type of the particles and
      provide a list of the buffers that are to be created and maintained for each particle.
      Particle types can be one of PART_PARTICLE (single particle) or PART_TRAIL (particle with
      tail).  As with most constants in LScript, these can also be specified using the string
      equivalents "particle" and "trail".

      The buffers that can be allocated for each particle are as follows:

            PART_POSITION     (or "position")
            PART_SIZE         (or "size")
            PART_SCALE        (or "scale")
            PART_ROTATION     (or "rotation")
            PART_VELOCITY     (or "velocity")
            PART_AGE          (or "age")
            PART_FORCE        (or "force")
            PART_PRESSURE     (or "pressure")
            PART_TEMPERATURE  (or "temperature")
            PART_MASS         (or "mass")
            PART_LINK         (or "link")
            PART_ID           (or "id")
            PART_ENABLE       (or "enable")
            PART_RGBA         (or "rgba")
            PART_COLLISION    (or "collision")

      Particles can exist in one of three states: PART_ALIVE, PART_DEAD, or PART_LIMBO.  These
      values are used when reading/writing the PART_ENABLE buffer.

      The following methods and data members are exported by the Particle Object Agent:

            count

               returns the current number of particles (integer) defined
               in the instance.

            save()

               writes particle-specific data during the save state

            load()

               loads particle data during the load state

            attach(Object Agent)

               attaches the particle instance to a Layout Object Agent
               so systems like HyperVoxels can access the particle data

            detach(Object Agent)

               disassociates the particle instance from its object

            reset()

               clears all particles from the instance

            addParticle()

               generates a new particle in the instance.  the index
               value of the new particle is returned.

            remParticle(index)

               removes the indicated particle index from the instance

            setParticle(index,bufid,value)

               sets the data for the specified particle buffer for the
               indicated particle index.

            getParticle(index,bufid)

               retrieves the value in the specified particle buffer for
               the indicated particle index.

      Here is the LScript equivalent of the particle SDK example.  You can test it by applying
      it to an object, and then enabling that object in HyperVoxels:

            @warnings
            @version 2.3
            @script displace

            part;

            create: id
            {
                part = Particle("particle",@"position","size","enable","rgba"@);
                part.attach(id);
            }

            newtime: id, frame, time
            {
                part.reset();
            }

            flags { return(WORLD); }

            process: da
            {
                i = part.addParticle();
                part.setParticle(i,"position",<da.oPos[1],da.oPos[2],da.oPos[3]>);
                part.setParticle(i,"rgba",100,150,200,255);
                part.setParticle(i,"enable",PART_ALIVE);
            }
      The following Command Sequence functions have been added to the Layout LScript
      programming environment, sychronizing the system to LightWave Layout 525:

            DynamicUpdate(1-3)
            BoundingBoxThreshold(threshold)
            AddPosition(<x,y,z>|x,y,z)
            AddRotation(<h,p,b>|h,p,b)
            AddScale(<x,y,z>|x,y,z)
            RadiosityIntensity(0.0-1.0)
            CausticIntensity(0.0-1.0)
            ZenithColor(<r,g,b>|r,g,b)
            SkyColor(<r,g,b>|r,g,b)
            GroundColor(<r,g,b>|r,g,b)
            NadirColor(<r,g,b>|r,g,b)
            EditServer(class,index)
            BoneFalloffType(1-7)
            ShadowMapSize(16-32000)
            ShowTargetLines()
            MasterPlugins()
            Generics()
            ClearAudio()
            LoadAudio()
            PlayAudio()
            ReplaceObjectLayer(layer,filename)
            HStiffness(stiffness)
            PStiffness(stiffness)
            BStiffness(stiffness)
            ItemActive(Boolean)
            ItemLock(Boolean)
            RadiosityType(1-3)
            AddButton(command,group)
            LoadObjectLayer(layer,filename)
            LightQuality(1-5)
      A new pragma called @name has been added to the pre-processor.  The value of this
      pragma is a display name for the script.  This value will be used by the LightWave
      LCore subsystem to provide a menu display name for the script when it is installed
      as a plug-in.  In addition, it will be used as a backup value for those scripts
      who have the capability to call the identification function setdesc(), but do not.

            @warnings
            @script generic
            @name Bone Cloner
            ...

      If specified, script names are also now embedded into compiled LScripts, providing
      them with the same naming capabilities when installed.
      The VMap Object Agent now supports creation of user-defined vertex maps in Modeler.

      The method used is nearly identical to that of creating a new vertex map from a
      pre-defined type.  In order to generate a custom vertex map, instead of using one
      of the pre-defined type identifiers, you must provide a custom map type.  Custom
      map types are a unique four-character sequence, housed in an initialization block.

      For example, to generate a vertex map of type NMBK, named my_normals, with
      two data elements per point:

            ...
            @define VMAP_NMBK  @'N','M','B','K'@
            ...
            normMap = VMap(VMAP_NMBK,"my_normals",2);

      Be aware that custom vertex maps will not appear anywhere in the application's
      interface.  They are of types not known to the application, so no allocation is
      made for them.  They will, however, be stored into the object file when it
      is saved, and will continue to be transported within the object file.

      The VMap() constructor will return 'nil' if it fails to generate the new vertex
      map, regardless of its mode (custom or pre-defined).

## Behavioral Changes

      Requester panels now adhere to the practice of mapping the Enter key to the 'Ok' button,
      and the Escape key to the 'Cancel' button (if it exists).
      The use of the LoadScene() and ClearScene() Layout CS commands are now disallowed in
      Master scripts that are functioning in SCENE mode.  Either of these commands can clear
      the script, potentially leaving LScript in an unstable state.
      The Channel Object Agent's setKeyTime() method has been removed from the object.  Key time
      indices are read-only, so this method has no purpose.
      The ctlfilename() function's default behavior is now to post a Save dialog when its selection
      button is pressed.  An optional Boolean true value as a fourth argument will alter this
      default behavior and post a Load dialog instead.  This change was made for the Mac where
      Save and Load dialogs have distinctly different functionality.
      The getfile() function now follows the same behavioral changes as ctlfilename().

## Bug Fixes

      The AddEnvelope() and RemoveEnvelope() commands were internally wrapping the
      envelope name in quotation marks, which caused Layout some heartburn.
      The ctlposition() function was not accepting the correct number of arguments
      when used in Modeler scripts.
      The polygon() method and polygon data member of the Point integral data type were
      not implemented correctly, resulting in incorrect return values.
      The run-time system was not managing short numeric types when a need existed to
      swap bytes--such as when running an Intel-compiled script on the Macintosh.  This
      lead to some odd error messages and occasional crashes.
      The sort methods (sortA() and sortD()) were not properly handling strings.  A
      new sorting algorithm has been implemented to address this.
      The pre- and post-increment and pre- and post-decrement operators were not being provided
      the correct internal pointer types when applied to an object's data member.
      The VMap setValue() method was suffering from a lack of clarity in my understanding of the
      Vertex Map access functions (and a lack of consolidation of that particular API interface
      in the SDK).  This caused a crash on second and subsequent calls for the same VMap Object
      Agent.
      Associative assignments were not working correctly when the data being assigned was
      multiple-return Object Agents.
      The CLEAR action for selpoint() and selpolygon() would not clear specific points or
      polygons from the current selection when provided as arguments using the ID or NDX
      flags.
      The image offset values provided to ctlimage() were not being processed correctly.
      The vertical selection argument to the ctlchoice() function has been enabled.
      A re-entrancy problem existed when Generic scripts invoked Command Sequence functions that
      then invoked other Generic scripts as part of their processing.  This has been addressed.