Property Accessors

It is possible to expose C++ functions to maxscript interfaces as a property, rather an as a function. A LayerProperties interface for example has properties such as on, lock, current etc. that can be read and written to. These properties remain as C++ level functions, but are accessed as properties in MAXScript by using the standard dot-notation for members.

L = layermanager.getlayer 1
<MixinInterface:LayerProperties>
showinterface L
  Interface: LayerProperties
   Properties:
    .on : boolean : Read|Write
    .lock : boolean : Read|Write
    .current : boolean : Read|Write
    .wireColor : color by value : Read|Write
    .isGIExcluded : boolean : Read|Write
    ...
--For example
L.on
true
L.on = false
false

It is possible to define selected methods in an FPInterface as 'property accessor' methods so that dispatch-based clients of the interface may present these methods as properties instead of functions. A 'property' is typically defined by a pair of accessor methods, one for getting and one for setting, but you can also define read-only methods that have a single getter method. As an example, here's a rework of the previous Cylinder interface in which the radius and direction are made properties.

classCylInterface : public  FPMixinInterface
{
   ...
   virtualvoid RemoveCaps()=0;
   virtualvoid AddBend(float offset, float angle, float radius)=0;
   ...
   virtualfloat GetRadius()=0;
   virtualvoid SetRadius(float radius)=0;
   virtualPoint3 GetDirection()=0;
   virtualvoid SetDirection(Point3 dir)=0;
   ...

   BEGIN_FUNCTION_MAP 
     ... 
     VFN_0(cyl_removeCaps, RemoveCaps);
     VFN_3(cyl_addBend, AddBend, TYPE_FLOAT, TYPE_ANGLE, TYPE_FLOAT);
     ...
     PROP_FNS(cyl_getRadius, GetRadius, cyl_setRadius, SetRadius, TYPE_FLOAT);
     PROP_FNS(cyl_getDir, GetDirection, cy_setDir, SetDirection, TYPE_POINT3_BV);
     ...
   END_FUNCTION_MAP

    FPInterfaceDesc* GetDesc();
};

Each property has a getter and setter virtual method. Their signatures must conform to the convention shown, namely, the getter takes no parameters and the getter one. The getter returns the same type as the setter and the setter type is void. There are also variants that take an explicit time as described below.

Properties have single entries in the function map, using one of the property-related entry macros. In this case, PROP_FNS() takes a getter ID, getter function, setter ID, setter function and finally the property type code. There is also a RO_PROP_FN() macro for read-only properties that takes a getter ID, getter FN and prop type. Finally, the are variants of PROP_FNS and RO_PROP_FN that indicate the accessors take an explicit TimeValue argument, presumably associated with animatable properties and indicating that the property access should be at the given time. These macros are PROP_TFNS and RO_PROP_TFN and takes the same arguments as their corresponding base macros. If the above example properties were time-sensitive, the definitions would be as follows:

classCylInterface : public  FPMixinInterface
{
   ...
   virtualvoid RemoveCaps()=0;
   virtualvoid AddBend(float offset, float angle, float radius)=0;
   ...
   virtualfloat GetRadius(TimeValue t)=0;
   virtualvoid SetRadius(float radius, TimeValue t)=0;
   virtualPoint3 GetDirection(TimeValue t)=0;
   virtualvoid SetDirection(Point3 dir, TimeValue t)=0;
   ...

   BEGIN_FUNCTION_MAP 
     ... 
     VFN_0(cyl_removeCaps, RemoveCaps);
     VFN_3(cyl_addBend, AddBend, TYPE_FLOAT, TYPE_ANGLE, TYPE_FLOAT);
     ...
     PROP_TFNS(cyl_getRadius, GetRadius, cyl_setRadius, SetRadius, TYPE_FLOAT);
     PROP_TFNS(cyl_getDir, GetDirection, cy_setDir, SetDirection, TYPE_POINT3_BV);
     ...
   END_FUNCTION_MAP
    FPInterfaceDesc* GetDesc();
};

Recall that there are time-sensitive variants for the FN_0, FN_1, etc., macros, namely TFN_0, TFN_1, etc. Functions specified by such macros in the function map have an implicit TimeValue last parameter. Note that in all the time-sensitive variants, the last TimeValue parameter is not defined as an explicit parameter in the FPInterface descriptor entry for that function. Such time-sensitive properties and functions are handled specially in MAXScript. It supplies the current MAXScript time, as defined by the current 'at time' context for this implicit parameter, making these functions and properties behave consistently with the rest of MAXScript.

Properties are defined in a special section in the FPInterface descriptor constructor following the function definitions, headed by the special tag 'properties'. They are also entered in the function map using new property-specific FUNCTION_MAP macros. Here's an example descriptor fragment:

staticFPInterfaceDesc cylfpi (
   CYL_INTERFACE, _T("cylMixin"), 0, &cylinderDesc, FP_MIXIN,
   ...
   cyl_removeCaps, _T("removeCaps"), 0, TYPE_VOID, 0, 0,
   cyl_addBend, _T("addBend"), 0, TYPE_VOID, 0, 3,
   _T("offset"), 0, TYPE_FLOAT,
   _T("angle"), 0, TYPE_ANGLE,
   _T("radius"), 0, TYPE_FLOAT,

   properties,
   cyl_getRadius, cyl_setRadius, _T("radius"), IDS_RADIUS,
   TYPE_FLOAT, f_range, 0.0, 10000.0,
   cyl_getDirection, cyl_setDirection, _T("direction"), IDS_DIR,
   TYPE_POINT3_BV,
   ...
   end
);

The 'properties' section follows the function definitions. Each propery has a single entry defining the function IDs for the getter and setter functions, a fixed internal property name, a descriptor string resource ID and the property type. If the property is read-only and there is no setter function, specify FP_NO_FUNCTION for the setter ID. Each property definition can optionally be followed by parameter validation options, as described in the Parameter Validation section above, and these apply to the parameter given to the setter function during property assignment. In this case, the radius is range checked.

If you need to get at it, the property metadata is accessible in the 'props' data member in an FPInterfaceDesc. It is a Tab<> of pointers to FPPropDef class instances, which contain individual property metadata. See the FPPropDef class definition in iFnPub.h for details.

Maxscript Convention

MAXScript now exposes properties defined in this way as direct dot-notation properties on the interface. So, where before the Get/SetRadius in the example cylinder mixin would have been accessed as functions:

r = $cyl01.cylMixin.getRadius()
$cyl01.cylMixin.setRadius 23

you now can use:

r = $cyl01.cylMixin.radius
$cyl01.cylMixin.radius = 23

This is true for all the types of FP interfaces that turn up in MAXScript, static, mixin and core. As a futher optimization, MAXScript now effectively promotes all interface methods and properties to the level of the interface, so if individual methods and properties have unqiue names within all the interfaces of an object or

class, you can eliminate the interface name. The above examples could now be written:

r = $cyl01.getRadius()
$cyl01.setRadius 23

and:

r = $cyl01.radius
$cyl01. radius = 23

If there is a naming conflict, you can always include the interface name level to resolve this.