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);
}