Selection Operation - Manual implementation

From The Foundry MODO SDK wiki
Jump to: navigation, search

Selection Operation items can be automatically generated directly from an ILxSelectionOperation interface, however in some cases, greater control over the item and evaluation is required. In these instances, the selection operation has to be implemented manually. A package and modifier need to be created along with a selection operation interface, enabling the modifier to write the selection operation object to a channel on the item.

The package/item should be of the type LXsITYPE_SELOP, and the modifier should read the inputs it requires, and output the ILxSelectionOperation COM object to the LXsICHAN_SELOP_OBJ channel. The LXsSELOP_PMODEL server tag should not be present on the ILxSelectionOperation spawned by this modifier.

Sample Code

The following sample code implements a very basic selection operation, item type and modifier that selects all elements within a particular index range.

C++
/*
 *	The Package and PackageInstance define the item that the user can
 *	connect to a mesh operation to control the selection.
 */
 
class Instance : public CLxImpl_PackageInstance
{
	public:
		static void initialize ()
		{
			CLxGenericPolymorph	*srv = NULL;
 
			srv = new CLxPolymorph						<Instance>;
			srv->AddInterface		(new CLxIfc_PackageInstance	<Instance>);
 
			lx::AddSpawner ("select.inRange.inst", srv);
		}
};
 
class Package : public CLxImpl_Package
{
	public:
		static void initialize ()
		{
			CLxGenericPolymorph	*srv = NULL;
 
			srv = new CLxPolymorph						<Package>;
			srv->AddInterface		(new CLxIfc_Package		<Package>);
			srv->AddInterface		(new CLxIfc_StaticDesc		<Package>);
 
			lx::AddServer ("select.inRange", srv);
		}
 
		Package () : _inst_spawn ("select.inRange.inst") {}
 
			LxResult
		pkg_TestInterface (
			const LXtGUID		 guid)			LXx_OVERRIDE
		{
			_inst_spawn.TestInterfaceRC (guid);
		}
 
			LxResult
		pkg_Attach (
			void			**ppvObj)		LXx_OVERRIDE
		{
			_inst_spawn.Alloc (ppvObj);
 
			return ppvObj[0] ? LXe_OK : LXe_FAILED;
		}
 
			LxResult
		pkg_SetupChannels (
			ILxUnknownID		 addChan_obj)		LXx_OVERRIDE
		{
			CLxUser_AddChannel	 add_chan (addChan_obj);
 
			if (add_chan.test ())
			{
				add_chan.NewChannel ("start", LXsTYPE_INTEGER);
				add_chan.SetDefault (0.0, 0);
 
				add_chan.NewChannel ("end", LXsTYPE_INTEGER);
				add_chan.SetDefault (0.0, 100);
 
				return LXe_OK;
			}
 
			return LXe_FAILED;
		}
 
		static LXtTagInfoDesc	 descInfo[];
 
	private:
		CLxSpawner <Instance>	 _inst_spawn;
};
 
LXtTagInfoDesc Package::descInfo[] =
{
	{ LXsPKG_SUPERTYPE,	LXsITYPE_SELOP },
	{ 0 }
};
 
/*
 *	The Selection Operation is spawned by the modifier and selects all
 *	elements within the defined range.
 */
 
class SelOp : public CLxImpl_SelectionOperation
{
	public:
		static void initialize ()
		{
			CLxGenericPolymorph	*srv = NULL;
 
			srv = new CLxPolymorph						<SelOp>;
			srv->AddInterface		(new CLxIfc_SelectionOperation	<SelOp>);
 
			lx::AddSpawner ("select.inRange.op", srv);
		}
 
		SelOp () : _start (0), _end (100) { }
 
			LxResult
		selop_SetMesh (
			ILxUnknownID		 mesh)		LXx_OVERRIDE
		{
			/*
			 *	Cache the mesh COM object. This is need to spawn the accessors
			 *	in the Test functions.
			 */
 
			return _mesh.set (mesh) ? LXe_OK : LXe_FAILED;
		}
 
			void
		SetRange (
			int			 start,
			int			 end)
		{
			if (start > end)
			{
				_start = end;
				_end = start;
			}
			else
			{
				_start = start;
				_end = end;
			}
		}
 
			LxResult
		selop_TestPoint (
			LXtPointID		 point)		LXx_OVERRIDE
		{
			CLxUser_Point		 accessor;
			unsigned int		 index = 0;
 
			/*
			 *	Select the provided point in the accessor and test it's index
			 *	against the range.
			 */
 
			if (_mesh.test () && accessor.fromMesh (_mesh))
			{
				if (LXx_OK (accessor.Select (point)))
				{
					accessor.Index (&index);
					if (IsIndexInRange (index))
						return LXe_TRUE;
				}
			}
 
			return LXe_FALSE;
		}
 
			LxResult
		selop_TestEdge (
			LXtEdgeID		 edge)		LXx_OVERRIDE
		{
			CLxUser_Edge		 accessor;
			unsigned int		 index = 0;
 
			/*
			 *	Select the provided point in the accessor and test it's index
			 *	against the range.
			 */
 
			if (_mesh.test () && accessor.fromMesh (_mesh))
			{
				if (LXx_OK (accessor.Select (edge)))
				{
					accessor.Index (&index);
					if (IsIndexInRange (index))
						return LXe_TRUE;
				}
			}
 
			return LXe_FALSE;
		}
 
			LxResult
		selop_TestPolygon (
			LXtPolygonID		 polygon)	LXx_OVERRIDE
		{
			CLxUser_Polygon		 accessor;
			unsigned int		 index = 0;
 
			/*
			 *	Select the provided point in the accessor and test it's index
			 *	against the range.
			 */
 
			if (_mesh.test () && accessor.fromMesh (_mesh))
			{
				if (LXx_OK (accessor.Select (polygon)))
				{
					accessor.Index (&index);
					if (IsIndexInRange (index))
						return LXe_TRUE;
				}
			}
 
			return LXe_FALSE;
		}
 
	private:
 
			bool
		IsIndexInRange (
			unsigned int		 index)
		{
			/*
			 *	Test if the provided index is within range.
			 */
 
			return (index <= _end && index >= _start);
		}
 
		CLxUser_Mesh		 _mesh;
		int			 _start;
		int			 _end;
};
 
/*
 *	The Modifier is associated with the all items of our type, and it reads
 *	the start and end channels as inputs, spawns the selection operation and
 *	writes it to the selection operation object channel.
 */
 
class ModifierElement : public CLxItemModifierElement
{
	public:
		ModifierElement (
			CLxUser_Evaluation	&eval,
			ILxUnknownID		 item_obj)
		{
			CLxUser_Item		 item (item_obj);
 
			_start_index = -1;
			_end_index = -1;
			_output_index = -1;
 
			if (item.test ())
			{
				/*
				 *	Allocate the start and end channels as
				 *	inputs and the selection operation object
				 *	as an output.
				 */
 
				_start_index = eval.AddChan (item, "start", LXfECHAN_READ);
				_end_index = eval.AddChan (item, "end", LXfECHAN_READ);
 
				_output_index = eval.AddChan (item, LXsICHAN_SELOP_OBJ, LXfECHAN_WRITE);
			}
		}
 
			void
		Eval (
			CLxUser_Evaluation	&eval,
			CLxUser_Attributes	&attr)			LXx_OVERRIDE
		{
			CLxUser_ValueReference	 val_ref;
			SelOp			*selOp = NULL;
			ILxUnknownID		 selOp_obj = NULL;
			int			 start = 0, end = 100;
 
			CLxSpawner <SelOp>	 spawner ("select.inRange.op");
 
			if (_output_index < 0)
				return;
 
			if (_start_index >= 0)
				start = attr.Int (_start_index);
 
			if (_end_index >= 0)
				end = attr.Int (_end_index);
 
			/*
			 *	Spawn the selection operation and set the start and end
			 *	values.
			 */
 
			selOp = spawner.Alloc (selOp_obj);
			if (selOp)
				selOp->SetRange (start, end);
 
			/*
			 *	Write the selection operation into the selection operation
			 *	item output channel.
			 */
 
			if (attr.ObjectRW (_output_index, val_ref))
				val_ref.SetObject (selOp_obj);
 
			/*
			 *	Release the selection operation COM object, as we spawned
			 *	the item directly.
			 */
 
			lx::UnkRelease (selOp_obj);
		}
 
	private:
		int			 _output_index, _start_index, _end_index;
};
 
class ModifierServer : public CLxItemModifierServer
{
	public:
		static void initialize ()
		{
			CLxExport_ItemModifierServer <ModifierServer>	("select.inRange.mod");
		}
 
			const char *
		ItemType ()						LXx_OVERRIDE
		{
			/*
			 *	Associate the modifier with all items of our type.
			 */
 
			return "select.inRange";
		}
 
			CLxItemModifierElement *
		Alloc (
			CLxUser_Evaluation	&eval,
			ILxUnknownID		 item_obj)		LXx_OVERRIDE
		{
			return new ModifierElement (eval, item_obj);
		}
};
 
void initialize ()
{
	Package::initialize ();
	Instance::initialize ();
	SelOp::initialize ();
	ModifierServer::initialize ();
}