.. _advanced-architecture:

Architecture
============

.. _advanced-architecture-oplookup:

Op lookup
---------

  NUKE has a symbol table, linking Ops to factory functions.  If an attempt is made
  to create an Op with a name not already in the symbol table, it will look on its plugin
  path for a matching plugin.  For example, if it trying to create an Op "Example", it will
  look for (on Linux) Example.so, or Example.tcl.  If it finds Example.so, it will call dlopen()
  or the equivalent on this.  NUKE's plugins do not have an entry point per se - they are expected
  to have a global or static object within them whose constructor is run.  This should be an
  Iop::Description or an Op::Description.  The constructors of these add the Op to the symbol
  table.

  If you wish for two different Ops to share the same .so, then you should make one of the plugins
  a TCL pointer to the other.  For example if you wish ExampleRed to be implemented within ExampleGreen.so,
  there would be an ExampleRed.tcl file containing the lines::

  	load ExampleGreen
  	returns

  The joint plugin should contain Description objects for all of its classes.
 
.. _advanced-architecture-opconstruction:

Op construction
---------------

  NUKE invokes your Op constructor through the factory helper function which
  is in the Description.  The constructor is passed the pointer to the
  Node.  Note that there can be multiple Ops per Node: if the node is
  being accessed at two separate times, or views, for example (through the
  use of nodes such as FrameBlend, or Anaglyph), multiple Ops will be created.
  
  At the time that your Op is constructed, knobs do not yet exist, and the
  inputs are not yet wired up.  Some Ops want to set defaults according to the
  size of the input image: the special function "input_format()" works during
  Op construction, and can be used to get the size of what will be connected as
  the input image.

.. _advanced-architecture-knobs:

Knobs
-----

  .. cpp:function:: void DD::Image::Knob::knobs(Knob_Callback)
    
    knobs() is invoked by NUKE on your Op soon after creation.  An example implementation
    is as follows

    .. code-block:: c

      void knobs(Knob_Callback f)
      {
        Float_knob(f, &_flt, "flt", "float value");
        SetRange(f, IRange(0, 10));
        SetFlags(f, Knob::EARLY_STORE);
        XY_Knob(f, &_pos, "pos");
        String_knob(f, &_str, "text");
      }

    The knobs() function has a dual purpose.  The first call to knobs() will be the "make knobs"
    phase.  During this phase, calls to function such as Float_knob() and XY_Knob() will create
    knobs.  These take a number of standard parameters

    * the Knob_Callback pointer itself
    * a pointer to the data field within the Op
    * the internal name of the knob
    * the label to use for the knob (this parameter is optional, and would default to the name)

    Some knobs take additional parameters, such as the Enumeration_knob, which takes a pointer
    the list of values.  The default value of the knobs will be taken from the value presently in the
    value.  Note that knobs at their default value will not be written out by NUKE's save script.  This
    means that if you change the default value of a knob, or make it unstable (by making it depend upon
    some environment variable, etc), and a script is saved at the old default value, then loading this
    up into a context where the default value is different, will result in the new default value. 
    For this reason it is advisable to avoid changing default values.  Use the knobDefault() mechanism if 
    you wish to change the value that a knob on a newly-created node will have by default.
 
    This behaviour can be overridden, by setting the flag Knob::ALWAYS_SAVE on the knob.  This will then
    always write out the value of the knob, even if it is the default.  This results in larger file sizes.
    In particular, it is recommended that if the knob's default depends upon the input_format(), then you
    should use this flag, as when it is recreated on a script load the input_format() may be different.

    The other purpose of the knobs() function is for NUKE and the knobs to communicate back to your Op to
    tell it what value the user has selected.  In order to do this, rather than store the pointers you passed,
    it calls knobs() again, with a different object as the callback.  This time, rather than create the knobs
    functions like Float_knob() will cause the knob to be evaluated at the proper time, and the value pointed
    to will be altered.

    Because of this any dual use, the knobs() function will be called frequently.  Code that genuinely only
    needs to run on the knob-creation call should call Knob_Callback::makeKnobs() on the callback passed on.
    The calls to the knob function.

    SetRange, SetFlags, ClearFlags are no-ops during the non-makeKnobs phase.

    When the knob values, there are two phases, and knobs() will be called twice.  It will
    be called as follows

    * knobs() called, storing knobs with the EARLY_STORE flag
    * split_input, inputContext(), inputUIContext, and uses_input are called
    * knobs() called, storing knobs without the EARLY_STORE flag.

.. _advanced-architecture-validation:

Validation
----------

  .. cpp:function:: void Op::_validate(bool)

    _validate() is called by NUKE on your Op to "validate" it.  Validation consists of

       * calling, in turn, validate() on all the inputs that the Op is using in its current configuration
         (it is possible to omit to validate inputs which are not used.  For example, a mask input controlled
         by a Bool_knob which is off, and is not used at all, need not be validated)

       * setting up Op-type-dependent data.  For the case of Iops, validate should set the "IopInfo", which is
         stored in the ``info_`` member on Iop.  For Deep Ops, validate should set the DeepInfo.

    See the respective sections on the various Op types for more info on the specifics on their validation implementation.
