.. _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, NUKE looks on its plug-in
  path for a matching plug-in.  For example, if trying to create an Op "Example", it
  looks for (on Linux) Example.so or Example.tcl.  If it finds Example.so, it calls dlopen()
  or the equivalent on this.  NUKE's plug-ins 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 want for two different Ops to share the same .so, then you should make one of the plug-ins
  a TCL pointer to the other.  For example, if you want ExampleRed to be implemented within ExampleGreen.so,
  there needs to be an ExampleRed.tcl file containing the lines::

  	load ExampleGreen
  	returns

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

Op Construction
---------------

  NUKE invokes your Op constructor through the factory helper function that
  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 are 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 functions such as Float_knob() and XY_Knob() 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 defaults 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 is taken from the value presently in the integer addressed by the pointer.  Note that knobs at their default value are not 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, for example), and a script is saved at the old default value, then loading this
    up into a context where the default value is different results 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 has by default.
 
    This behavior can be overridden by setting the flag Knob::ALWAYS_SAVE on the knob.  This then
    always writes 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 creating the knobs
    functions like Float_knob(), calling knobs() again causes the knob to be evaluated at the proper time, and the value pointed
    to will be altered.

    Because of this 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 knobs() function evaluates, there are two phases and knobs() is called twice.  It is called as follows:

    * knobs() is called, storing knobs with the EARLY_STORE flag.
    * split_input, inputContext(), inputUIContext, and uses_input are called.
    * knobs() is 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 that are not used.  For example, a mask input controlled
         by a Bool_knob that 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.
