feat(shaders_lab): visual node editor (imgui-node-editor) + multi-source
- cpp/vendor/imgui-node-editor: vendorized thedmd/imgui-node-editor v0.9.4 - cpp/functions/gfx/dag_node_editor: new visual pipeline editor replacing dag_panel - DagStep: source_ids[4] + editor_pos + editor_uid (multi-input support) - DagNodeDef: num_inputs explicit; circle reclassified as Op - dag_compile: N inputs per node, topological ordering preserved - main: use node editor; destroy on shutdown - patch imgui_extra_math.inl: guard operator*(float, ImVec2) for imgui >= 18955 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
add_example_executable(blueprints-example
|
||||
blueprints-example.cpp
|
||||
utilities/builders.h
|
||||
utilities/drawing.h
|
||||
utilities/widgets.h
|
||||
utilities/builders.cpp
|
||||
utilities/drawing.cpp
|
||||
utilities/widgets.cpp
|
||||
)
|
||||
+1835
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 332 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 168 B |
+310
@@ -0,0 +1,310 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// LICENSE
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
//
|
||||
// CREDITS
|
||||
// Written by Michal Cichon
|
||||
//------------------------------------------------------------------------------
|
||||
# define IMGUI_DEFINE_MATH_OPERATORS
|
||||
# include "builders.h"
|
||||
# include <imgui_internal.h>
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
namespace ed = ax::NodeEditor;
|
||||
namespace util = ax::NodeEditor::Utilities;
|
||||
|
||||
util::BlueprintNodeBuilder::BlueprintNodeBuilder(ImTextureID texture, int textureWidth, int textureHeight):
|
||||
HeaderTextureId(texture),
|
||||
HeaderTextureWidth(textureWidth),
|
||||
HeaderTextureHeight(textureHeight),
|
||||
CurrentNodeId(0),
|
||||
CurrentStage(Stage::Invalid),
|
||||
HasHeader(false)
|
||||
{
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Begin(ed::NodeId id)
|
||||
{
|
||||
HasHeader = false;
|
||||
HeaderMin = HeaderMax = ImVec2();
|
||||
|
||||
ed::PushStyleVar(StyleVar_NodePadding, ImVec4(8, 4, 8, 8));
|
||||
|
||||
ed::BeginNode(id);
|
||||
|
||||
ImGui::PushID(id.AsPointer());
|
||||
CurrentNodeId = id;
|
||||
|
||||
SetStage(Stage::Begin);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::End()
|
||||
{
|
||||
SetStage(Stage::End);
|
||||
|
||||
ed::EndNode();
|
||||
|
||||
if (ImGui::IsItemVisible())
|
||||
{
|
||||
auto alpha = static_cast<int>(255 * ImGui::GetStyle().Alpha);
|
||||
|
||||
auto drawList = ed::GetNodeBackgroundDrawList(CurrentNodeId);
|
||||
|
||||
const auto halfBorderWidth = ed::GetStyle().NodeBorderWidth * 0.5f;
|
||||
|
||||
auto headerColor = IM_COL32(0, 0, 0, alpha) | (HeaderColor & IM_COL32(255, 255, 255, 0));
|
||||
if ((HeaderMax.x > HeaderMin.x) && (HeaderMax.y > HeaderMin.y) && HeaderTextureId)
|
||||
{
|
||||
const auto uv = ImVec2(
|
||||
(HeaderMax.x - HeaderMin.x) / (float)(4.0f * HeaderTextureWidth),
|
||||
(HeaderMax.y - HeaderMin.y) / (float)(4.0f * HeaderTextureHeight));
|
||||
|
||||
drawList->AddImageRounded(HeaderTextureId,
|
||||
HeaderMin - ImVec2(8 - halfBorderWidth, 4 - halfBorderWidth),
|
||||
HeaderMax + ImVec2(8 - halfBorderWidth, 0),
|
||||
ImVec2(0.0f, 0.0f), uv,
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
headerColor, GetStyle().NodeRounding, ImDrawFlags_RoundCornersTop);
|
||||
#else
|
||||
headerColor, GetStyle().NodeRounding, 1 | 2);
|
||||
#endif
|
||||
|
||||
if (ContentMin.y > HeaderMax.y)
|
||||
{
|
||||
drawList->AddLine(
|
||||
ImVec2(HeaderMin.x - (8 - halfBorderWidth), HeaderMax.y - 0.5f),
|
||||
ImVec2(HeaderMax.x + (8 - halfBorderWidth), HeaderMax.y - 0.5f),
|
||||
ImColor(255, 255, 255, 96 * alpha / (3 * 255)), 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentNodeId = 0;
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
ed::PopStyleVar();
|
||||
|
||||
SetStage(Stage::Invalid);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Header(const ImVec4& color)
|
||||
{
|
||||
HeaderColor = ImColor(color);
|
||||
SetStage(Stage::Header);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::EndHeader()
|
||||
{
|
||||
SetStage(Stage::Content);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Input(ed::PinId id)
|
||||
{
|
||||
if (CurrentStage == Stage::Begin)
|
||||
SetStage(Stage::Content);
|
||||
|
||||
const auto applyPadding = (CurrentStage == Stage::Input);
|
||||
|
||||
SetStage(Stage::Input);
|
||||
|
||||
if (applyPadding)
|
||||
ImGui::Spring(0);
|
||||
|
||||
Pin(id, PinKind::Input);
|
||||
|
||||
ImGui::BeginHorizontal(id.AsPointer());
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::EndInput()
|
||||
{
|
||||
ImGui::EndHorizontal();
|
||||
|
||||
EndPin();
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Middle()
|
||||
{
|
||||
if (CurrentStage == Stage::Begin)
|
||||
SetStage(Stage::Content);
|
||||
|
||||
SetStage(Stage::Middle);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Output(ed::PinId id)
|
||||
{
|
||||
if (CurrentStage == Stage::Begin)
|
||||
SetStage(Stage::Content);
|
||||
|
||||
const auto applyPadding = (CurrentStage == Stage::Output);
|
||||
|
||||
SetStage(Stage::Output);
|
||||
|
||||
if (applyPadding)
|
||||
ImGui::Spring(0);
|
||||
|
||||
Pin(id, PinKind::Output);
|
||||
|
||||
ImGui::BeginHorizontal(id.AsPointer());
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::EndOutput()
|
||||
{
|
||||
ImGui::EndHorizontal();
|
||||
|
||||
EndPin();
|
||||
}
|
||||
|
||||
bool util::BlueprintNodeBuilder::SetStage(Stage stage)
|
||||
{
|
||||
if (stage == CurrentStage)
|
||||
return false;
|
||||
|
||||
auto oldStage = CurrentStage;
|
||||
CurrentStage = stage;
|
||||
|
||||
ImVec2 cursor;
|
||||
switch (oldStage)
|
||||
{
|
||||
case Stage::Begin:
|
||||
break;
|
||||
|
||||
case Stage::Header:
|
||||
ImGui::EndHorizontal();
|
||||
HeaderMin = ImGui::GetItemRectMin();
|
||||
HeaderMax = ImGui::GetItemRectMax();
|
||||
|
||||
// spacing between header and content
|
||||
ImGui::Spring(0, ImGui::GetStyle().ItemSpacing.y * 2.0f);
|
||||
|
||||
break;
|
||||
|
||||
case Stage::Content:
|
||||
break;
|
||||
|
||||
case Stage::Input:
|
||||
ed::PopStyleVar(2);
|
||||
|
||||
ImGui::Spring(1, 0);
|
||||
ImGui::EndVertical();
|
||||
|
||||
// #debug
|
||||
// ImGui::GetWindowDrawList()->AddRect(
|
||||
// ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 0, 0, 255));
|
||||
|
||||
break;
|
||||
|
||||
case Stage::Middle:
|
||||
ImGui::EndVertical();
|
||||
|
||||
// #debug
|
||||
// ImGui::GetWindowDrawList()->AddRect(
|
||||
// ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 0, 0, 255));
|
||||
|
||||
break;
|
||||
|
||||
case Stage::Output:
|
||||
ed::PopStyleVar(2);
|
||||
|
||||
ImGui::Spring(1, 0);
|
||||
ImGui::EndVertical();
|
||||
|
||||
// #debug
|
||||
// ImGui::GetWindowDrawList()->AddRect(
|
||||
// ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 0, 0, 255));
|
||||
|
||||
break;
|
||||
|
||||
case Stage::End:
|
||||
break;
|
||||
|
||||
case Stage::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (stage)
|
||||
{
|
||||
case Stage::Begin:
|
||||
ImGui::BeginVertical("node");
|
||||
break;
|
||||
|
||||
case Stage::Header:
|
||||
HasHeader = true;
|
||||
|
||||
ImGui::BeginHorizontal("header");
|
||||
break;
|
||||
|
||||
case Stage::Content:
|
||||
if (oldStage == Stage::Begin)
|
||||
ImGui::Spring(0);
|
||||
|
||||
ImGui::BeginHorizontal("content");
|
||||
ImGui::Spring(0, 0);
|
||||
break;
|
||||
|
||||
case Stage::Input:
|
||||
ImGui::BeginVertical("inputs", ImVec2(0, 0), 0.0f);
|
||||
|
||||
ed::PushStyleVar(ed::StyleVar_PivotAlignment, ImVec2(0, 0.5f));
|
||||
ed::PushStyleVar(ed::StyleVar_PivotSize, ImVec2(0, 0));
|
||||
|
||||
if (!HasHeader)
|
||||
ImGui::Spring(1, 0);
|
||||
break;
|
||||
|
||||
case Stage::Middle:
|
||||
ImGui::Spring(1);
|
||||
ImGui::BeginVertical("middle", ImVec2(0, 0), 1.0f);
|
||||
break;
|
||||
|
||||
case Stage::Output:
|
||||
if (oldStage == Stage::Middle || oldStage == Stage::Input)
|
||||
ImGui::Spring(1);
|
||||
else
|
||||
ImGui::Spring(1, 0);
|
||||
ImGui::BeginVertical("outputs", ImVec2(0, 0), 1.0f);
|
||||
|
||||
ed::PushStyleVar(ed::StyleVar_PivotAlignment, ImVec2(1.0f, 0.5f));
|
||||
ed::PushStyleVar(ed::StyleVar_PivotSize, ImVec2(0, 0));
|
||||
|
||||
if (!HasHeader)
|
||||
ImGui::Spring(1, 0);
|
||||
break;
|
||||
|
||||
case Stage::End:
|
||||
if (oldStage == Stage::Input)
|
||||
ImGui::Spring(1, 0);
|
||||
if (oldStage != Stage::Begin)
|
||||
ImGui::EndHorizontal();
|
||||
ContentMin = ImGui::GetItemRectMin();
|
||||
ContentMax = ImGui::GetItemRectMax();
|
||||
|
||||
//ImGui::Spring(0);
|
||||
ImGui::EndVertical();
|
||||
NodeMin = ImGui::GetItemRectMin();
|
||||
NodeMax = ImGui::GetItemRectMax();
|
||||
break;
|
||||
|
||||
case Stage::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::Pin(ed::PinId id, ed::PinKind kind)
|
||||
{
|
||||
ed::BeginPin(id, kind);
|
||||
}
|
||||
|
||||
void util::BlueprintNodeBuilder::EndPin()
|
||||
{
|
||||
ed::EndPin();
|
||||
|
||||
// #debug
|
||||
// ImGui::GetWindowDrawList()->AddRectFilled(
|
||||
// ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 0, 0, 64));
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// LICENSE
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
//
|
||||
// CREDITS
|
||||
// Written by Michal Cichon
|
||||
//------------------------------------------------------------------------------
|
||||
# pragma once
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
# include <imgui_node_editor.h>
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
namespace ax {
|
||||
namespace NodeEditor {
|
||||
namespace Utilities {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
struct BlueprintNodeBuilder
|
||||
{
|
||||
BlueprintNodeBuilder(ImTextureID texture = nullptr, int textureWidth = 0, int textureHeight = 0);
|
||||
|
||||
void Begin(NodeId id);
|
||||
void End();
|
||||
|
||||
void Header(const ImVec4& color = ImVec4(1, 1, 1, 1));
|
||||
void EndHeader();
|
||||
|
||||
void Input(PinId id);
|
||||
void EndInput();
|
||||
|
||||
void Middle();
|
||||
|
||||
void Output(PinId id);
|
||||
void EndOutput();
|
||||
|
||||
|
||||
private:
|
||||
enum class Stage
|
||||
{
|
||||
Invalid,
|
||||
Begin,
|
||||
Header,
|
||||
Content,
|
||||
Input,
|
||||
Output,
|
||||
Middle,
|
||||
End
|
||||
};
|
||||
|
||||
bool SetStage(Stage stage);
|
||||
|
||||
void Pin(PinId id, ax::NodeEditor::PinKind kind);
|
||||
void EndPin();
|
||||
|
||||
ImTextureID HeaderTextureId;
|
||||
int HeaderTextureWidth;
|
||||
int HeaderTextureHeight;
|
||||
NodeId CurrentNodeId;
|
||||
Stage CurrentStage;
|
||||
ImU32 HeaderColor;
|
||||
ImVec2 NodeMin;
|
||||
ImVec2 NodeMax;
|
||||
ImVec2 HeaderMin;
|
||||
ImVec2 HeaderMax;
|
||||
ImVec2 ContentMin;
|
||||
ImVec2 ContentMax;
|
||||
bool HasHeader;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
} // namespace Utilities
|
||||
} // namespace Editor
|
||||
} // namespace ax
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
# define IMGUI_DEFINE_MATH_OPERATORS
|
||||
# include "drawing.h"
|
||||
# include <imgui_internal.h>
|
||||
|
||||
void ax::Drawing::DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor)
|
||||
{
|
||||
auto rect = ImRect(a, b);
|
||||
auto rect_x = rect.Min.x;
|
||||
auto rect_y = rect.Min.y;
|
||||
auto rect_w = rect.Max.x - rect.Min.x;
|
||||
auto rect_h = rect.Max.y - rect.Min.y;
|
||||
auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
|
||||
auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
|
||||
auto rect_center = ImVec2(rect_center_x, rect_center_y);
|
||||
const auto outline_scale = rect_w / 24.0f;
|
||||
const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
|
||||
|
||||
if (type == IconType::Flow)
|
||||
{
|
||||
const auto origin_scale = rect_w / 24.0f;
|
||||
|
||||
const auto offset_x = 1.0f * origin_scale;
|
||||
const auto offset_y = 0.0f * origin_scale;
|
||||
const auto margin = (filled ? 2.0f : 2.0f) * origin_scale;
|
||||
const auto rounding = 0.1f * origin_scale;
|
||||
const auto tip_round = 0.7f; // percentage of triangle edge (for tip)
|
||||
//const auto edge_round = 0.7f; // percentage of triangle edge (for corner)
|
||||
const auto canvas = ImRect(
|
||||
rect.Min.x + margin + offset_x,
|
||||
rect.Min.y + margin + offset_y,
|
||||
rect.Max.x - margin + offset_x,
|
||||
rect.Max.y - margin + offset_y);
|
||||
const auto canvas_x = canvas.Min.x;
|
||||
const auto canvas_y = canvas.Min.y;
|
||||
const auto canvas_w = canvas.Max.x - canvas.Min.x;
|
||||
const auto canvas_h = canvas.Max.y - canvas.Min.y;
|
||||
|
||||
const auto left = canvas_x + canvas_w * 0.5f * 0.3f;
|
||||
const auto right = canvas_x + canvas_w - canvas_w * 0.5f * 0.3f;
|
||||
const auto top = canvas_y + canvas_h * 0.5f * 0.2f;
|
||||
const auto bottom = canvas_y + canvas_h - canvas_h * 0.5f * 0.2f;
|
||||
const auto center_y = (top + bottom) * 0.5f;
|
||||
//const auto angle = AX_PI * 0.5f * 0.5f * 0.5f;
|
||||
|
||||
const auto tip_top = ImVec2(canvas_x + canvas_w * 0.5f, top);
|
||||
const auto tip_right = ImVec2(right, center_y);
|
||||
const auto tip_bottom = ImVec2(canvas_x + canvas_w * 0.5f, bottom);
|
||||
|
||||
drawList->PathLineTo(ImVec2(left, top) + ImVec2(0, rounding));
|
||||
drawList->PathBezierCubicCurveTo(
|
||||
ImVec2(left, top),
|
||||
ImVec2(left, top),
|
||||
ImVec2(left, top) + ImVec2(rounding, 0));
|
||||
drawList->PathLineTo(tip_top);
|
||||
drawList->PathLineTo(tip_top + (tip_right - tip_top) * tip_round);
|
||||
drawList->PathBezierCubicCurveTo(
|
||||
tip_right,
|
||||
tip_right,
|
||||
tip_bottom + (tip_right - tip_bottom) * tip_round);
|
||||
drawList->PathLineTo(tip_bottom);
|
||||
drawList->PathLineTo(ImVec2(left, bottom) + ImVec2(rounding, 0));
|
||||
drawList->PathBezierCubicCurveTo(
|
||||
ImVec2(left, bottom),
|
||||
ImVec2(left, bottom),
|
||||
ImVec2(left, bottom) - ImVec2(0, rounding));
|
||||
|
||||
if (!filled)
|
||||
{
|
||||
if (innerColor & 0xFF000000)
|
||||
drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor);
|
||||
|
||||
drawList->PathStroke(color, true, 2.0f * outline_scale);
|
||||
}
|
||||
else
|
||||
drawList->PathFillConvex(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto triangleStart = rect_center_x + 0.32f * rect_w;
|
||||
|
||||
auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f);
|
||||
|
||||
rect.Min.x += rect_offset;
|
||||
rect.Max.x += rect_offset;
|
||||
rect_x += rect_offset;
|
||||
rect_center_x += rect_offset * 0.5f;
|
||||
rect_center.x += rect_offset * 0.5f;
|
||||
|
||||
if (type == IconType::Circle)
|
||||
{
|
||||
const auto c = rect_center;
|
||||
|
||||
if (!filled)
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f - 0.5f;
|
||||
|
||||
if (innerColor & 0xFF000000)
|
||||
drawList->AddCircleFilled(c, r, innerColor, 12 + extra_segments);
|
||||
drawList->AddCircle(c, r, color, 12 + extra_segments, 2.0f * outline_scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
drawList->AddCircleFilled(c, 0.5f * rect_w / 2.0f, color, 12 + extra_segments);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == IconType::Square)
|
||||
{
|
||||
if (filled)
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f;
|
||||
const auto p0 = rect_center - ImVec2(r, r);
|
||||
const auto p1 = rect_center + ImVec2(r, r);
|
||||
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRectFilled(p0, p1, color, 0, ImDrawFlags_RoundCornersAll);
|
||||
#else
|
||||
drawList->AddRectFilled(p0, p1, color, 0, 15);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f - 0.5f;
|
||||
const auto p0 = rect_center - ImVec2(r, r);
|
||||
const auto p1 = rect_center + ImVec2(r, r);
|
||||
|
||||
if (innerColor & 0xFF000000)
|
||||
{
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRectFilled(p0, p1, innerColor, 0, ImDrawFlags_RoundCornersAll);
|
||||
#else
|
||||
drawList->AddRectFilled(p0, p1, innerColor, 0, 15);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRect(p0, p1, color, 0, ImDrawFlags_RoundCornersAll, 2.0f * outline_scale);
|
||||
#else
|
||||
drawList->AddRect(p0, p1, color, 0, 15, 2.0f * outline_scale);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (type == IconType::Grid)
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f;
|
||||
const auto w = ceilf(r / 3.0f);
|
||||
|
||||
const auto baseTl = ImVec2(floorf(rect_center_x - w * 2.5f), floorf(rect_center_y - w * 2.5f));
|
||||
const auto baseBr = ImVec2(floorf(baseTl.x + w), floorf(baseTl.y + w));
|
||||
|
||||
auto tl = baseTl;
|
||||
auto br = baseBr;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
tl.x = baseTl.x;
|
||||
br.x = baseBr.x;
|
||||
drawList->AddRectFilled(tl, br, color);
|
||||
tl.x += w * 2;
|
||||
br.x += w * 2;
|
||||
if (i != 1 || filled)
|
||||
drawList->AddRectFilled(tl, br, color);
|
||||
tl.x += w * 2;
|
||||
br.x += w * 2;
|
||||
drawList->AddRectFilled(tl, br, color);
|
||||
|
||||
tl.y += w * 2;
|
||||
br.y += w * 2;
|
||||
}
|
||||
|
||||
triangleStart = br.x + w + 1.0f / 24.0f * rect_w;
|
||||
}
|
||||
|
||||
if (type == IconType::RoundSquare)
|
||||
{
|
||||
if (filled)
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f;
|
||||
const auto cr = r * 0.5f;
|
||||
const auto p0 = rect_center - ImVec2(r, r);
|
||||
const auto p1 = rect_center + ImVec2(r, r);
|
||||
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRectFilled(p0, p1, color, cr, ImDrawFlags_RoundCornersAll);
|
||||
#else
|
||||
drawList->AddRectFilled(p0, p1, color, cr, 15);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto r = 0.5f * rect_w / 2.0f - 0.5f;
|
||||
const auto cr = r * 0.5f;
|
||||
const auto p0 = rect_center - ImVec2(r, r);
|
||||
const auto p1 = rect_center + ImVec2(r, r);
|
||||
|
||||
if (innerColor & 0xFF000000)
|
||||
{
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRectFilled(p0, p1, innerColor, cr, ImDrawFlags_RoundCornersAll);
|
||||
#else
|
||||
drawList->AddRectFilled(p0, p1, innerColor, cr, 15);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IMGUI_VERSION_NUM > 18101
|
||||
drawList->AddRect(p0, p1, color, cr, ImDrawFlags_RoundCornersAll, 2.0f * outline_scale);
|
||||
#else
|
||||
drawList->AddRect(p0, p1, color, cr, 15, 2.0f * outline_scale);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (type == IconType::Diamond)
|
||||
{
|
||||
if (filled)
|
||||
{
|
||||
const auto r = 0.607f * rect_w / 2.0f;
|
||||
const auto c = rect_center;
|
||||
|
||||
drawList->PathLineTo(c + ImVec2( 0, -r));
|
||||
drawList->PathLineTo(c + ImVec2( r, 0));
|
||||
drawList->PathLineTo(c + ImVec2( 0, r));
|
||||
drawList->PathLineTo(c + ImVec2(-r, 0));
|
||||
drawList->PathFillConvex(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto r = 0.607f * rect_w / 2.0f - 0.5f;
|
||||
const auto c = rect_center;
|
||||
|
||||
drawList->PathLineTo(c + ImVec2( 0, -r));
|
||||
drawList->PathLineTo(c + ImVec2( r, 0));
|
||||
drawList->PathLineTo(c + ImVec2( 0, r));
|
||||
drawList->PathLineTo(c + ImVec2(-r, 0));
|
||||
|
||||
if (innerColor & 0xFF000000)
|
||||
drawList->AddConvexPolyFilled(drawList->_Path.Data, drawList->_Path.Size, innerColor);
|
||||
|
||||
drawList->PathStroke(color, true, 2.0f * outline_scale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto triangleTip = triangleStart + rect_w * (0.45f - 0.32f);
|
||||
|
||||
drawList->AddTriangleFilled(
|
||||
ImVec2(ceilf(triangleTip), rect_y + rect_h * 0.5f),
|
||||
ImVec2(triangleStart, rect_center_y + 0.15f * rect_h),
|
||||
ImVec2(triangleStart, rect_center_y - 0.15f * rect_h),
|
||||
color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# pragma once
|
||||
# include <imgui.h>
|
||||
|
||||
namespace ax {
|
||||
namespace Drawing {
|
||||
|
||||
enum class IconType: ImU32 { Flow, Circle, Square, Grid, RoundSquare, Diamond };
|
||||
|
||||
void DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor);
|
||||
|
||||
} // namespace Drawing
|
||||
} // namespace ax
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# define IMGUI_DEFINE_MATH_OPERATORS
|
||||
# include "widgets.h"
|
||||
# include <imgui_internal.h>
|
||||
|
||||
void ax::Widgets::Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color/* = ImVec4(1, 1, 1, 1)*/, const ImVec4& innerColor/* = ImVec4(0, 0, 0, 0)*/)
|
||||
{
|
||||
if (ImGui::IsRectVisible(size))
|
||||
{
|
||||
auto cursorPos = ImGui::GetCursorScreenPos();
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
ax::Drawing::DrawIcon(drawList, cursorPos, cursorPos + size, type, filled, ImColor(color), ImColor(innerColor));
|
||||
}
|
||||
|
||||
ImGui::Dummy(size);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <imgui.h>
|
||||
#include "drawing.h"
|
||||
|
||||
namespace ax {
|
||||
namespace Widgets {
|
||||
|
||||
using Drawing::IconType;
|
||||
|
||||
void Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color = ImVec4(1, 1, 1, 1), const ImVec4& innerColor = ImVec4(0, 0, 0, 0));
|
||||
|
||||
} // namespace Widgets
|
||||
} // namespace ax
|
||||
Reference in New Issue
Block a user