Link Search Menu Expand Document

Compute

The Compute base class is the base logic for perform a given node’s computation. The Compute base class contains all the outputs (data values) for a give node. The inputs to the compute are retrieved from the outputs on other compute instances, or from parameters on the node. Parameters on a node are considered to be inputs to a node.

Compute base class header.

// The main input and main output are known to our nodegraph system
// and various logic uses that knowledge.
// Other inputs and outputs are only used within that specific compute.
class COMPUTES_EXPORT Compute: public Component {
 public:

  static const char* kMainInputName;
  static const char* kMainOutputName;

  static const char* kValuePropertyName;

  COMPONENT_ID(Compute, InvalidComponent);
  Compute(Entity* entity, ComponentDID derived_id);
  virtual ~Compute();

  virtual void create_inputs_outputs(const EntityConfig& config);
  virtual void set_self_dirty(bool dirty);

  virtual const Dep<Inputs>& get_inputs() {return _inputs;}

  // Inputs.
  virtual QJsonObject get_editable_inputs() const;
  virtual void set_editable_inputs(const QJsonObject& inputs);
  virtual QJsonObject get_input_exposure() const;
  virtual void set_input_exposure(const QJsonObject& settings);

  // Access outputs.
  virtual const QJsonObject& get_outputs() const;
  virtual QJsonValue get_output(const std::string& name) const;
  virtual QJsonValue get_main_output() const;

  // Get our hints.
  virtual const QJsonObject& get_hints() const {return _hints;}

  bool eval_js_with_inputs(const QString& text, QJsonValue& result, QString& error) const;

  void on_error(const QString& error_message);


  virtual bool update_unlocked_group() {return true;}

 protected:
  // Our state.
  virtual void initialize_wires();
  virtual bool update_state();

  virtual bool clean_finalize();

  // Our outputs. These are called during cleaning, so they don't dirty the instance's state.
  virtual void set_outputs(const QJsonObject& outputs);
  virtual void set_output(const std::string& name, const QJsonValue& value);
  virtual void set_main_output(const QJsonValue& value);

  // Plugs.
  Entity* create_input(const std::string& name, const EntityConfig& config);
  Entity* create_output(const std::string& name, const EntityConfig& config);

  Entity* create_main_input(const EntityConfig& config);
  Entity* create_main_output(const EntityConfig& config);

  Entity* create_namespace(const std::string& name);
  Entity* get_inputs_space();
  Entity* get_outputs_space();
  Entity* get_links_space();

  // Used by derived classes.
  static void add_hint(QJsonObject& map, const std::string& name, GUITypes::HintKey hint_type, const QJsonValue& value);
  static void remove_hint(QJsonObject& node_hints, const std::string& name);
  static void add_main_input_hint(QJsonObject& map);

 protected:
  Dep<Inputs> _inputs;
  Dep<BaseNodeGraphManipulator> _manipulator;

  // Our outputs.
  QJsonObject _outputs;

  // Hints are generally used for all inputs on a node.
  // They are used to display customized guis for the input.
  // Note this maps the input name to hints.
  // The key is the string of the number representing the HintType.
  // The value is a QJsonValue holding an int representing an enum or another type.
  static const QJsonObject _hints;

};

Compute base class implementation.

const char* Compute::kMainInputName = "in";
const char* Compute::kMainOutputName = "out";

const char* Compute::kValuePropertyName = "value";

struct InputComputeComparator {
  bool operator()(const Dep<InputCompute>& left, const Dep<InputCompute>& right) const {
    return left->get_name() < right->get_name();
  }
};

const QJsonObject Compute::_hints;

Compute::Compute(Entity* entity, ComponentDID derived_id)
    : Component(entity, kIID(), derived_id),
      _inputs(this),
      _manipulator(this) {
  // Note this only exists for node computes and not for plug computes.
  get_dep_loader()->register_fixed_dep(_inputs, Path({"."}));
  // We only grab the manipulator for non input/output computes, to avoid cycles.
}

Compute::~Compute() {
}

void Compute::create_inputs_outputs(const EntityConfig& config) {
  external();
  create_namespace(kInputsFolderName);
  create_namespace(kOutputsFolderName);
}

void Compute::set_self_dirty(bool dirty) {
  Component::set_self_dirty(dirty);
  if (_manipulator) {
      std::cerr << to_underlying(get_did()) << " Compute setting marker to: " << !is_state_dirty() << "\n";
      _manipulator->update_clean_marker(our_entity(), !is_state_dirty());
      if (dirty) {
        std::cerr << to_underlying(get_did()) << " Compute is bubbling dirtiness\n";
        _manipulator->bubble_group_dirtiness(our_entity());
      }
  }
}

void Compute::initialize_wires() {
  Component::initialize_wires();
  if ((get_did() != ComponentDID::kInputCompute) && (get_did() != ComponentDID::kOutputCompute)) {
    _manipulator = get_dep<BaseNodeGraphManipulator>(get_app_root());
  }
}

bool Compute::update_state() {
  internal();
  // Notify the gui side that a computation is now processing on the compute side.
  if (_manipulator) {
    _manipulator->set_processing_node(our_entity());
  }

  return true;
}

bool Compute::clean_finalize() {
  internal();
  Component::clean_finalize();
  // Notify the gui side that a computation is now processing on the compute side.
  //if (_manipulator) {
  //  _manipulator->clear_processing_node();
  //}
  return true;
}

QJsonObject Compute::get_editable_inputs() const {
  return _inputs->get_editable_inputs();
}

void Compute::set_editable_inputs(const QJsonObject& inputs) {
  _inputs->set_editable_inputs(inputs);
}

QJsonObject Compute::get_input_exposure() const {
  return _inputs->get_exposure();
}

void Compute::set_input_exposure(const QJsonObject& settings) {
  _inputs->set_exposure(settings);
}

const QJsonObject& Compute::get_outputs() const {
  external();
  return _outputs;
}

void Compute::set_outputs(const QJsonObject& outputs) {
  internal();
  _outputs = outputs;
}

QJsonValue Compute::get_output(const std::string& name) const{
  external();
  if (!_outputs.contains(name.c_str())) {
    return QJsonValue();
  }
  return _outputs.value(name.c_str());
}

QJsonValue Compute::get_main_output() const {
  return get_output(kMainOutputName);
}

void Compute::set_output(const std::string& name, const QJsonValue& value) {
  internal();
  _outputs.insert(name.c_str(), value);
}

void Compute::set_main_output(const QJsonValue& value) {
  internal();
  _outputs.insert(kMainOutputName, value);
}

Entity* Compute::create_input(const std::string& name, const EntityConfig& config) {
  external();

  // Get the inputs namespace.
  Dep<BaseFactory> factory = get_dep<BaseFactory>(Path());
  Entity* inputs_space = get_inputs_space();

  // Make sure the name doesn't exist already.
  assert(!inputs_space->get_child(name));

  // Create the input.
  InputEntity* input = static_cast<InputEntity*>(factory->instance_entity(inputs_space, EntityDID::kInputEntity, name));
  input->create_internals(config);
  return input;
}

Entity* Compute::create_output(const std::string& name, const EntityConfig& config) {
  external();

  // Get the outputs namespace.
  Dep<BaseFactory> factory = get_dep<BaseFactory>(Path());
  Entity* outputs_space = get_outputs_space();

  // Make sure the name doesn't exist already.
  assert(!outputs_space->get_child(name));

  // Create the output.
  OutputEntity* output = static_cast<OutputEntity*>(factory->instance_entity(outputs_space, EntityDID::kOutputEntity, name));
  output->create_internals(config);
  return output;
}

Entity* Compute::create_main_input(const EntityConfig& config) {
  EntityConfig c = config;
  c.unconnected_value = QJsonObject();
  return create_input(kMainInputName, c);
}

Entity* Compute::create_main_output(const EntityConfig& config) {
  EntityConfig c = config;
  c.unconnected_value = QJsonObject();
  return create_output(kMainOutputName, c);
}

Entity* Compute::create_namespace(const std::string& name) {
  external();
  Dep<BaseFactory> factory = get_dep<BaseFactory>(Path());
  Entity* space = static_cast<BaseNamespaceEntity*>(factory->instance_entity(our_entity(), EntityDID::kBaseNamespaceEntity, name));
  space->create_internals();
  return space;
}

Entity* Compute::get_inputs_space() {
  external();
  return our_entity()->get_child(kInputsFolderName);
}

Entity* Compute::get_outputs_space() {
  external();
  return our_entity()->get_child(kOutputsFolderName);
}

Entity* Compute::get_links_space() {
  external();
  return our_entity()->get_child(kLinksFolderName);
}

void Compute::add_hint(QJsonObject& node_hints, const std::string& name, GUITypes::HintKey hint_type, const QJsonValue& value) {
  QJsonObject param_hints = node_hints.value(name.c_str()).toObject();
  param_hints.insert(QString::number(to_underlying(hint_type)), value);
  node_hints.insert(name.c_str(),  param_hints);
}

void Compute::remove_hint(QJsonObject& node_hints, const std::string& name) {
  node_hints.remove(name.c_str());
}

void Compute::add_main_input_hint(QJsonObject& map) {
  add_hint(map, kMainInputName, GUITypes::HintKey::DescriptionHint, "The main input object that the node operates on.");
}

void Compute::on_error(const QString& error_message) {
  _manipulator->set_error_node(error_message);
  _manipulator->clear_ultimate_targets();
}

bool Compute::eval_js_with_inputs(const QString& text, QJsonValue& result, QString& error) const {
  internal();
  QJSEngine engine;

  // Add the input values into the context.
  for (auto &iter: _inputs->get_all()) {
    const Dep<InputCompute>& input = iter.second;
    QJsonValue value = input->get_main_output();
    const std::string& input_name = input->get_name();
    QJSValue jsvalue = engine.toScriptValue(value);
    engine.globalObject().setProperty(QString::fromStdString(input_name), jsvalue);
  }

  return JSONUtils::eval_js_in_context(engine, text, result,error);
}