-
-
Save sphaero/5042d4ef83b451455eb93afbca0fe080 to your computer and use it in GitHub Desktop.
Second prototype of standalone node graph editor for ImGui
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Nodes.h" | |
namespace ImGui | |
{ | |
template<int n> | |
struct BezierWeights | |
{ | |
constexpr BezierWeights() : x_(), y_(), z_(), w_() | |
{ | |
for (int i = 1; i <= n; ++i) | |
{ | |
float t = (float)i / (float)(n + 1); | |
float u = 1.0f - t; | |
x_[i - 1] = u * u * u; | |
y_[i - 1] = 3 * u * u * t; | |
z_[i - 1] = 3 * u * t * t; | |
w_[i - 1] = t * t * t; | |
} | |
} | |
float x_[n]; | |
float y_[n]; | |
float z_[n]; | |
float w_[n]; | |
}; | |
static constexpr auto bezier_weights_ = BezierWeights<16>(); | |
float ImVec2Dot(const ImVec2& S1, const ImVec2& S2) | |
{ | |
return (S1.x * S2.x + S1.y * S2.y); | |
} | |
float GetSquaredDistancePointSegment(const ImVec2& P, const ImVec2& S1, const ImVec2& S2) | |
{ | |
const float l2 = (S1.x - S2.x) * (S1.x - S2.x) + (S1.y - S2.y) * (S1.y - S2.y); | |
if (l2 < 1.0f) | |
{ | |
return (P.x - S2.x) * (P.x - S2.x) + (P.y - S2.y) * (P.y - S2.y); | |
} | |
ImVec2 PS1(P.x - S1.x, P.y - S1.y); | |
ImVec2 T(S2.x - S1.x, S2.y - S2.y); | |
const float tf = ImVec2Dot(PS1, T) / l2; | |
const float minTf = 1.0f < tf ? 1.0f : tf; | |
const float t = 0.0f > minTf ? 0.0f : minTf; | |
T.x = S1.x + T.x * t; | |
T.y = S1.y + T.y * t; | |
return (P.x - T.x) * (P.x - T.x) + (P.y - T.y) * (P.y - T.y); | |
} | |
float GetSquaredDistanceToBezierCurve(const ImVec2& point, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4) | |
{ | |
float minSquaredDistance = FLT_MAX; | |
float tmp; | |
ImVec2 L = p1; | |
ImVec2 temp; | |
for (int i = 1; i < 16 - 1; ++i) | |
{ | |
const ImVec4& W = ImVec4(bezier_weights_.x_[i], bezier_weights_.y_[i], bezier_weights_.z_[i], bezier_weights_.w_[i]); | |
temp.x = W.x * p1.x + W.y * p2.x + W.z * p3.x + W.w * p4.x; | |
temp.y = W.x * p1.y + W.y * p2.y + W.z * p3.y + W.w * p4.y; | |
tmp = GetSquaredDistancePointSegment(point, L, temp); | |
if (minSquaredDistance > tmp) | |
{ | |
minSquaredDistance = tmp; | |
} | |
L = temp; | |
} | |
tmp = GetSquaredDistancePointSegment(point, L, p4); | |
if (minSquaredDistance > tmp) | |
{ | |
minSquaredDistance = tmp; | |
} | |
return minSquaredDistance; | |
} | |
bool ImGuiNodes::UpdateCanvasGeometry(ImDrawList * draw_list) | |
{ | |
ImGuiIO& io = ImGui::GetIO(); | |
pos_ = ImGui::GetWindowPos(); | |
size_ = ImGui::GetWindowSize(); | |
mouse_ = ImGui::GetMousePos(); | |
ImRect canvas(pos_, pos_ + size_); | |
if (!ImGui::IsMouseDown(0) && canvas.Contains(mouse_)) | |
{ | |
if (ImGui::IsMouseDragging(1)) | |
scroll_ += io.MouseDelta; | |
if (io.KeyShift && !io.KeyCtrl) | |
scroll_.x += io.MouseWheel * 16.0f; | |
if (!io.KeyShift && !io.KeyCtrl) | |
scroll_.y += io.MouseWheel * 16.0f; | |
if (!io.KeyShift && io.KeyCtrl) | |
{ | |
ImVec2 focus = (mouse_ - scroll_ - pos_) / scale_; | |
if (io.MouseWheel < 0.0f) | |
for (float zoom = io.MouseWheel; zoom < 0.0f; zoom += 1.0f) | |
scale_ = ImMax(0.3f, scale_ / 1.05f); | |
if (io.MouseWheel > 0.0f) | |
for (float zoom = io.MouseWheel; zoom > 0.0f; zoom -= 1.0f) | |
scale_ = ImMin(3.0f, scale_ * 1.05f); | |
ImVec2 shift = scroll_ + (focus * scale_); | |
scroll_ += mouse_ - shift - pos_; | |
} | |
if (ImGui::IsMouseReleased(1)) | |
if (io.MouseDragMaxDistanceSqr[1] < (io.MouseDragThreshold * io.MouseDragThreshold)) | |
ImGui::OpenPopup("NodesContextMenu"); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
const float grid = 64.0f * scale_; | |
for (float x = fmodf(scroll_.x, grid); x < size_.x; x += grid) | |
draw_list->AddLine(ImVec2(x, 0.0f) + pos_, ImVec2(x, size_.y) + pos_, ImColor(0.5f, 0.5f, 0.5f, 0.1f)); | |
for (float y = fmodf(scroll_.y, grid); y < size_.y; y += grid) | |
draw_list->AddLine(ImVec2(0.0f, y) + pos_, ImVec2(size_.x, y) + pos_, ImColor(0.5f, 0.5f, 0.5f, 0.1f)); | |
return true; | |
} | |
ImGuiNodesNode* ImGuiNodes::UpdateNodesFromCanvas() | |
{ | |
ImGuiIO& io = ImGui::GetIO(); | |
ImVec2 offset = pos_ + scroll_; | |
ImRect canvas(pos_, pos_ + size_); | |
ImGuiNodesNode* hovered_node = NULL; | |
for (int node_idx = 0; node_idx < nodes_.size(); ++node_idx) | |
{ | |
ImGuiNodesNode* node = nodes_[node_idx]; | |
ImRect node_rect = node->area_node_; | |
node_rect.Min *= scale_; | |
node_rect.Max *= scale_; | |
node_rect.Translate(offset); | |
node_rect.ClipWith(canvas); | |
if (canvas.Overlaps(node_rect)) | |
{ | |
node->state_ |= ImGuiNodesNodeStateFlag_Visible; | |
node->state_ &= ~ImGuiNodesNodeStateFlag_Hovered; | |
} | |
else | |
{ | |
node->state_ &= ~(ImGuiNodesNodeStateFlag_Visible | ImGuiNodesNodeStateFlag_Hovered | ImGuiNodesNodeStateFlag_Marked); | |
continue; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
if (!hovered_node && node_rect.Contains(mouse_)) | |
hovered_node = node; | |
//////////////////////////////////////////////////////////////////////////////// | |
for (int input_idx = 0; input_idx < node->inputs_.size(); ++input_idx) | |
{ | |
ImGuiNodesInput* input = node->inputs_[input_idx]; | |
if (input->type_ == ImGuiNodesConnectorType_Invisible) | |
continue; | |
input->state_ &= ~(ImGuiNodesConnectorStateFlag_Hovered | ImGuiNodesConnectorStateFlag_Consider | ImGuiNodesConnectorStateFlag_Dragging); | |
if (state_ == ImGuiNodesState_DraggingInput) | |
{ | |
if (input == element_input_) | |
input->state_ |= ImGuiNodesConnectorStateFlag_Dragging; | |
continue; | |
} | |
if (state_ == ImGuiNodesState_DraggingOutput) | |
{ | |
if (element_node_ == node) | |
continue; | |
if (ConnectionMatrix(node, element_node_, input, element_output_)) | |
input->state_ |= ImGuiNodesConnectorStateFlag_Consider; | |
} | |
if (!hovered_node || hovered_node != node) | |
continue; | |
ImRect input_rect = input->area_input_; | |
input_rect.Min *= scale_; | |
input_rect.Max *= scale_; | |
input_rect.Translate(offset); | |
if (input_rect.Contains(mouse_)) | |
{ | |
if (state_ != ImGuiNodesState_DraggingOutput) | |
{ | |
input->state_ |= ImGuiNodesConnectorStateFlag_Hovered; | |
continue; | |
} | |
if (input->state_ & ImGuiNodesConnectorStateFlag_Consider) | |
input->state_ |= ImGuiNodesConnectorStateFlag_Hovered; | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
for (int output_idx = 0; output_idx < node->outputs_.size(); ++output_idx) | |
{ | |
ImGuiNodesOutput* output = node->outputs_[output_idx]; | |
if (output->type_ == ImGuiNodesConnectorType_Invisible) | |
continue; | |
output->state_ &= ~(ImGuiNodesConnectorStateFlag_Hovered | ImGuiNodesConnectorStateFlag_Consider | ImGuiNodesConnectorStateFlag_Dragging); | |
if (state_ == ImGuiNodesState_DraggingOutput) | |
{ | |
if (output == element_output_) | |
output->state_ |= ImGuiNodesConnectorStateFlag_Dragging; | |
continue; | |
} | |
if (state_ == ImGuiNodesState_DraggingInput) | |
{ | |
if (element_node_ == node) | |
continue; | |
if (ConnectionMatrix(element_node_, node, element_input_, output)) | |
output->state_ |= ImGuiNodesConnectorStateFlag_Consider; | |
} | |
if (!hovered_node || hovered_node != node) | |
continue; | |
ImRect output_rect = output->area_output_; | |
output_rect.Min *= scale_; | |
output_rect.Max *= scale_; | |
output_rect.Translate(offset); | |
if (output_rect.Contains(mouse_)) | |
{ | |
if (state_ != ImGuiNodesState_DraggingInput) | |
{ | |
output->state_ |= ImGuiNodesConnectorStateFlag_Hovered; | |
continue; | |
} | |
if (output->state_ & ImGuiNodesConnectorStateFlag_Consider) | |
output->state_ |= ImGuiNodesConnectorStateFlag_Hovered; | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
if (state_ == ImGuiNodesState_Selecting) | |
{ | |
if (io.KeyCtrl && area_.Overlaps(node_rect)) | |
{ | |
node->state_ |= ImGuiNodesNodeStateFlag_Marked; | |
continue; | |
} | |
if (!io.KeyCtrl && area_.Contains(node_rect)) | |
{ | |
node->state_ |= ImGuiNodesNodeStateFlag_Marked; | |
continue; | |
} | |
node->state_ &= ~ImGuiNodesNodeStateFlag_Marked; | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
if (hovered_node) | |
hovered_node->state_ |= ImGuiNodesNodeStateFlag_Hovered; | |
return hovered_node; | |
} | |
ImGuiNodesConnectionPair* ImGuiNodes::UpdateConnectionsFromCanvas() | |
{ | |
ImGuiNodesConnectionPair* ret = NULL; | |
ImVector<ImGuiNodesConnectionPair>::iterator it = nodes_connections_.begin(); | |
while ( it != nodes_connections_.end() ) | |
{ | |
// TODO: optimise this by first checking within canvas and checking within bezier bbox | |
if ( ret == NULL ) | |
{ | |
ImVec2 p1, p2, p3, p4; | |
p1 = (it->output->pos_ + pos_) * scale_; | |
p2 = p1 + (ImVec2(+50.0f, 0.0f) * scale_); | |
p3 = (it->input->pos_ + pos_) * scale_; | |
p4 = p3 + (ImVec2(-50.0f, 0.0f) * scale_); | |
const float distance_squared = GetSquaredDistanceToBezierCurve(mouse_, p1, p2, p3, p4); | |
if (distance_squared < (10.0f * 10.0f) ) | |
{ | |
it->state = ImGuiNodesConnectionStateFlag_Hovered; | |
ret = &*it; | |
} | |
else | |
{ | |
it->state = ImGuiNodesConnectionStateFlag_Default; | |
} | |
} | |
else | |
{ | |
it->state = ImGuiNodesConnectionStateFlag_Default; | |
} | |
++it; | |
} | |
return ret; | |
} | |
ImGuiNodesNode* ImGuiNodes::CreateNodeFromDesc(ImGuiNodesNodeDesc* desc, ImVec2 pos) | |
{ | |
ImGuiNodesNode* node = new ImGuiNodesNode(desc->name_, desc->type_); | |
ImVec2 inputs; | |
ImVec2 outputs; | |
//////////////////////////////////////////////////////////////////////////////// | |
for (int input_idx = 0; input_idx < ImGuiNodesConnectionsMaxNumber; ++input_idx) | |
{ | |
if (desc->inputs_[input_idx].type_ == ImGuiNodesConnectorType_None) | |
break; | |
ImGuiNodesInput* input = new ImGuiNodesInput(desc->inputs_[input_idx].name_, desc->inputs_[input_idx].type_); | |
inputs.x = ImMax(inputs.x, input->area_input_.GetWidth()); | |
inputs.y += input->area_input_.GetHeight(); | |
node->inputs_.push_back(input); | |
} | |
for (int output_idx = 0; output_idx < ImGuiNodesConnectionsMaxNumber; ++output_idx) | |
{ | |
if (desc->outputs_[output_idx].type_ == ImGuiNodesConnectorType_None) | |
break; | |
ImGuiNodesOutput* output = new ImGuiNodesOutput(desc->outputs_[output_idx].name_, desc->outputs_[output_idx].type_); | |
outputs.x = ImMax(outputs.x, output->area_output_.GetWidth()); | |
outputs.y += output->area_output_.GetHeight(); | |
node->outputs_.push_back(output); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
node->BuildNodeGeometry(inputs, outputs); | |
node->TranslateNode(pos - node->area_node_.GetCenter()); | |
node->state_ |= ImGuiNodesNodeStateFlag_Visible | ImGuiNodesNodeStateFlag_Hovered; | |
return node; | |
} | |
void ImGuiNodes::Update() | |
{ | |
ImGuiIO& io = ImGui::GetIO(); | |
//////////////////////////////////////////////////////////////////////////////// | |
if (UpdateCanvasGeometry(ImGui::GetWindowDrawList())) | |
{ | |
ImGuiNodesNode* hovered_node = UpdateNodesFromCanvas(); | |
ImGuiNodesConnectionPair* hovered_conn = NULL; | |
bool consider_hover = state_ == ImGuiNodesState_Default; | |
consider_hover |= state_ == ImGuiNodesState_HoveringNode; | |
consider_hover |= state_ == ImGuiNodesState_HoveringInput; | |
consider_hover |= state_ == ImGuiNodesState_HoveringOutput; | |
consider_hover |= state_ == ImGuiNodesState_HoveringConnection; | |
//////////////////////////////////////////////////////////////////////////////// | |
if (hovered_node && consider_hover) | |
{ | |
element_input_ = NULL; | |
element_output_ = NULL; | |
for (int input_idx = 0; input_idx < hovered_node->inputs_.size(); ++input_idx) | |
{ | |
if (hovered_node->inputs_[input_idx]->state_ & ImGuiNodesConnectorStateFlag_Hovered) | |
{ | |
element_input_ = hovered_node->inputs_[input_idx]; | |
state_ = ImGuiNodesState_HoveringInput; | |
break; | |
} | |
} | |
for (int output_idx = 0; output_idx < hovered_node->outputs_.size(); ++output_idx) | |
{ | |
if (hovered_node->outputs_[output_idx]->state_ & ImGuiNodesConnectorStateFlag_Hovered) | |
{ | |
element_output_ = hovered_node->outputs_[output_idx]; | |
state_ = ImGuiNodesState_HoveringOutput; | |
break; | |
} | |
} | |
if (!element_input_ && !element_output_) | |
state_ = ImGuiNodesState_HoveringNode; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
if (state_ == ImGuiNodesState_DraggingInput) | |
{ | |
element_output_ = NULL; | |
if (hovered_node) | |
for (int output_idx = 0; output_idx < hovered_node->outputs_.size(); ++output_idx) | |
{ | |
ImGuiNodesConnectorState state = hovered_node->outputs_[output_idx]->state_; | |
if (state & ImGuiNodesConnectorStateFlag_Hovered && state & ImGuiNodesConnectorStateFlag_Consider) | |
element_output_ = hovered_node->outputs_[output_idx]; | |
} | |
} | |
if (state_ == ImGuiNodesState_DraggingOutput) | |
{ | |
element_input_ = NULL; | |
if (hovered_node) | |
for (int input_idx = 0; input_idx < hovered_node->inputs_.size(); ++input_idx) | |
{ | |
ImGuiNodesConnectorState state = hovered_node->inputs_[input_idx]->state_; | |
if (state & ImGuiNodesConnectorStateFlag_Hovered && state & ImGuiNodesConnectorStateFlag_Consider) | |
element_input_ = hovered_node->inputs_[input_idx]; | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
if (consider_hover) | |
{ | |
element_node_ = hovered_node; | |
// we can only hover a connection if we're in ImGuiNodesState_Default | |
hovered_conn = UpdateConnectionsFromCanvas(); | |
if (!hovered_node) | |
{ | |
if ( hovered_conn ) | |
{ | |
state_ = ImGuiNodesState_HoveringConnection; | |
element_conn_ = hovered_conn; | |
} | |
else | |
state_ = ImGuiNodesState_Default; | |
} | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
if (ImGui::IsMouseDoubleClicked(0)) | |
{ | |
switch (state_) | |
{ | |
case ImGuiNodesState_Default: | |
{ | |
for (int node_idx = 0; node_idx < nodes_.size(); ++node_idx) | |
nodes_[node_idx]->state_ &= ~(ImGuiNodesNodeStateFlag_Selected | ImGuiNodesNodeStateFlag_Marked | ImGuiNodesNodeStateFlag_Hovered); | |
} break; | |
case ImGuiNodesState_HoveringInput: | |
case ImGuiNodesState_HoveringOutput: | |
case ImGuiNodesState_HoveringNode: | |
{ | |
if (element_node_->state_ & ImGuiNodesNodeStateFlag_Collapsed) | |
{ | |
element_node_->state_ &= ~ImGuiNodesNodeStateFlag_Collapsed; | |
element_node_->area_node_.Max.y += element_node_->body_height_; | |
element_node_->TranslateNode(ImVec2(0.0f, element_node_->body_height_ * -0.5f)); | |
} | |
else | |
{ | |
element_node_->state_ |= ImGuiNodesNodeStateFlag_Collapsed; | |
element_node_->area_node_.Max.y -= element_node_->body_height_; | |
element_node_->TranslateNode(ImVec2(0.0f, element_node_->body_height_ * 0.5f)); | |
} | |
state_ = ImGuiNodesState_Dragging; | |
} break; | |
case ImGuiNodesState_HoveringConnection: | |
{ | |
// Delete connection | |
element_conn_->input->connections_--; | |
element_conn_->output->connections_--; | |
nodes_connections_.erase(element_conn_); | |
element_conn_ = NULL; | |
} break; | |
} | |
return; | |
} | |
if (ImGui::IsMouseClicked(0)) | |
{ | |
switch (state_) | |
{ | |
case ImGuiNodesState_HoveringNode: | |
{ | |
if (io.KeyCtrl) | |
element_node_->state_ ^= ImGuiNodesNodeStateFlag_Selected; | |
if (io.KeyShift) | |
element_node_->state_ |= ImGuiNodesNodeStateFlag_Selected; | |
state_ = ImGuiNodesState_Dragging; | |
return; | |
} | |
case ImGuiNodesState_HoveringInput: | |
{ | |
state_ = ImGuiNodesState_DraggingInput; | |
return; | |
} | |
case ImGuiNodesState_HoveringOutput: | |
{ | |
state_ = ImGuiNodesState_DraggingOutput; | |
return; | |
} | |
} | |
return; | |
} | |
if (ImGui::IsMouseDragging(0)) | |
{ | |
switch (state_) | |
{ | |
case ImGuiNodesState_Default: | |
{ | |
ImRect canvas(pos_, pos_ + size_); | |
if (!canvas.Contains(mouse_)) | |
return; | |
ImVec2 pos = mouse_ - ImGui::GetMouseDragDelta(0); | |
area_ = ImRect(pos, pos); | |
if (!io.KeyShift) | |
for (int node_idx = 0; node_idx < nodes_.size(); ++node_idx) | |
nodes_[node_idx]->state_ &= ~(ImGuiNodesNodeStateFlag_Selected | ImGuiNodesNodeStateFlag_Marked); | |
state_ = ImGuiNodesState_Selecting; | |
return; | |
} | |
case ImGuiNodesState_Selecting: | |
{ | |
ImVec2 pos = mouse_ - ImGui::GetMouseDragDelta(0); | |
area_.Min = ImMin(pos, mouse_); | |
area_.Max = ImMax(pos, mouse_); | |
return; | |
} | |
case ImGuiNodesState_Dragging: | |
{ | |
if (!(element_node_->state_ & ImGuiNodesNodeStateFlag_Selected)) | |
element_node_->TranslateNode(io.MouseDelta / scale_, false); | |
else | |
for (int node_idx = 0; node_idx < nodes_.size(); ++node_idx) | |
nodes_[node_idx]->TranslateNode(io.MouseDelta / scale_, true); | |
return; | |
} | |
case ImGuiNodesState_DraggingInput: | |
{ | |
ImVec2 offset = pos_ + scroll_; | |
ImVec2 p1 = offset + (element_input_->pos_ * scale_); | |
ImVec2 p2 = p1 + (ImVec2(-50.0f, 0.0f) * scale_); | |
ImVec2 p4 = element_output_ ? (offset + (element_output_->pos_ * scale_)) : mouse_; | |
ImVec2 p3 = p4 + (ImVec2(+50.0f, 0.0f) * scale_); | |
ImGui::GetWindowDrawList()->AddBezierCurve(p1, p2, p3, p4, ImColor(0.0f, 1.0f, 0.0f, 1.0f), 2.0f * scale_); | |
return; | |
} | |
case ImGuiNodesState_DraggingOutput: | |
{ | |
ImVec2 offset = pos_ + scroll_; | |
ImVec2 p1 = offset + (element_output_->pos_ * scale_); | |
ImVec2 p2 = p1 + (ImVec2(+50.0f, 0.0f) * scale_); | |
ImVec2 p4 = element_input_ ? (offset + (element_input_->pos_ * scale_)) : mouse_; | |
ImVec2 p3 = p4 + (ImVec2(-50.0f, 0.0f) * scale_); | |
ImGui::GetWindowDrawList()->AddBezierCurve(p1, p2, p3, p4, ImColor(0.0f, 1.0f, 0.0f, 1.0f), 2.0f * scale_); | |
return; | |
} | |
} | |
return; | |
} | |
if (ImGui::IsMouseReleased(0)) | |
{ | |
switch (state_) | |
{ | |
case ImGuiNodesState_Selecting: | |
{ | |
area_ = ImRect(); | |
for (int node_idx = 0; node_idx < nodes_.size(); ++node_idx) | |
if (nodes_[node_idx]->state_ & ImGuiNodesNodeStateFlag_Marked) | |
{ | |
nodes_[node_idx]->state_ |= ImGuiNodesNodeStateFlag_Selected; | |
nodes_[node_idx]->state_ &= ~ImGuiNodesNodeStateFlag_Marked; | |
} | |
state_ = ImGuiNodesState_Default; | |
return; | |
} | |
case ImGuiNodesState_Dragging: | |
{ | |
state_ = ImGuiNodesState_HoveringNode; | |
return; | |
} | |
case ImGuiNodesState_DraggingInput: | |
case ImGuiNodesState_DraggingOutput: | |
{ | |
if (element_input_ && element_output_) | |
{ | |
// prevent duplicate | |
if ( findConnection(element_input_, element_output_) == NULL ) | |
{ | |
ImGuiNodesConnectionPair conn; // = ImGuiNodesConnectionPair(); | |
conn.input = element_input_; | |
conn.output = element_output_; | |
conn.input->connections_++; | |
conn.output->connections_++; | |
conn.state = ImGuiNodesConnectionStateFlag_Default; | |
nodes_connections_.push_back(conn); | |
} | |
// Trigger new connection!!! | |
} | |
state_ = ImGuiNodesState_Default; | |
return; | |
} | |
} | |
return; | |
} | |
} | |
void ImGuiNodes::ProcessNodes() | |
{ | |
ImDrawList* draw_list = ImGui::GetWindowDrawList(); | |
ImVec2 offset = pos_ + scroll_; | |
//////////////////////////////////////////////////////////////////////////////// | |
ImGui::SetWindowFontScale(scale_); | |
for ( int conn_idx = 0; conn_idx < nodes_connections_.size(); ++conn_idx ) | |
{ | |
ImGuiNodesConnectionPair conn = nodes_connections_[conn_idx]; | |
ImVec2 p1 = offset + (conn.input->pos_ * scale_); | |
ImVec2 p2 = p1 + (ImVec2(-50.0f, 0.0f) * scale_); | |
ImVec2 p4 = offset + (conn.output->pos_ * scale_); | |
ImVec2 p3 = p4 + (ImVec2(+50.0f, 0.0f) * scale_); | |
if ( conn.state == ImGuiNodesConnectionStateFlag_Hovered ) | |
draw_list->AddBezierCurve(p1, p2, p3, p4, ImColor(1.0f, 0.7f, 0.0f, 1.0f), 2.0f * scale_); | |
else | |
draw_list->AddBezierCurve(p1, p2, p3, p4, ImColor(1.0f, 1.0f, 1.0f, 1.0f), 2.0f * scale_); | |
} | |
for (int node_idx = 0; node_idx < nodes_.size(); ++node_idx) | |
nodes_[node_idx]->DrawNode(draw_list, offset, scale_); | |
ImGui::SetWindowFontScale(1.0f); | |
//////////////////////////////////////////////////////////////////////////////// | |
if (state_ == ImGuiNodesState_Selecting) | |
{ | |
draw_list->AddRectFilled(area_.Min, area_.Max, ImColor(1.0f, 1.0f, 0.0f, 0.1f)); | |
draw_list->AddRect(area_.Min, area_.Max, ImColor(1.0f, 1.0f, 0.0f, 0.5f)); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
ImGui::SetCursorPos(ImVec2(0.0f, 0.0f)); | |
ImGui::NewLine(); | |
switch (state_) | |
{ | |
case ImGuiNodesState_Default: ImGui::Text("ImGuiNodesState_Default"); break; | |
case ImGuiNodesState_HoveringNode: ImGui::Text("ImGuiNodesState_HoveringNode"); break; | |
case ImGuiNodesState_HoveringInput: ImGui::Text("ImGuiNodesState_HoveringInput"); break; | |
case ImGuiNodesState_HoveringOutput: ImGui::Text("ImGuiNodesState_HoveringOutput"); break; | |
case ImGuiNodesState_Dragging: ImGui::Text("ImGuiNodesState_Dragging"); break; | |
case ImGuiNodesState_DraggingInput: ImGui::Text("ImGuiNodesState_DraggingInput"); break; | |
case ImGuiNodesState_DraggingOutput: ImGui::Text("ImGuiNodesState_DraggingOutput"); break; | |
case ImGuiNodesState_Selecting: ImGui::Text("ImGuiNodesState_Selecting"); break; | |
case ImGuiNodesState_HoveringConnection: ImGui::Text("ImGuiNodesState_HoveringConnection"); break; | |
default: ImGui::Text("UNKNOWN"); break; | |
} | |
ImGui::NewLine(); | |
ImGui::Text("Position: %.2f, %.2f", pos_.x, pos_.y); | |
ImGui::Text("Size: %.2f, %.2f", size_.x, size_.y); | |
ImGui::Text("Mouse: %.2f, %.2f", mouse_.x, mouse_.y); | |
ImGui::Text("Scroll: %.2f, %.2f", scroll_.x, scroll_.y); | |
ImGui::Text("Scale: %.2f", scale_); | |
ImGui::NewLine(); | |
if (element_node_) | |
ImGui::Text("Element_node: %s", element_node_->name_); | |
if (element_input_) | |
ImGui::Text("Element_input: %s", element_input_->name_); | |
if (element_output_) | |
ImGui::Text("Element_output: %s", element_output_->name_); | |
//////////////////////////////////////////////////////////////////////////////// | |
} | |
void ImGuiNodes::ProcessContextMenu() | |
{ | |
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8)); | |
if (ImGui::BeginPopup("NodesContextMenu")) | |
{ | |
for (int node_idx = 0; node_idx < nodes_desc_.size(); ++node_idx) | |
if (ImGui::MenuItem(nodes_desc_[node_idx].name_)) | |
{ | |
ImGuiNodesNode* node = CreateNodeFromDesc(&nodes_desc_[node_idx], (mouse_ - scroll_ - pos_) / scale_); | |
nodes_.push_back(node); | |
} | |
ImGui::EndPopup(); | |
} | |
ImGui::PopStyleVar(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#define IMGUI_DEFINE_MATH_OPERATORS | |
#include "imgui.h" | |
#include "imgui_internal.h" | |
namespace ImGui | |
{ | |
//////////////////////////////////////////////////////////////////////////////// | |
enum ImGuiNodesConnectorType_ | |
{ | |
ImGuiNodesConnectorType_None = 0, | |
ImGuiNodesConnectorType_Invisible, | |
ImGuiNodesConnectorType_Generic, | |
ImGuiNodesConnectorType_Int, | |
ImGuiNodesConnectorType_Float, | |
ImGuiNodesConnectorType_Vector, | |
ImGuiNodesConnectorType_Image, | |
ImGuiNodesConnectorType_Text | |
}; | |
enum ImGuiNodesNodeType_ | |
{ | |
ImGuiNodesNodeType_None = 0, | |
ImGuiNodesNodeType_Generic, | |
ImGuiNodesNodeType_Generator, | |
ImGuiNodesNodeType_Test | |
}; | |
enum ImGuiNodesConnectorStateFlag_ | |
{ | |
ImGuiNodesConnectorStateFlag_Default = 0, | |
ImGuiNodesConnectorStateFlag_Visible = 1 << 0, | |
ImGuiNodesConnectorStateFlag_Hovered = 1 << 1, | |
ImGuiNodesConnectorStateFlag_Consider = 1 << 2, | |
ImGuiNodesConnectorStateFlag_Dragging = 1 << 3 | |
}; | |
enum ImGuiNodesConnectionStateFlag_ | |
{ | |
ImGuiNodesConnectionStateFlag_Default = 0, | |
ImGuiNodesConnectionStateFlag_Hovered = 1 << 0, | |
ImGuiNodesConnectionStateFlag_Selected = 1 << 1, | |
ImGuiNodesConnectionStateFlag_Consider = 1 << 2 | |
}; | |
enum ImGuiNodesNodeStateFlag_ | |
{ | |
ImGuiNodesNodeStateFlag_Default = 0, | |
ImGuiNodesNodeStateFlag_Visible = 1 << 0, | |
ImGuiNodesNodeStateFlag_Hovered = 1 << 1, | |
ImGuiNodesNodeStateFlag_Marked = 1 << 2, | |
ImGuiNodesNodeStateFlag_Selected = 1 << 3, | |
ImGuiNodesNodeStateFlag_Collapsed = 1 << 4, | |
ImGuiNodesNodeStateFlag_Disabled = 1 << 5 | |
}; | |
enum ImGuiNodesState_ | |
{ | |
ImGuiNodesState_Default = 0, | |
ImGuiNodesState_HoveringNode, | |
ImGuiNodesState_HoveringInput, | |
ImGuiNodesState_HoveringOutput, | |
ImGuiNodesState_Dragging, | |
ImGuiNodesState_DraggingInput, | |
ImGuiNodesState_DraggingOutput, | |
ImGuiNodesState_Selecting, | |
ImGuiNodesState_HoveringConnection | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
typedef unsigned int ImGuiNodesConnectorType; | |
typedef unsigned int ImGuiNodesNodeType; | |
typedef unsigned int ImGuiNodesConnectorState; | |
typedef unsigned int ImGuiNodesConnectionState; | |
typedef unsigned int ImGuiNodesNodeState; | |
typedef unsigned int ImGuiNodesState; | |
//////////////////////////////////////////////////////////////////////////////// | |
// connector text name heights factors | |
static const float ImGuiNodesConnectorDotDiameter = 0.7f; // connectors dot diameter | |
static const float ImGuiNodesConnectorDotPadding = 0.35f; // connectors dot left/right sides padding | |
static const float ImGuiNodesConnectorDistance = 1.5f; // vertical distance between connectors centers | |
// title text name heights factors | |
static const float ImGuiNodesHSeparation = 1.7f; // extra horizontal separation inbetween IOs | |
static const float ImGuiNodesVSeparation = 1.5f; // total IOs area separation from title and node bottom edge | |
static const float ImGuiNodesTitleHight = 2.0f; | |
struct ImGuiNodesNode; | |
struct ImGuiNodesInput; | |
struct ImGuiNodesOutput; | |
struct ImGuiNodesConnectionPair; | |
//////////////////////////////////////////////////////////////////////////////// | |
struct ImGuiNodesInput | |
{ | |
ImVec2 pos_; | |
ImRect area_input_; | |
ImRect area_name_; | |
ImGuiNodesConnectorType type_; | |
ImGuiNodesConnectorState state_; | |
ImGuiNodesNode* owner; | |
u_int8_t connections_; | |
const char* name_; | |
inline void TranslateInput(ImVec2 delta) | |
{ | |
pos_ += delta; | |
area_input_.Translate(delta); | |
area_name_.Translate(delta); | |
} | |
inline void DrawInput(ImDrawList* draw_list, ImVec2 offset, float scale) const | |
{ | |
if (type_ == ImGuiNodesConnectorType_Invisible) | |
return; | |
if (state_ & ImGuiNodesConnectorStateFlag_Hovered && !(state_ & ImGuiNodesConnectorStateFlag_Consider)) | |
draw_list->AddRectFilled((area_input_.Min * scale) + offset, (area_input_.Max * scale) + offset, ImColor(0.0f, 0.0f, 1.0f, 0.5f)); | |
if (state_ & (ImGuiNodesConnectorStateFlag_Consider | ImGuiNodesConnectorStateFlag_Dragging)) | |
draw_list->AddRectFilled((area_input_.Min * scale) + offset, (area_input_.Max * scale) + offset, ImColor(0.0f, 1.0f, 0.0f, 0.5f)); | |
ImColor color = ImColor(1.0f, 1.0f, 1.0f, 1.0f); | |
bool consider_fill = false; | |
consider_fill |= bool(state_ & ImGuiNodesConnectorStateFlag_Dragging); | |
consider_fill |= bool(state_ & ImGuiNodesConnectorStateFlag_Hovered && state_ & ImGuiNodesConnectorStateFlag_Consider); | |
if (consider_fill) | |
color = ImColor(0.0f, 1.0f, 0.0f, 1.0f); | |
consider_fill |= bool(connections_); | |
if (consider_fill) | |
draw_list->AddCircleFilled((pos_ * scale) + offset, (ImGuiNodesConnectorDotDiameter * 0.5f) * area_name_.GetHeight() * scale, color); | |
else | |
draw_list->AddCircle((pos_ * scale) + offset, (ImGuiNodesConnectorDotDiameter * 0.5f) * area_name_.GetHeight() * scale, color); | |
ImGui::SetCursorScreenPos((area_name_.Min * scale) + offset); | |
ImGui::Text(name_); | |
} | |
ImGuiNodesInput(const char* name, ImGuiNodesConnectorType type) | |
{ | |
type_ = type; | |
state_ = ImGuiNodesConnectorStateFlag_Default; | |
name_ = name; | |
area_name_.Min = ImVec2(0.0f, 0.0f); | |
area_name_.Max = ImGui::CalcTextSize(name); | |
area_input_.Min = ImVec2(0.0f, 0.0f); | |
area_input_.Max.x = ImGuiNodesConnectorDotPadding + ImGuiNodesConnectorDotDiameter + ImGuiNodesConnectorDotPadding; | |
area_input_.Max.y = ImGuiNodesConnectorDistance; | |
area_input_.Max *= area_name_.GetHeight(); | |
ImVec2 offset = ImVec2(0.0f, 0.0f) - area_input_.GetCenter(); | |
offset.x = -2.0f; //put connectors slightly over the border | |
area_name_.Translate(ImVec2(area_input_.GetWidth(), (area_input_.GetHeight() - area_name_.GetHeight()) * 0.5f)); | |
area_input_.Max.x += area_name_.GetWidth(); | |
area_input_.Max.x += ImGuiNodesConnectorDotPadding * area_name_.GetHeight(); | |
area_input_.Translate(offset); | |
area_name_.Translate(offset); | |
connections_ = 0; | |
} | |
}; | |
struct ImGuiNodesOutput | |
{ | |
ImVec2 pos_; | |
ImRect area_output_; | |
ImRect area_name_; | |
ImGuiNodesConnectorType type_; | |
ImGuiNodesConnectorState state_; | |
const char* name_; | |
unsigned int connections_; | |
inline void TranslateOutput(ImVec2 delta) | |
{ | |
pos_ += delta; | |
area_output_.Translate(delta); | |
area_name_.Translate(delta); | |
} | |
inline void DrawOutput(ImDrawList* draw_list, ImVec2 offset, float scale) const | |
{ | |
if (type_ == ImGuiNodesConnectorType_Invisible) | |
return; | |
if (state_ & ImGuiNodesConnectorStateFlag_Hovered && !(state_ & ImGuiNodesConnectorStateFlag_Consider)) | |
draw_list->AddRectFilled((area_output_.Min * scale) + offset, (area_output_.Max * scale) + offset, ImColor(0.0f, 0.0f, 1.0f, 0.5f)); | |
if (state_ & (ImGuiNodesConnectorStateFlag_Consider | ImGuiNodesConnectorStateFlag_Dragging)) | |
draw_list->AddRectFilled((area_output_.Min * scale) + offset, (area_output_.Max * scale) + offset, ImColor(0.0f, 1.0f, 0.0f, 0.5f)); | |
ImColor color = ImColor(1.0f, 1.0f, 1.0f, 1.0f); | |
bool consider_fill = false; | |
consider_fill |= bool(state_ & ImGuiNodesConnectorStateFlag_Dragging); | |
consider_fill |= bool(state_ & ImGuiNodesConnectorStateFlag_Hovered && state_ & ImGuiNodesConnectorStateFlag_Consider); | |
if (consider_fill) | |
color = ImColor(0.0f, 1.0f, 0.0f, 1.0f); | |
consider_fill |= bool(connections_ > 0); | |
if (consider_fill) | |
draw_list->AddCircleFilled((pos_ * scale) + offset, (ImGuiNodesConnectorDotDiameter * 0.5f) * area_name_.GetHeight() * scale, color); | |
else | |
draw_list->AddCircle((pos_ * scale) + offset, (ImGuiNodesConnectorDotDiameter * 0.5f) * area_name_.GetHeight() * scale, color); | |
ImGui::SetCursorScreenPos((area_name_.Min * scale) + offset); | |
ImGui::Text(name_); | |
} | |
ImGuiNodesOutput(const char* name, ImGuiNodesConnectorType type) | |
{ | |
type_ = type; | |
state_ = ImGuiNodesConnectorStateFlag_Default; | |
connections_ = 0; | |
name_ = name; | |
area_name_.Min = ImVec2(0.0f, 0.0f) - ImGui::CalcTextSize(name); | |
area_name_.Max = ImVec2(0.0f, 0.0f); | |
area_output_.Min.x = ImGuiNodesConnectorDotPadding + ImGuiNodesConnectorDotDiameter + ImGuiNodesConnectorDotPadding; | |
area_output_.Min.y = ImGuiNodesConnectorDistance; | |
area_output_.Min *= -area_name_.GetHeight(); | |
area_output_.Max = ImVec2(0.0f, 0.0f); | |
ImVec2 offset = ImVec2(0.0f, 0.0f) - area_output_.GetCenter(); | |
offset.x = 2.0f; //put connectors slightly over the border | |
area_name_.Translate(ImVec2(area_output_.Min.x, (area_output_.GetHeight() - area_name_.GetHeight()) * -0.5f)); | |
area_output_.Min.x -= area_name_.GetWidth(); | |
area_output_.Min.x -= ImGuiNodesConnectorDotPadding * area_name_.GetHeight(); | |
area_output_.Translate(offset); | |
area_name_.Translate(offset); | |
} | |
}; | |
struct ImGuiNodesNode | |
{ | |
ImRect area_node_; | |
ImRect area_name_; | |
float title_height_; | |
float body_height_; | |
ImGuiNodesNodeState state_; | |
ImGuiNodesNodeType type_; | |
const char* name_; | |
ImVector<ImGuiNodesInput*> inputs_; | |
ImVector<ImGuiNodesOutput*> outputs_; | |
inline void TranslateNode(ImVec2 delta, bool selected_only = false) | |
{ | |
if (selected_only && !(state_ & ImGuiNodesNodeStateFlag_Selected)) | |
return; | |
area_node_.Translate(delta); | |
area_name_.Translate(delta); | |
for (int input_idx = 0; input_idx < inputs_.size(); ++input_idx) | |
inputs_[input_idx]->TranslateInput(delta); | |
for (int output_idx = 0; output_idx < outputs_.size(); ++output_idx) | |
outputs_[output_idx]->TranslateOutput(delta); | |
} | |
inline void BuildNodeGeometry(ImVec2 inputs_size, ImVec2 outputs_size) | |
{ | |
body_height_ = ImMax(inputs_size.y, outputs_size.y) + (ImGuiNodesVSeparation * area_name_.GetHeight()); | |
area_node_.Min = ImVec2(0.0f, 0.0f); | |
area_node_.Max = ImVec2(0.0f, 0.0f); | |
area_node_.Max.x += inputs_size.x + outputs_size.x; | |
area_node_.Max.x += ImGuiNodesHSeparation * area_name_.GetHeight(); | |
area_node_.Max.y += title_height_ + body_height_; | |
area_name_.Translate(ImVec2((area_node_.GetWidth() - area_name_.GetWidth()) * 0.5f, ((title_height_ - area_name_.GetHeight()) * 0.5f))); | |
ImVec2 inputs = area_node_.GetTL(); | |
inputs.y += title_height_ + (ImGuiNodesVSeparation * area_name_.GetHeight() * 0.5f); | |
for (int input_idx = 0; input_idx < inputs_.size(); ++input_idx) | |
{ | |
inputs_[input_idx]->TranslateInput(inputs - inputs_[input_idx]->area_input_.GetTL()); | |
inputs.y += inputs_[input_idx]->area_input_.GetHeight(); | |
} | |
ImVec2 outputs = area_node_.GetTR(); | |
outputs.y += title_height_ + (ImGuiNodesVSeparation * area_name_.GetHeight() * 0.5f); | |
for (int output_idx = 0; output_idx < outputs_.size(); ++output_idx) | |
{ | |
outputs_[output_idx]->TranslateOutput(outputs - outputs_[output_idx]->area_output_.GetTR()); | |
outputs.y += outputs_[output_idx]->area_output_.GetHeight(); | |
} | |
} | |
inline void DrawNode(ImDrawList* draw_list, ImVec2 offset, float scale) const | |
{ | |
if (!(state_ & ImGuiNodesNodeStateFlag_Visible)) | |
return; | |
ImRect node_rect = area_node_; | |
node_rect.Min *= scale; | |
node_rect.Max *= scale; | |
node_rect.Translate(offset); | |
float rounding = title_height_ * scale * 0.3f; | |
int rounding_corners_flags = state_ & ImGuiNodesNodeStateFlag_Collapsed ? ImDrawCornerFlags_All : ImDrawCornerFlags_Top; | |
draw_list->AddRectFilled(node_rect.Min, node_rect.Max, ImColor(0.5f, 0.5f, 0.5f, 0.5f), rounding, rounding_corners_flags); | |
draw_list->AddRectFilled(node_rect.Min, node_rect.GetTR() + ImVec2(0.0f, title_height_ * scale), ImColor(0.5f, 0.5f, 0.5f, 0.5f), rounding, rounding_corners_flags); | |
if (!(state_ & ImGuiNodesNodeStateFlag_Collapsed)) | |
{ | |
for (int input_idx = 0; input_idx < inputs_.size(); ++input_idx) | |
inputs_[input_idx]->DrawInput(draw_list, offset, scale); | |
for (int output_idx = 0; output_idx < outputs_.size(); ++output_idx) | |
outputs_[output_idx]->DrawOutput(draw_list, offset, scale); | |
} | |
ImGui::SetCursorScreenPos((area_name_.Min * scale) + offset); | |
ImGui::Text(name_); | |
if (state_ & (ImGuiNodesNodeStateFlag_Marked | ImGuiNodesNodeStateFlag_Selected)) | |
draw_list->AddRectFilled(node_rect.Min, node_rect.Max, ImColor(1.0f, 1.0f, 1.0f, 0.25f), rounding, rounding_corners_flags); | |
else | |
if (state_ & ImGuiNodesNodeStateFlag_Hovered) | |
draw_list->AddRect(node_rect.Min, node_rect.Max, ImColor(1.0f, 1.0f, 1.0f, 0.5f), rounding, rounding_corners_flags); | |
} | |
ImGuiNodesNode(const char* name, ImGuiNodesNodeType type) | |
{ | |
name_ = name; | |
type_ = type; | |
state_ = ImGuiNodesNodeStateFlag_Default; | |
area_name_.Min = ImVec2(0.0f, 0.0f); | |
area_name_.Max = ImGui::CalcTextSize(name); | |
title_height_ = ImGuiNodesTitleHight * area_name_.GetHeight(); | |
} | |
}; | |
struct ImGuiNodesConnectionPair { | |
ImGuiNodesNode* input_node; | |
ImGuiNodesInput* input; | |
ImGuiNodesNode* output_node; | |
ImGuiNodesOutput* output; | |
ImGuiNodesConnectionState state; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
static const int ImGuiNodesNamesMaxLen = 32; | |
static const int ImGuiNodesConnectionsMaxNumber = 16; | |
struct ImGuiNodesConnectionDesc | |
{ | |
char name_[ImGuiNodesNamesMaxLen]; | |
ImGuiNodesConnectorType type_; | |
}; | |
struct ImGuiNodesNodeDesc | |
{ | |
char name_[ImGuiNodesNamesMaxLen]; | |
ImGuiNodesNodeType type_; | |
ImGuiNodesConnectionDesc inputs_[ImGuiNodesConnectionsMaxNumber]; | |
ImGuiNodesConnectionDesc outputs_[ImGuiNodesConnectionsMaxNumber]; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
struct ImGuiNodes | |
{ | |
private: | |
ImVec2 mouse_; | |
ImVec2 pos_; | |
ImVec2 size_; | |
ImVec2 scroll_; | |
float scale_; | |
//////////////////////////////////////////////////////////////////////////////// | |
ImGuiNodesState state_; | |
ImRect area_; | |
ImGuiNodesNode* element_node_; | |
ImGuiNodesInput* element_input_; | |
ImGuiNodesOutput* element_output_; | |
ImGuiNodesConnectionPair* element_conn_; | |
//////////////////////////////////////////////////////////////////////////////// | |
ImVector<ImGuiNodesNode*> nodes_; | |
ImVector<ImGuiNodesNodeDesc> nodes_desc_; | |
ImVector<ImGuiNodesConnectionPair> nodes_connections_; | |
//////////////////////////////////////////////////////////////////////////////// | |
private: | |
bool UpdateCanvasGeometry(ImDrawList* draw_list); | |
ImGuiNodesNode* UpdateNodesFromCanvas(); | |
ImGuiNodesConnectionPair* UpdateConnectionsFromCanvas(); | |
ImGuiNodesNode* CreateNodeFromDesc(ImGuiNodesNodeDesc* desc, ImVec2 pos); | |
inline bool ConnectionMatrix(ImGuiNodesNode* input_node, ImGuiNodesNode* output_node, ImGuiNodesInput* input, ImGuiNodesOutput* output) | |
{ | |
//UNREFERENCED_PARAMETER(input_node); | |
//UNREFERENCED_PARAMETER(output_node); | |
// if connection already exists? | |
if ( findConnection(input, output) != NULL ) | |
return false; | |
// if connection is valid | |
if (input->type_ == output->type_) | |
return true; | |
if (input->type_ == ImGuiNodesConnectorType_Generic) | |
return true; | |
if (output->type_ == ImGuiNodesConnectorType_Generic) | |
return true; | |
return false; | |
} | |
inline ImGuiNodesConnectionPair* findConnection(ImGuiNodesInput* input, ImGuiNodesOutput* output) | |
{ | |
ImVector<ImGuiNodesConnectionPair>::iterator it = nodes_connections_.begin(); | |
while (it != nodes_connections_.end() ) | |
{ | |
if (it->input == input && it->output == output) return &*it; | |
++it; | |
} | |
return NULL; | |
} | |
public: | |
void Update(); | |
void ProcessNodes(); | |
void ProcessContextMenu(); | |
ImGuiNodes() | |
{ | |
scale_ = 1.0f; | |
state_ = ImGuiNodesState_Default; | |
element_node_ = NULL; | |
element_input_ = NULL; | |
element_output_ = NULL; | |
//////////////////////////////////////////////////////////////////////////////// | |
ImGuiNodesNodeDesc nd = { | |
"TestTest", ImGuiNodesNodeType_Generic, | |
{ // inputs | |
{ "Float", ImGuiNodesConnectorType_Float }, | |
{ "Int", ImGuiNodesConnectorType_Int }, | |
{ "TextStream", ImGuiNodesConnectorType_Text } | |
}, | |
{ // outputs | |
{ "Float", ImGuiNodesConnectorType_Float } | |
} | |
}; | |
nodes_desc_.push_back( nd ); | |
ImGuiNodesNodeDesc nd2 = { | |
"InputBox", ImGuiNodesNodeType_Generic, | |
{ // inputs | |
{ "Float1", ImGuiNodesConnectorType_Float }, | |
{ "Float2", ImGuiNodesConnectorType_Float }, | |
{ "Int1", ImGuiNodesConnectorType_Int }, | |
{ "Int2", ImGuiNodesConnectorType_Int }, | |
{ "", ImGuiNodesConnectorType_Invisible }, | |
{ "GenericSink", ImGuiNodesConnectorType_Generic }, | |
{ "", ImGuiNodesConnectorType_Invisible }, | |
{ "Vector", ImGuiNodesConnectorType_Vector }, | |
{ "Image", ImGuiNodesConnectorType_Image }, | |
{ "Text", ImGuiNodesConnectorType_Text } | |
}, | |
{ // outputs | |
{ "TextStream", ImGuiNodesConnectorType_Text }, | |
{ "", ImGuiNodesConnectorType_Invisible }, | |
{ "Float", ImGuiNodesConnectorType_Float }, | |
{ "", ImGuiNodesConnectorType_Invisible }, | |
{ "Int", ImGuiNodesConnectorType_Int } | |
} | |
}; | |
nodes_desc_.push_back( nd2 ); | |
ImGuiNodesNodeDesc nd3 = { | |
"OutputBox", ImGuiNodesNodeType_Generic, | |
{ // inputs | |
{ "GenericSink1", ImGuiNodesConnectorType_Generic }, | |
{ "GenericSink2", ImGuiNodesConnectorType_Generic }, | |
{ "", ImGuiNodesConnectorType_Invisible }, | |
{ "Float", ImGuiNodesConnectorType_Float }, | |
{ "Int", ImGuiNodesConnectorType_Int }, | |
{ "Text", ImGuiNodesConnectorType_Text }, | |
}, | |
{ // outputs | |
{ "Vector", ImGuiNodesConnectorType_Vector }, | |
{ "Image", ImGuiNodesConnectorType_Image }, | |
{ "Text", ImGuiNodesConnectorType_Text }, | |
{ "", ImGuiNodesConnectorType_Invisible }, | |
{ "Float", ImGuiNodesConnectorType_Float }, | |
{ "Int", ImGuiNodesConnectorType_Int }, | |
{ "", ImGuiNodesConnectorType_Invisible }, | |
{ "", ImGuiNodesConnectorType_Invisible }, | |
{ "", ImGuiNodesConnectorType_Invisible }, | |
{ "Generic", ImGuiNodesConnectorType_Generic } | |
} | |
}; | |
nodes_desc_.push_back( nd3 ); | |
//////////////////////////////////////////////////////////////////////////////// | |
} | |
~ImGuiNodes() | |
{ | |
for (int node_idx = 0; node_idx < nodes_.size(); ++node_idx) | |
{ | |
ImGuiNodesNode* node = nodes_[node_idx]; | |
for (int input_idx = 0; input_idx < node->inputs_.size(); ++input_idx) | |
delete node->inputs_[input_idx]; | |
for (int output_idx = 0; output_idx < node->outputs_.size(); ++output_idx) | |
delete node->outputs_[output_idx]; | |
delete node; | |
} | |
} | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
} |
Better have a look at: https://github.com/rokups/ImNodes
Better have a look at: https://github.com/rokups/ImNodes
ok, thanks!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, do you have sample code to try it out?