Difference between revisions of "Item orb"

From The Foundry MODO SDK wiki
Jump to: navigation, search
(Created page with "Item_orb.cpp is a basic example plugin. This wiki page is intended as a walkthrough of the code in order to help you better understand the SDK. ==Functionality== Item_orb.cp...")
 
Line 1: Line 1:
 
Item_orb.cpp is a basic example plugin. This wiki page is intended as a walkthrough of the code in order to help you better understand the SDK.
 
Item_orb.cpp is a basic example plugin. This wiki page is intended as a walkthrough of the code in order to help you better understand the SDK.
 
==Functionality==
 
  
 
Item_orb.cpp is a plugin that adds the orb item to modo's toolkit.
 
Item_orb.cpp is a plugin that adds the orb item to modo's toolkit.
  
 +
[[File:Orbshot.png]]
 +
 +
Orb Item pictured above
  
 
==Code Walkthrough==
 
==Code Walkthrough==

Revision as of 17:10, 10 September 2013

Item_orb.cpp is a basic example plugin. This wiki page is intended as a walkthrough of the code in order to help you better understand the SDK.

Item_orb.cpp is a plugin that adds the orb item to modo's toolkit.

Orbshot.png

Orb Item pictured above

Code Walkthrough

Class Declarations

class COrbLog : public CLxLuxologyLogMessage
{
   public:
       COrbLog () : CLxLuxologyLogMessage ("orb-item") { }

       const char *	 GetFormat  () { return "Orb Object"; }
};

We want this class to be able to write out to the log, so we inherit from CLxLuxologyLogMessage.

class COrbPart {

   public:
       /*
        * Tableau Surface implementation.
        */
       LxResult	Bound (LXtTableauBox bbox);
       unsigned	FeatureCount (LXtID4 type);
       LxResult	FeatureByIndex (
                               LXtID4		 type,
                               unsigned int	 index,
                               const char	**name);
       LxResult	SetVertex (ILxUnknownID vdesc);
       LxResult	Sample (
                               const LXtTableauBox	 bbox,
                               float			 scale,
                               ILxUnknownID		 trisoup);

       /*
        * Part configuration.
        */
       void		SetPart (unsigned part);

       /*
        * Part. See the GearPart enum.
        */
       unsigned		 m_part;
       unsigned		 hasUVs;
       CLxUser_TableauVertex	 vrt_desc;

       /*
        * Indices for vertex feature subscripts.
        */
       enum
       {
               FEATURE_POSITION,
               FEATURE_OBJECT_POSITION,
               FEATURE_NORMAL,
               FEATURE_VELOCITY,
               BASE_FEATURE_COUNT
       };

       enum
       {
               FEATURE_UV	= BASE_FEATURE_COUNT,
               FEATURE_DPDU,
               TOTAL_FEATURE_COUNT
       };

       int			 f_pos[TOTAL_FEATURE_COUNT];

       LxResult	InitializePart (
                               CLxUser_Item		&m_item,
                               CLxUser_ChannelRead	&chanRead);

       LxResult	InitializePart (
                               double		coverage,
                               double		radius);

       /*
        * Construction channels.
        */
       double			 latitude_coverage;
       double			 longitude_coverage;

       float			 m_radius;

   private:
       LxResult	 SampleOrbPartShell (
                               CLxUser_TriangleSoup	&soup,
                               unsigned		&segmentID);

       LxResult	 SampleOrbPartCore (
                               CLxUser_TriangleSoup	&soup,
                               unsigned		&segmentID);
};

This is a gear part that will be used in rendering.

class COrbElement :
       public CLxImpl_TableauSurface,
       public CLxImpl_TableauInstance,
       public COrbPart
{
       COrbLog		 orb_log;

   public:
       CLxUser_TableauVertex	 vrt_desc;
       float			 m_radius;
       int			 m_resolution;
       int			 f_pos[4];
       LXtVector		 m_offset;
       LXtMatrix		 m_xfrm;

       LxResult	 tsrf_Bound (LXtTableauBox bbox) LXx_OVERRIDE;
       unsigned	 tsrf_FeatureCount (LXtID4 type) LXx_OVERRIDE;
       LxResult	 tsrf_FeatureByIndex (
                               LXtID4		 type,
                               unsigned int	 index,
                               const char	**name) LXx_OVERRIDE;
       LxResult	 tsrf_SetVertex (ILxUnknownID vdesc) LXx_OVERRIDE;
       LxResult	 tsrf_Sample (
                               const LXtTableauBox bbox,
                               float scale,
                               ILxUnknownID trisoup) LXx_OVERRIDE;

       LxResult	 tins_Properties (
                               ILxUnknownID	 vecstack) LXx_OVERRIDE;

       LxResult	 tins_GetTransform (
                               unsigned	 endPoint,
                               LXtVector	 offset,
                               LXtMatrix	 xfrm) LXx_OVERRIDE;
};

In order to display the part in the viewport, we inherit from CLxImpl_TableauSurface and CLxTableauInstance. We also need to inherit from COrbPart, as that is where the actual gear part is created.

class COrbItemSurface :
       public CLxImpl_Surface
{
       /*
        * The parts of the gear (side and endcaps)
        */
       COrbPackage	*src_pkg;

   public:
       LxResult	 surf_GetBBox (LXtBBox *bbox) LXx_OVERRIDE;

       LxResult	 surf_FrontBBox (
                               const LXtVector	 pos,
                               const LXtVector	 dir,
                               LXtBBox		*bbox) LXx_OVERRIDE;

       LxResult	 surf_RayCast (
                               const LXtRayInfo *ray,
                               LXtRayHit *hit) LXx_OVERRIDE;

       LxResult	 surf_BinCount (unsigned int *count) LXx_OVERRIDE;

       LxResult	 surf_BinByIndex (
                               unsigned int	 index,
                               void		**ppvObj) LXx_OVERRIDE;

       LxResult	 surf_TagCount (
                               LXtID4		 type,
                               unsigned int	*count) LXx_OVERRIDE;

       LxResult	 surf_TagByIndex (
                               LXtID4		 type,
                               unsigned int	 index,
                               const char	**stag) LXx_OVERRIDE;

       LxResult	 Initialize (
                               COrbPackage		*pkg,
                               CLxUser_Item		&m_item,
                               CLxUser_ChannelRead	&chanRead);

       LxResult	 Initialize (
                               COrbPackage		*pkg,
                               double			 radius);
};

In order to create the item's surface, we need to inherit from CLxImpl_Surface. Among other things, this will perform binning, or the grouping of the primitives that make up the item's surface.

class COrbInstance :
       public CLxImpl_PackageInstance,
       public CLxImpl_StringTag,
       public CLxImpl_TableauSource,
       public CLxImpl_ViewItem3D
{
       COrbLog		 	orb_log;

   public:
       COrbPackage	*src_pkg;
       CLxUser_Item	 m_item;
       ILxUnknownID	 inst_ifc;

       LxResult	 pins_Initialize (
                               ILxUnknownID	 item,
                               ILxUnknownID	 super) LXx_OVERRIDE;
       void		 pins_Cleanup (void) LXx_OVERRIDE;
       LxResult	 pins_SynthName (char *buf, unsigned len) LXx_OVERRIDE;
       unsigned	 pins_DupType (void) LXx_OVERRIDE;
       LxResult	 pins_TestParent (ILxUnknownID item) LXx_OVERRIDE;
       LxResult	 pins_Newborn (ILxUnknownID original) LXx_OVERRIDE;
       LxResult	 pins_Loading (void) LXx_OVERRIDE;
       LxResult	 pins_AfterLoad (void) LXx_OVERRIDE;
       void		 pins_Doomed (void) LXx_OVERRIDE;

       LxResult	 stag_Get (LXtID4 type, const char **tag) LXx_OVERRIDE;

       LxResult	 tsrc_Elements (ILxUnknownID tblx) LXx_OVERRIDE;
       LxResult	 tsrc_PreviewUpdate (
                               int	 chanIndex,
                               int	*update) LXx_OVERRIDE;

       LxResult	 vitm_Draw (
                               ILxUnknownID	 itemChanRead,
                               ILxUnknownID	 viewStrokeDraw,
                               int		 selectionFlags,
                               LXtVector	 itemColor) LXx_OVERRIDE;

       LxResult	 vitm_HandleCount (
                               int		*count) LXx_OVERRIDE;

       LxResult	 vitm_HandleMotion (
                               int		 handleIndex,
                               int		*motionType,
                               double		*min,
                               double		*max,
                               LXtVector	 plane,
                               LXtVector	 offset) LXx_OVERRIDE;

       LxResult	 vitm_HandleChannel (
                               int		 handleIndex,
                               int		*chanIndex) LXx_OVERRIDE;

       LxResult	 vitm_HandleValueToPosition (
                               int		 handleIndex,
                               double		*chanValue,
                               LXtVector	 position) LXx_OVERRIDE;

       LxResult	 vitm_HandlePositionToValue (
                               int		 handleIndex,
                               LXtVector	 position,
                               double		*chanValue) LXx_OVERRIDE;
};

We want this class to create an instance of the orb. In order to do that, we will need to implement the related package, so we inherit from CLxImpl_PackageInstance. Additionally, we will want to set the tags of the instance, so we inherit from CLxImpl_PackageInstance. Finally, we want to display the object in the viewport, so we inherit from CLxImpl_ViewItem3D, which has basic 3D display methods, and CLxImpl_TableauSource, which makes it so that channels update the preview.

class COrbPackage :
       public CLxImpl_Package,
       public CLxImpl_SelectionListener
{
   public:
       static LXtTagInfoDesc		 descInfo[];
       CLxPolymorph<COrbInstance>	 orb_factory;
       CLxPolymorph<COrbElement>	 elt_factory;

       COrbPackage ();

       LxResult		pkg_SetupChannels (
                                       ILxUnknownID addChan) LXx_OVERRIDE;
       LxResult		pkg_TestInterface (const LXtGUID *guid) LXx_OVERRIDE;
       LxResult		pkg_Attach (void **ppvObj) LXx_OVERRIDE;

       void			selevent_Add     (
                                       LXtID4		 type,
                                       unsigned int	 subtType) LXx_OVERRIDE;
       void			selevent_Current (LXtID4 type) LXx_OVERRIDE;
};

290-310 We want to create a package for the orb item, so we have our class inherit from ClxImplPackage. Additionally, we want to have this class listen for global selection events, so we inherit from CLxImpl_SelectionListener.

Server Tags

LXtTagInfoDesc	 COrbPackage::descInfo[] = {
       { LXsPKG_SUPERTYPE,	"locator"	},
       { LXsPKG_IS_MASK,	"."		},
       { LXsSRV_LOGSUBSYSTEM,	"test-orb"	},
       { 0 }
};


These tags indicate that the class COrbPackage is a subtype of the super type locator and has the internal name of test-orb.

Initialize

       void
initialize ()
{
       CLxGenericPolymorph		*srv;

       srv = new CLxPolymorph<COrbPackage>;
       srv->AddInterface (new CLxIfc_Package          <COrbPackage>);
       srv->AddInterface (new CLxIfc_StaticDesc       <COrbPackage>);
       srv->AddInterface (new CLxIfc_SelectionListener<COrbPackage>);
       thisModule.AddServer ("test.orb", srv);
}

This method indicates that we will be exporting one server that will be dependent on the COrbPackage class. All of the interfaces of the classes that it inherited will also be added and the server will have the name of test.orb.

Implementations

LxResult
COrbElement::tsrf_Bound (
       LXtTableauBox		 bbox)
{
       bbox[0] = -m_radius;
       bbox[1] = -m_radius;
       bbox[2] = -m_radius;
       bbox[3] =  m_radius;
       bbox[4] =  m_radius;
       bbox[5] =  m_radius;
       return LXe_OK;
}

       unsigned
COrbElement::tsrf_FeatureCount (
       LXtID4			 type)
{
       return (type == LXiTBLX_BASEFEATURE ? 4 : 0);
}

       LxResult
COrbElement::tsrf_FeatureByIndex (
       LXtID4			 type,
       unsigned		 index,
       const char	       **name)
{
       if (type != LXiTBLX_BASEFEATURE)
               return LXe_NOTFOUND;

       switch (index) {
           case 0:
               name[0] = LXsTBLX_FEATURE_POS;
               return LXe_OK;

           case 1:
               name[0] = LXsTBLX_FEATURE_OBJPOS;
               return LXe_OK;

           case 2:
               name[0] = LXsTBLX_FEATURE_NORMAL;
               return LXe_OK;

           case 3:
               name[0] = LXsTBLX_FEATURE_VEL;
               return LXe_OK;
       }
       return LXe_OUTOFBOUNDS;
}

       LxResult
COrbElement::tsrf_SetVertex (
       ILxUnknownID		 vdesc)
{
       LxResult		 rc;
       const char		*name;
       unsigned		 offset, i;

       if (!vrt_desc.set (vdesc))
               return LXe_NOINTERFACE;

       for (i = 0; i < 4; i++) {
               tsrf_FeatureByIndex (LXiTBLX_BASEFEATURE, i, &name);
               rc = vrt_desc.Lookup (LXiTBLX_BASEFEATURE, name, &offset);
               f_pos[i] = (rc == LXe_OK ? offset : -1);
       }

       return LXe_OK;
}

       LxResult 
COrbElement::tsrf_Sample (
       const LXtTableauBox	 bbox,
       float			 scale,
       ILxUnknownID		 trisoup)
{
       CLxUser_TriangleSoup	 soup (trisoup);
       LXtTableauBox		 box;
       LxResult		 rc;
       float			 vec[3 * 4];
       double			 ang, sn, cs;
       unsigned		 index;
       int			 i, n, k;

       orb_log.Info ("Sample test box");

       /*
        * Return early if the bounding box (as determined by our radius
        * channel value) isn't visible.
        */
       box[0] = -m_radius;
       box[1] = -m_radius;
       box[2] = -m_radius * 0.5f;
       box[3] =  m_radius;
       box[4] =  m_radius;
       box[5] =  m_radius * 0.5f;

       if (!soup.TestBox (box))
               return LXe_OK;

       orb_log.Info ("Sample test seg");

       rc = soup.Segment (1, LXiTBLX_SEG_TRIANGLE);
       if (rc == LXe_FALSE)
               return LXe_OK;
       else if (LXx_FAIL (rc))
               return rc;

       orb_log.Info ("Sampling!");

       /*
        * This example doesn't have any velocity for motion blur.
        */
       vec[f_pos[3] + 0] = 0.0;
       vec[f_pos[3] + 1] = 0.0;
       vec[f_pos[3] + 2] = 0.0;

       /*
        * Vary the number of sides by the radius and resolution.
        */
       int res;
       if (scale < 1) {
               scale = 1;
               res = m_resolution;
       }
       else {
               res = 1;
       }
       scale = scale / 40.0f;
       n = (int) (3.142 / sqrt (scale / res / m_radius));
       if (n < 8)
               n = 8;
       else if (n > 1200)
               n = 1200;

       /*
        * Build the vertex list.
        */
       for (i = 0; i < n; i++) {
               ang = 3.14159 * 2.0 * i / n;
               sn  = sin (ang);
               cs  = cos (ang);

               vec[f_pos[0] + 0] = vec[f_pos[1] + 0] = static_cast<float>(sn  * m_radius);
               vec[f_pos[0] + 1] = vec[f_pos[1] + 1] = static_cast<float>(cs  * m_radius);
               vec[f_pos[0] + 2] = vec[f_pos[1] + 2] = static_cast<float>(0.5 * m_radius);

               vec[f_pos[2] + 0] = static_cast<float>(sn);
               vec[f_pos[2] + 1] = static_cast<float>(cs);
               vec[f_pos[2] + 2] = 0.0f;

               soup.Vertex (vec, &index);

               vec[f_pos[0] + 2] = static_cast<float>(-0.5 * m_radius);
               vec[f_pos[1] + 2] = static_cast<float>(-0.5 * m_radius);

               soup.Vertex (vec, &index);
       }

       /*
        * Build the triangle list.
        */
       k = 0;
       for (i = 0; i < n; i++) {
               soup.Polygon (k, (k + 2) % (n * 2), (k + 3) % (n * 2));
               soup.Polygon (k, (k + 3) % (n * 2), (k + 1) % (n * 2));
               k += 2;
       }

       return LXe_OK;
}

These functions create a tableau surface element that lives in the tableau and generates geometry for the renderer. It has a bounding box, vertex features, and a sample method.

       LxResult
COrbInstance::pins_Initialize (
       ILxUnknownID		 item,
       ILxUnknownID		 super)
{
       orb_log.Info ("Initialize");
       if (m_item.set (item)) {
               std::string x = "-- got item ";
               orb_log.Info (x.c_str ());
       }
       return LXe_OK;
}

       void
COrbInstance::pins_Cleanup (void)
{
       m_item.clear ();
}

       LxResult
COrbInstance::pins_SynthName (
       char			*buf,
       unsigned		 len)
{
       std::string name("test.orb");
       size_t count = name.size () + 1;
       if (count > len) {
               count = len;
       }
       memcpy (buf, &name[0], count);

       return LXe_OK;
}

       unsigned
COrbInstance::pins_DupType (void)
{
       return 0;
}

       LxResult
COrbInstance::pins_TestParent (
       ILxUnknownID		 item)
{
       return LXe_NOTIMPL;
}

       LxResult
COrbInstance::pins_Newborn (ILxUnknownID original)
{ 
       return LXe_NOTIMPL;
}

       LxResult
COrbInstance::pins_Loading (void)
{
       return LXe_NOTIMPL;
}

       LxResult
COrbInstance::pins_AfterLoad (void)
{
       return LXe_NOTIMPL;
}

       void
COrbInstance::pins_Doomed (void)
{
}

The instance is the implementation of the item, and there will be one allocated for each item in the scene. The functions here all perform a function(generally made evident by their name) for the instance.

LxResult
COrbInstance::stag_Get (
       LXtID4			 type,
       const char	       **tag)
{
       tag[0] = "Default";
       return LXe_OK;
}

The instance also presents a StringTag interface so it can pretend to have part and material tags for finding a shader.

       LxResult
COrbInstance::tsrc_Elements (
       ILxUnknownID		 tblx)
{
       CLxUser_Tableau		 tbx (tblx);
       CLxUser_ChannelRead	 chan;
       CLxUser_TableauShader	 shader;
       ILxUnknownID		 element;
       LxResult		 rc;
       double			 rad;
       int			 res;
       int			 idx;

       /*
        * This is our opportunity to fetch our custom channel values.
        */
       if (!tbx.GetChannels (chan, 0))
               return LXe_NOINTERFACE;

       idx = m_item.ChannelIndex ("radius");
       if (idx < 0)
               return LXe_NOTFOUND;

       rad = chan.FValue (m_item, idx);

       idx = m_item.ChannelIndex ("resolution");
       if (idx < 0)
               return LXe_NOTFOUND;

       res = chan.IValue (m_item, idx);

       if (!tbx.GetShader (shader, m_item, inst_ifc))
               return LXe_NOTFOUND;

       element = src_pkg->elt_factory.Spawn ();
       if (!element)
               return LXe_FAILED;

       LXCWxOBJ(element,COrbElement)->m_radius = static_cast<float>(rad);
       LXCWxOBJ(element,COrbElement)->m_resolution = res;

       /*
        * We also need to store the locator transform, so it can be looked
        * up later on when TableauInstance::GetTransform is called.
        */
       CLxLoc_Locator locator;
       if (locator.set (m_item)) {
               LXtMatrix	 xfrm;
               LXtVector	 offset;

               locator.WorldTransform (chan, xfrm, offset);
 
               for (unsigned i = 0; i < 3; ++i)
                       LXx_VCPY (LXCWxOBJ(element,COrbElement)->m_xfrm[i], xfrm[i]);
               LXx_VCPY (LXCWxOBJ(element,COrbElement)->m_offset, offset);
       }

       rc = tbx.AddElement (element, shader);
       lx::UnkRelease (element);

       return rc;
}

       LxResult
COrbInstance::tsrc_PreviewUpdate (
       int			 chanIndex,
       int			*update)
{
       int idx = m_item.ChannelIndex ("radius");
       if (chanIndex == idx) {
               *update = LXfTBLX_PREVIEW_UPDATE_GEOMETRY;
       }
       else {
               *update = LXfTBLX_PREVIEW_UPDATE_NONE;
       }

       return LXe_OK;
}

The instance's TableauSource interface allows it to place elements into the tableau, in this case our orb element.

LxResult
COrbInstance::vitm_Draw (
       ILxUnknownID	 itemChanRead,
       ILxUnknownID	 viewStrokeDraw,
       int		 selectionFlags,
       LXtVector	 itemColor)
{
       CLxUser_ChannelRead	 chanRead;
       CLxLoc_StrokeDraw	 strokeDraw;
       float			 lineWidth;
       int			 chanIndex;
       double			 rad;
       double			 ang, sn, cs;
       int			 i, n, k;
       std::vector<Point>	 points;

       chanRead.set (itemChanRead);
       strokeDraw.set (viewStrokeDraw);

       /*
        * Fetch the radius value for the current frame.
        */
       chanIndex = m_item.ChannelIndex ("radius");
       if (chanIndex < 0)
               return LXe_NOTFOUND;

       rad = chanRead.FValue (m_item, chanIndex);

       /*
        * Use a thicker line width when selected or rollover.
        */
       if (selectionFlags & LXiSELECTION_SELECTED ||
           selectionFlags & LXiSELECTION_ROLLOVER) {
               lineWidth = 2.0;
       }
       else {
               lineWidth = 1.0;
       }

       /*
        * The item color is automatically set according to the last hit test.
        */
       strokeDraw.BeginW (LXiSTROKE_LINES, itemColor, 1.0, lineWidth);

       LXtVector vert;
       int strokeFlags = LXiSTROKE_ABSOLUTE;

       /*
        * Vary the number of sides by the radius.
        */
       float scale = 1.0;
       scale /= 40.0;
       n = (int) (3.142 / sqrt (scale / rad));
       if (n < 8)
               n = 8;
       else if (n > 1200)
               n = 1200;

       /*
        * Build the vertex list.
        */
       for (i = 0; i < n; i++) {
               ang = 3.14159 * 2.0 * i / n;
               sn  = sin (ang);
               cs  = cos (ang);

               Point point;
               point.vec[0] = sn  * rad;
               point.vec[1] = cs  * rad;
               point.vec[2] = 0.5 * rad;
               points.push_back (point);

               point.vec[2] = -0.5 * rad;
               points.push_back (point);
       }

       /*
        * Draw the edges of each polygon.
        */
       k = 0;
       for (i = 0; i < n; i++) {
               LXx_VCPY (vert, points[k].vec);
               strokeDraw.Vertex (vert, strokeFlags);
               LXx_VCPY (vert, points[(k + 2) % (n * 2)].vec);
               strokeDraw.Vertex (vert, strokeFlags);

               strokeDraw.Vertex (vert, strokeFlags);
               LXx_VCPY (vert, points[(k + 3) % (n * 2)].vec);
               strokeDraw.Vertex (vert, strokeFlags);

               LXx_VCPY (vert, points[k].vec);
               strokeDraw.Vertex (vert, strokeFlags);
               LXx_VCPY (vert, points[(k + 3) % (n * 2)].vec);
               strokeDraw.Vertex (vert, strokeFlags);

               strokeDraw.Vertex (vert, strokeFlags);
               LXx_VCPY (vert, points[(k + 1) % (n * 2)].vec);
               strokeDraw.Vertex (vert, strokeFlags);

               k += 2;
       }

       return LXe_OK;
}

       LxResult
COrbInstance::vitm_HandleCount (
       int		*count)
{
       *count = 1;

       return LXe_OK;
}

       LxResult
COrbInstance::vitm_HandleMotion (
       int		 handleIndex,
       int		*motionType,
       double		*min,
       double		*max,
       LXtVector	 plane,
       LXtVector	 offset)
{
       LxResult	result = LXe_OUTOFBOUNDS;

       if (handleIndex == 0) {
               *motionType = LXfVHANDLE_DRAW_X;

               *min = 0.0001;
               *max = 10000;

               plane[0] = plane[1] = 1.0;
               plane[3] = 0.0;

               offset[0] = offset[1] = offset[2] = 0.0;

               result = LXe_OK;
       }

       return result;
}

       LxResult
COrbInstance::vitm_HandleChannel (
       int		 handleIndex,
       int		*chanIndex)
{ 
       LxResult	result = LXe_OUTOFBOUNDS;

       if (handleIndex == 0) {
               *chanIndex = m_item.ChannelIndex ("radius");
               result = LXe_OK;
       }

       return result;
}

       LxResult
COrbInstance::vitm_HandleValueToPosition (
       int		 handleIndex,
       double		*chanValue,
       LXtVector	 position)
{
       LxResult	result = LXe_OUTOFBOUNDS;

       if (handleIndex == 0) {
               position[0] = *chanValue;
               position[1] = position[2] = 0.0;

               result = LXe_OK;
       }

       return result;
}

       LxResult
COrbInstance::vitm_HandlePositionToValue (
       int		 handleIndex,
       LXtVector	 position,
       double		*chanValue)
{
       LxResult	result = LXe_OUTOFBOUNDS;

       if (handleIndex == 0) {
               *chanValue = position[0];

               result = LXe_OK;
       }

       return result;
}

Based on the channel values, draw the abstract item representation using the stroke drawing interface.