Commit 78e65437 authored by Gauthier Quesnel's avatar Gauthier Quesnel
Browse files

imnodes: bump to v0.5pre

parent 2e4a0705
......@@ -3,7 +3,7 @@ project(irritator-gui VERSION 0.1.0 LANGUAGES CXX)
set(gui_sources
application.cpp application.hpp dialog.cpp dialog.hpp imnodes.cpp
imnodes.hpp implot.h implot.cpp internal.hpp internal.cpp
imnodes.h imnodes_internal.h implot.h implot.cpp internal.hpp internal.cpp
node-editor.cpp simulation-editor.cpp sources.cpp window-logger.cpp
${PROJECT_SOURCE_DIR}/../../external/imgui/imgui.cpp
${PROJECT_SOURCE_DIR}/../../external/imgui/imgui.h
......
......@@ -18,9 +18,9 @@ application::init()
return false;
}
if (auto* ed = alloc_editor(); ed) {
ed->context = imnodes::EditorContextCreate();
ed->settings.compute_colors();
if (auto* ed = alloc_editor(); !ed) {
std::fprintf(stderr, "Fail to initialize editor\n");
return false;
}
try {
......@@ -70,7 +70,7 @@ application::show()
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("New")) {
if (auto* ed = alloc_editor(); ed)
ed->context = imnodes::EditorContextCreate();
ed->context = ImNodes::EditorContextCreate();
}
ImGui::Separator();
......@@ -213,7 +213,7 @@ application::shutdown()
{
editor* ed = nullptr;
while (editors.next(ed))
imnodes::EditorContextFree(ed->context);
ImNodes::EditorContextFree(ed->context);
}
static void
......
......@@ -14,7 +14,7 @@
#include <variant>
#include <vector>
#include "imnodes.hpp"
#include "imnodes.h"
#include "implot.h"
#include <imgui.h>
......@@ -224,7 +224,7 @@ struct editor
{
small_string<16> name;
std::filesystem::path path;
imnodes::EditorContext* context = nullptr;
ImNodesEditorContext* context = nullptr;
bool show = true;
simulation sim;
......@@ -243,6 +243,9 @@ struct editor
editor_status st = editor_status::editing;
status sim_st = status::success;
editor() noexcept;
~editor() noexcept;
bool is_running() const noexcept
{
return match(st,
......
// the structure of this file:
//
// [SECTION] internal data structures
// [SECTION] global struct
// [SECTION] editor context definition
// [SECTION] bezier curve helpers
// [SECTION] draw list helper
// [SECTION] ObjectPool implementation
// [SECTION] ui state logic
// [SECTION] render helpers
// [SECTION] API implementation
#include "imnodes.hpp"
#include "imnodes.h"
#include "imnodes_internal.h"
#include <imgui.h>
#define IMGUI_DEFINE_MATH_OPERATORS
......@@ -18,8 +16,7 @@
// Check minimum ImGui version
#define MINIMUM_COMPATIBLE_IMGUI_VERSION 17400
#if IMGUI_VERSION_NUM < MINIMUM_COMPATIBLE_IMGUI_VERSION
#error \
"Minimum ImGui version requirement not met -- please use a newer version!"
#error "Minimum ImGui version requirement not met -- please use a newer version!"
#endif
#include <assert.h>
......@@ -31,399 +28,54 @@
#include <stdlib.h>
#include <string.h> // strlen, strncmp
namespace imnodes {
namespace {
enum ScopeFlags
{
Scope_None = 1,
Scope_Editor = 1 << 1,
Scope_Node = 1 << 2,
Scope_Attribute = 1 << 3
};
ImNodesContext* GImNodes = NULL;
enum AttributeType
namespace ImNodes
{
AttributeType_None,
AttributeType_Input,
AttributeType_Output
};
enum ElementStateChange
namespace
{
ElementStateChange_None = 0,
ElementStateChange_LinkStarted = 1 << 0,
ElementStateChange_LinkDropped = 1 << 1,
ElementStateChange_LinkCreated = 1 << 2
};
// [SECTION] bezier curve helpers
// [SECTION] internal data structures
// The object T must have the following interface:
//
// struct T
// {
// T();
//
// int id;
// };
template<typename T>
struct ObjectPool
{
ImVector<T> pool;
ImVector<bool> in_use;
ImVector<int> free_list;
ImGuiStorage id_map;
ObjectPool()
: pool()
, in_use()
, free_list()
, id_map()
{}
};
// Emulates std::optional<int> using the sentinel value `invalid_index`.
struct OptionalIndex
struct CubicBezier
{
OptionalIndex()
: m_index(invalid_index)
{}
OptionalIndex(const int value)
: m_index(value)
{}
// Observers
inline bool has_value() const
{
return m_index != invalid_index;
}
inline int value() const
{
assert(has_value());
return m_index;
}
// Modifiers
inline OptionalIndex& operator=(const int value)
{
m_index = value;
return *this;
}
inline void reset()
{
m_index = invalid_index;
}
inline bool operator==(const OptionalIndex& rhs) const
{
return m_index == rhs.m_index;
}
inline bool operator==(const int rhs) const
{
return m_index == rhs;
}
inline bool operator!=(const OptionalIndex& rhs) const
{
return m_index != rhs.m_index;
}
inline bool operator!=(const int rhs) const
{
return m_index != rhs;
}
static const int invalid_index = -1;
private:
int m_index;
};
struct NodeData
{
int id;
ImVec2 origin; // The node origin is in editor space
ImRect title_bar_content_rect;
ImRect rect;
struct
{
ImU32 background, background_hovered, background_selected, outline,
titlebar, titlebar_hovered, titlebar_selected;
} color_style;
struct
{
float corner_rounding;
ImVec2 padding;
float border_thickness;
} layout_style;
ImVector<int> pin_indices;
bool draggable;
NodeData(const int node_id)
: id(node_id)
, origin(100.0f, 100.0f)
, title_bar_content_rect()
, rect(ImVec2(0.0f, 0.0f), ImVec2(0.0f, 0.0f))
, color_style()
, layout_style()
, pin_indices()
, draggable(true)
{}
~NodeData()
{
id = INT_MIN;
}
ImVec2 P0, P1, P2, P3;
int NumSegments;
};
struct PinData
{
int id;
int parent_node_idx;
ImRect attribute_rect;
AttributeType type;
PinShape shape;
ImVec2 pos; // screen-space coordinates
int flags;
struct
{
ImU32 background, hovered;
} color_style;
PinData(const int pin_id)
: id(pin_id)
, parent_node_idx()
, attribute_rect()
, type(AttributeType_None)
, shape(PinShape_CircleFilled)
, pos()
, flags(AttributeFlags_None)
, color_style()
{}
};
struct LinkData
{
int id;
int start_pin_idx, end_pin_idx;
struct
{
ImU32 base, hovered, selected;
} color_style;
LinkData(const int link_id)
: id(link_id)
, start_pin_idx()
, end_pin_idx()
, color_style()
{}
};
struct LinkPredicate
{
bool operator()(const LinkData& lhs, const LinkData& rhs) const
{
// Do a unique compare by sorting the pins' addresses.
// This catches duplicate links, whether they are in the
// same direction or not.
// Sorting by pin index should have the uniqueness guarantees as sorting
// by id -- each unique id will get one slot in the link pool array.
int lhs_start = lhs.start_pin_idx;
int lhs_end = lhs.end_pin_idx;
int rhs_start = rhs.start_pin_idx;
int rhs_end = rhs.end_pin_idx;
if (lhs_start > lhs_end) {
ImSwap(lhs_start, lhs_end);
}
if (rhs_start > rhs_end) {
ImSwap(rhs_start, rhs_end);
}
return lhs_start == rhs_start && lhs_end == rhs_end;
}
};
struct BezierCurve
{
// the curve control points
ImVec2 p0, p1, p2, p3;
};
struct LinkBezierData
{
BezierCurve bezier;
int num_segments;
};
enum ClickInteractionType
{
ClickInteractionType_Node,
ClickInteractionType_Link,
ClickInteractionType_LinkCreation,
ClickInteractionType_Panning,
ClickInteractionType_BoxSelection,
ClickInteractionType_None
};
enum LinkCreationType
{
LinkCreationType_Standard,
LinkCreationType_FromDetach
};
struct ClickInteractionState
{
struct
{
int start_pin_idx;
OptionalIndex end_pin_idx;
LinkCreationType link_creation_type;
} link_creation;
struct
{
ImRect rect;
} box_selector;
};
struct ColorStyleElement
{
ImU32 color;
ColorStyle item;
ColorStyleElement(const ImU32 c, const ColorStyle s)
: color(c)
, item(s)
{}
};
struct StyleElement
{
StyleVar item;
float value;
StyleElement(const float value, const StyleVar variable)
: item(variable)
, value(value)
{}
};
} // namespace
// [SECTION] global struct
// this stores data which only lives for one frame
struct Context
{
EditorContext* default_editor_ctx;
EditorContext* editor_ctx;
// Canvas draw list and helper state
ImDrawList* canvas_draw_list;
ImGuiStorage node_idx_to_submission_idx;
ImVector<int> node_idx_submission_order;
ImVector<int> node_indices_overlapping_with_mouse;
ImVector<int> occluded_pin_indices;
// Canvas extents
ImVec2 canvas_origin_screen_space;
ImRect canvas_rect_screen_space;
// Debug helpers
ScopeFlags current_scope;
// Configuration state
IO io;
Style style;
ImVector<ColorStyleElement> color_modifier_stack;
ImVector<StyleElement> style_modifier_stack;
ImGuiTextBuffer text_buffer;
int current_attribute_flags;
ImVector<int> attribute_flag_stack;
// UI element state
int current_node_idx;
int current_pin_idx;
int current_attribute_id;
OptionalIndex hovered_node_idx;
OptionalIndex interactive_node_idx;
OptionalIndex hovered_link_idx;
OptionalIndex hovered_pin_idx;
int hovered_pin_flags;
OptionalIndex deleted_link_idx;
OptionalIndex snap_link_idx;
// Event helper state
int element_state_change;
int active_attribute_id;
bool active_attribute;
// ImGui::IO cache
ImVec2 mouse_pos;
bool left_mouse_clicked;
bool left_mouse_released;
bool alt_mouse_clicked;
bool left_mouse_dragging;
bool alt_mouse_dragging;
};
Context* g = NULL;
namespace {
EditorContext&
editor_context_get()
{
// No editor context was set! Did you forget to call imnodes::Initialize?
assert(g->editor_ctx != NULL);
return *g->editor_ctx;
}
inline ImVec2
eval_bezier(float t, const BezierCurve& bezier)
inline ImVec2 EvalCubicBezier(
const float t,
const ImVec2& P0,
const ImVec2& P1,
const ImVec2& P2,
const ImVec2& P3)
{
// B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3
return ImVec2((1 - t) * (1 - t) * (1 - t) * bezier.p0.x +
3 * (1 - t) * (1 - t) * t * bezier.p1.x +
3 * (1 - t) * t * t * bezier.p2.x + t * t * t * bezier.p3.x,
(1 - t) * (1 - t) * (1 - t) * bezier.p0.y +
3 * (1 - t) * (1 - t) * t * bezier.p1.y +
3 * (1 - t) * t * t * bezier.p2.y +
t * t * t * bezier.p3.y);
const float u = 1.0f - t;
const float b0 = u * u * u;
const float b1 = 3 * u * u * t;
const float b2 = 3 * u * t * t;
const float b3 = t * t * t;
return ImVec2(
b0 * P0.x + b1 * P1.x + b2 * P2.x + b3 * P3.x,
b0 * P0.y + b1 * P1.y + b2 * P2.y + b3 * P3.y);
}
// Calculates the closest point along each bezier curve segment.
ImVec2
get_closest_point_on_cubic_bezier(const int num_segments,
const ImVec2& p,
const BezierCurve& bezier)
ImVec2 GetClosestPointOnCubicBezier(const int num_segments, const ImVec2& p, const CubicBezier& cb)
{
IM_ASSERT(num_segments > 0);
ImVec2 p_last = bezier.p0;
ImVec2 p_last = cb.P0;
ImVec2 p_closest;
float p_closest_dist = FLT_MAX;
float t_step = 1.0f / (float)num_segments;
for (int i = 1; i <= num_segments; ++i) {
ImVec2 p_current = eval_bezier(t_step * i, bezier);
float p_closest_dist = FLT_MAX;
float t_step = 1.0f / (float)num_segments;
for (int i = 1; i <= num_segments; ++i)
{
ImVec2 p_current = EvalCubicBezier(t_step * i, cb.P0, cb.P1, cb.P2, cb.P3);
ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
float dist = ImLengthSqr(p - p_line);
if (dist < p_closest_dist) {
float dist = ImLengthSqr(p - p_line);
if (dist < p_closest_dist)
{
p_closest = p_line;
p_closest_dist = dist;
}
......@@ -432,91 +84,81 @@ get_closest_point_on_cubic_bezier(const int num_segments,
return p_closest;
}
inline float
get_distance_to_cubic_bezier(const ImVec2& pos,
const BezierCurve& bezier,
const int num_segments)
inline float GetDistanceToCubicBezier(
const ImVec2& pos,
const CubicBezier& cubic_bezier,
const int num_segments)
{
const ImVec2 point_on_curve =
get_closest_point_on_cubic_bezier(num_segments, pos, bezier);
const ImVec2 point_on_curve = GetClosestPointOnCubicBezier(num_segments, pos, cubic_bezier);
const ImVec2 to_curve = point_on_curve - pos;
return ImSqrt(ImLengthSqr(to_curve));
}
inline ImRect
get_containing_rect_for_bezier_curve(const BezierCurve& bezier)
inline ImRect GetContainingRectForCubicBezier(const CubicBezier& cb)
{
const ImVec2 min =
ImVec2(ImMin(bezier.p0.x, bezier.p3.x), ImMin(bezier.p0.y, bezier.p3.y));
const ImVec2 max =
ImVec2(ImMax(bezier.p0.x, bezier.p3.x), ImMax(bezier.p0.y, bezier.p3.y));
const ImVec2 min = ImVec2(ImMin(cb.P0.x, cb.P3.x), ImMin(cb.P0.y, cb.P3.y));
const ImVec2 max = ImVec2(ImMax(cb.P0.x, cb.P3.x), ImMax(cb.P0.y, cb.P3.y));
const float hover_distance = g->style.link_hover_distance;
const float hover_distance = GImNodes->Style.LinkHoverDistance;
ImRect rect(min, max);
rect.Add(bezier.p1);
rect.Add(bezier.p2);
rect.Add(cb.P1);
rect.Add(cb.P2);
rect.Expand(ImVec2(hover_distance, hover_distance));
return rect;
}
inline LinkBezierData
get_link_renderable(ImVec2 start,
ImVec2 end,
const AttributeType start_type,
const float line_segments_per_length)
inline CubicBezier GetCubicBezier(
ImVec2 start,
ImVec2 end,
const ImNodesAttributeType start_type,
const float line_segments_per_length)
{
assert((start_type == AttributeType_Input) ||
(start_type == AttributeType_Output));
if (start_type == AttributeType_Input) {
assert(
(start_type == ImNodesAttributeType_Input) || (start_type == ImNodesAttributeType_Output));
if (start_type == ImNodesAttributeType_Input)
{
ImSwap(start, end);
}
const float link_length = ImSqrt(ImLengthSqr(end - start));
const float link_length = ImSqrt(ImLengthSqr(end - start));
const ImVec2 offset = ImVec2(0.25f * link_length, 0.f);
LinkBezierData link_data;
link_data.bezier.p0 = start;
link_data.bezier.p1 = start + offset;
link_data.bezier.p2 = end - offset;
link_data.bezier.p3 = end;
link_data.num_segments =
ImMax(static_cast<int>(link_length * line_segments_per_length), 1);
return link_data;
CubicBezier cubic_bezier;
cubic_bezier.P0 = start;
cubic_bezier.P1 = start + offset;
cubic_bezier.P2 = end - offset;
cubic_bezier.P3 = end;
cubic_bezier.NumSegments = ImMax(static_cast<int>(link_length * line_segments_per_length), 1);
return cubic_bezier;
}
inline float
eval_implicit_line_eq(const ImVec2& p1, const ImVec2& p2, const ImVec2& p)
inline float EvalImplicitLineEq(const ImVec2& p1, const ImVec2& p2, const ImVec2& p)
{
return (p2.y - p1.y) * p.x + (p1.x - p2.x) * p.y +
(p2.x * p1.y - p1.x * p2.y);
return (p2.y - p1.y) * p.x + (p1.x - p2.x) * p.y + (p2.x * p1.y - p1.x * p2.y);
}
inline int
sign(float val)
{