attempt at sequencer

This commit is contained in:
John Alanbrook 2024-09-14 09:36:40 -05:00
parent 79c5e7f3a4
commit 640c8389f3
17 changed files with 6340 additions and 8 deletions

View file

@ -597,7 +597,7 @@ render.sprites = function render_sprites(gridsize = 1)
}
render.circle = function render_circle(pos, radius, color, inner_radius = 1) {
flush();
check_flush(undefined);
if (inner_radius >= 1)
inner_radius = inner_radius/radius;

View file

@ -5,7 +5,10 @@
#include "imgui.h"
#include "implot.h"
#include "imnodes.h"
#Include "imgui_neo_sequencer.h"
#include "imgui_neo_sequencer.h"
#include "ImSequencer.h"
#include "stb_ds.h"
#define SOKOL_IMPL
#include "sokol/util/sokol_imgui.h"
@ -338,8 +341,73 @@ JSC_CCALL(imgui_nodelink,
JSC_CCALL(imgui_nodemini, ImNodes::MiniMap(js2number(argv[0])))
struct MySequence : public ImSequencer::SequenceInterface {
int GetFrameMin() const { return 0; }
int GetFrameMax() const { return 100; }
int GetItemCount() const { return 0; }
virtual void Get(int index, int **start, int **end, int *type, unsigned int *color) {
}
};
JSC_SCALL(imgui_seq,
struct MySequence mseq = {};
int selected = -1;
int first = 0;
int current = 100;
bool expanded = true;
Sequencer(&mseq, &current, &expanded, &selected, &first, ImSequencer::SEQUENCER_EDIT_STARTEND | ImSequencer::SEQUENCER_ADD | ImSequencer::SEQUENCER_DEL | ImSequencer::SEQUENCER_COPYPASTE | ImSequencer::SEQUENCER_CHANGE_FRAME);
)
JSC_SCALL(imgui_sequencer,
ImGui::BeginNeoSequencer(str,
int32_t current = js2number(js_getpropstr(argv[1], "current"));
int32_t start = js2number(js_getpropstr(argv[1], "start"));
int32_t end = js2number(js_getpropstr(argv[1], "end"));
if(ImGui::BeginNeoSequencer(str, &current, &start, &end, {700,200},
ImGuiNeoSequencerFlags_AllowLengthChanging |
ImGuiNeoSequencerFlags_EnableSelection |
ImGuiNeoSequencerFlags_Selection_EnableDragging |
ImGuiNeoSequencerFlags_Selection_EnableDeletion)) {
script_call_sym(argv[2], 0, NULL);
ImGui::EndNeoSequencer();
}
js_setpropstr(argv[1], "current", number2js(current));
js_setpropstr(argv[1], "start", number2js(start));
js_setpropstr(argv[1], "end", number2js(end));
)
JSC_SCALL(imgui_timeline,
float *k = js2newfloatarr(argv[1]);
int n = arrlen(k);
int32_t *keys = (int32_t*)malloc(n*sizeof(*keys));
for (int i = 0; i < n; i++) {
keys[i] = k[i];
}
arrfree(k);
if (ImGui::BeginNeoTimelineEx(str)) {
for (int i = 0; i < n; i++)
ImGui::NeoKeyframe(keys+i);
ImGui::EndNeoTimeLine();
}
JSValue arr = JS_NewArray(js);
for (int i = 0; i < n; i++)
js_setprop_num(arr, i, number2js(keys[i]));
free(keys);
ret = arr;
)
JSC_SCALL(imgui_tlgroup,
if (ImGui::BeginNeoGroup(str)) {
script_call_sym(argv[1], 0, NULL);
ImGui::EndNeoGroup();
}
)
static const JSCFunctionListEntry js_imgui_funcs[] = {
@ -387,7 +455,10 @@ static const JSCFunctionListEntry js_imgui_funcs[] = {
MIST_FUNC_DEF(imgui, nodeout, 2),
MIST_FUNC_DEF(imgui, nodelink, 3),
MIST_FUNC_DEF(imgui, nodemini, 1),
MIST_FUNC_DEF(imgui, sequencer, 4),
MIST_FUNC_DEF(imgui, sequencer, 3),
MIST_FUNC_DEF(imgui, timeline, 3),
MIST_FUNC_DEF(imgui, tlgroup, 2),
MIST_FUNC_DEF(imgui, seq, 3),
};
static int started = 0;

View file

@ -409,6 +409,15 @@ JSValue floatarr2js(int n, float *a) {
return arr;
}
float *js2newfloatarr(JSValue v)
{
float *arr = NULL;
for (int i = 0; i < js_arrlen(v); i++)
arrpush(arr, js2number(js_getpropidx(v,i)));
return arr;
}
HMM_Vec2 js2vec2(JSValue v)
{
HMM_Vec2 v2;

View file

@ -160,6 +160,7 @@ struct texture *js2texture(JSValue v);
void nota_int(char *blob);
void js_setprop_num(JSValue a, uint32_t n, JSValue v);
JSValue js_getpropidx(JSValue v, uint32_t i);
JSValue js_getpropstr(JSValue v, const char *str);
void jsfreestr(const char *str);
@ -168,6 +169,8 @@ void js_setpropstr(JSValue v, const char *str, JSValue p);
void js2floatarr(JSValue v, int n, float *a);
JSValue floatarr2js(int n, float *a);
float *js2newfloatarr(JSValue v);
int js2boolean(JSValue v);
JSValue boolean2js(int b);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,151 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
#include <vector>
#include <stdint.h>
#include <string>
#include "imgui.h"
#include "imgui_internal.h"
namespace GraphEditor {
typedef size_t NodeIndex;
typedef size_t SlotIndex;
typedef size_t LinkIndex;
typedef size_t TemplateIndex;
// Force the view to be respositionned and zoom to fit nodes with Show function.
// Parameter value will be changed to Fit_None by the function.
enum FitOnScreen
{
Fit_None,
Fit_AllNodes,
Fit_SelectedNodes
};
// Display options and colors
struct Options
{
ImRect mMinimap{{0.75f, 0.8f, 0.99f, 0.99f}}; // rectangle coordinates of minimap
ImU32 mBackgroundColor{ IM_COL32(40, 40, 40, 255) }; // full background color
ImU32 mGridColor{ IM_COL32(0, 0, 0, 60) }; // grid lines color
ImU32 mGridColor2{ IM_COL32(0, 0, 0, 160) }; // grid lines color every 10th
ImU32 mSelectedNodeBorderColor{ IM_COL32(255, 130, 30, 255) }; // node border color when it's selected
ImU32 mNodeBorderColor{ IM_COL32(100, 100, 100, 0) }; // node border color when it's not selected
ImU32 mQuadSelection{ IM_COL32(255, 32, 32, 64) }; // quad selection inside color
ImU32 mQuadSelectionBorder{ IM_COL32(255, 32, 32, 255) }; // quad selection border color
ImU32 mDefaultSlotColor{ IM_COL32(128, 128, 128, 255) }; // when no color is provided in node template, use this value
ImU32 mFrameFocus{ IM_COL32(64, 128, 255, 255) }; // rectangle border when graph editor has focus
float mLineThickness{ 5 }; // links width in pixels when zoom value is 1
float mGridSize{ 64.f }; // background grid size in pixels when zoom value is 1
float mRounding{ 3.f }; // rounding at node corners
float mZoomRatio{ 0.1f }; // factor per mouse wheel delta
float mZoomLerpFactor{ 0.25f }; // the smaller, the smoother
float mBorderSelectionThickness{ 6.f }; // thickness of selection border around nodes
float mBorderThickness{ 6.f }; // thickness of selection border around nodes
float mNodeSlotRadius{ 8.f }; // circle radius for inputs and outputs
float mNodeSlotHoverFactor{ 1.2f }; // increase size when hovering
float mMinZoom{ 0.2f }, mMaxZoom { 1.1f };
float mSnap{ 5.f };
bool mDisplayLinksAsCurves{ true }; // false is straight and 45deg lines
bool mAllowQuadSelection{ true }; // multiple selection using drag and drop
bool mRenderGrid{ true }; // grid or nothing
bool mDrawIONameOnHover{ true }; // only draw node input/output when hovering
};
// View state: scroll position and zoom factor
struct ViewState
{
ImVec2 mPosition{0.0f, 0.0f}; // scroll position
float mFactor{ 1.0f }; // current zoom factor
float mFactorTarget{ 1.0f }; // targeted zoom factor interpolated using Options.mZoomLerpFactor
};
struct Template
{
ImU32 mHeaderColor;
ImU32 mBackgroundColor;
ImU32 mBackgroundColorOver;
ImU8 mInputCount;
const char** mInputNames; // can be nullptr. No text displayed.
ImU32* mInputColors; // can be nullptr, default slot color will be used.
ImU8 mOutputCount;
const char** mOutputNames; // can be nullptr. No text displayed.
ImU32* mOutputColors; // can be nullptr, default slot color will be used.
};
struct Node
{
const char* mName;
TemplateIndex mTemplateIndex;
ImRect mRect;
bool mSelected{ false };
};
struct Link
{
NodeIndex mInputNodeIndex;
SlotIndex mInputSlotIndex;
NodeIndex mOutputNodeIndex;
SlotIndex mOutputSlotIndex;
};
struct Delegate
{
virtual bool AllowedLink(NodeIndex from, NodeIndex to) = 0;
virtual void SelectNode(NodeIndex nodeIndex, bool selected) = 0;
virtual void MoveSelectedNodes(const ImVec2 delta) = 0;
virtual void AddLink(NodeIndex inputNodeIndex, SlotIndex inputSlotIndex, NodeIndex outputNodeIndex, SlotIndex outputSlotIndex) = 0;
virtual void DelLink(LinkIndex linkIndex) = 0;
// user is responsible for clipping
virtual void CustomDraw(ImDrawList* drawList, ImRect rectangle, NodeIndex nodeIndex) = 0;
// use mouse position to open context menu
// if nodeIndex != -1, right click happens on the specified node
virtual void RightClick(NodeIndex nodeIndex, SlotIndex slotIndexInput, SlotIndex slotIndexOutput) = 0;
virtual const size_t GetTemplateCount() = 0;
virtual const Template GetTemplate(TemplateIndex index) = 0;
virtual const size_t GetNodeCount() = 0;
virtual const Node GetNode(NodeIndex index) = 0;
virtual const size_t GetLinkCount() = 0;
virtual const Link GetLink(LinkIndex index) = 0;
virtual ~Delegate() = default;
};
void Show(Delegate& delegate, const Options& options, ViewState& viewState, bool enabled, FitOnScreen* fit = nullptr);
void GraphEditorClear();
bool EditOptions(Options& options);
} // namespace

View file

@ -0,0 +1,457 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include "ImCurveEdit.h"
#include "imgui.h"
#include "imgui_internal.h"
#include <stdint.h>
#include <set>
#include <vector>
#if defined(_MSC_VER) || defined(__MINGW32__)
#include <malloc.h>
#endif
#if !defined(_MSC_VER) && !defined(__MINGW64_VERSION_MAJOR)
#define _malloca(x) alloca(x)
#define _freea(x)
#endif
namespace ImCurveEdit
{
#ifndef IMGUI_DEFINE_MATH_OPERATORS
static ImVec2 operator+(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x + b.x, a.y + b.y);
}
static ImVec2 operator-(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x - b.x, a.y - b.y);
}
static ImVec2 operator*(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x * b.x, a.y * b.y);
}
static ImVec2 operator/(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x / b.x, a.y / b.y);
}
static ImVec2 operator*(const ImVec2& a, const float b) {
return ImVec2(a.x * b, a.y * b);
}
#endif
static float smoothstep(float edge0, float edge1, float x)
{
x = ImClamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
return x * x * (3 - 2 * x);
}
static float distance(float x, float y, float x1, float y1, float x2, float y2)
{
float A = x - x1;
float B = y - y1;
float C = x2 - x1;
float D = y2 - y1;
float dot = A * C + B * D;
float len_sq = C * C + D * D;
float param = -1.f;
if (len_sq > FLT_EPSILON)
param = dot / len_sq;
float xx, yy;
if (param < 0.f) {
xx = x1;
yy = y1;
}
else if (param > 1.f) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
float dx = x - xx;
float dy = y - yy;
return sqrtf(dx * dx + dy * dy);
}
static int DrawPoint(ImDrawList* draw_list, ImVec2 pos, const ImVec2 size, const ImVec2 offset, bool edited)
{
int ret = 0;
ImGuiIO& io = ImGui::GetIO();
static const ImVec2 localOffsets[4] = { ImVec2(1,0), ImVec2(0,1), ImVec2(-1,0), ImVec2(0,-1) };
ImVec2 offsets[4];
for (int i = 0; i < 4; i++)
{
offsets[i] = pos * size + localOffsets[i] * 4.5f + offset;
}
const ImVec2 center = pos * size + offset;
const ImRect anchor(center - ImVec2(5, 5), center + ImVec2(5, 5));
draw_list->AddConvexPolyFilled(offsets, 4, 0xFF000000);
if (anchor.Contains(io.MousePos))
{
ret = 1;
if (io.MouseDown[0])
ret = 2;
}
if (edited)
draw_list->AddPolyline(offsets, 4, 0xFFFFFFFF, true, 3.0f);
else if (ret)
draw_list->AddPolyline(offsets, 4, 0xFF80B0FF, true, 2.0f);
else
draw_list->AddPolyline(offsets, 4, 0xFF0080FF, true, 2.0f);
return ret;
}
int Edit(Delegate& delegate, const ImVec2& size, unsigned int id, const ImRect* clippingRect, ImVector<EditPoint>* selectedPoints)
{
static bool selectingQuad = false;
static ImVec2 quadSelection;
static int overCurve = -1;
static int movingCurve = -1;
static bool scrollingV = false;
static std::set<EditPoint> selection;
static bool overSelectedPoint = false;
int ret = 0;
ImGuiIO& io = ImGui::GetIO();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushStyleColor(ImGuiCol_Border, 0);
ImGui::BeginChildFrame(id, size);
delegate.focused = ImGui::IsWindowFocused();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (clippingRect)
draw_list->PushClipRect(clippingRect->Min, clippingRect->Max, true);
const ImVec2 offset = ImGui::GetCursorScreenPos() + ImVec2(0.f, size.y);
const ImVec2 ssize(size.x, -size.y);
const ImRect container(offset + ImVec2(0.f, ssize.y), offset + ImVec2(ssize.x, 0.f));
ImVec2& min = delegate.GetMin();
ImVec2& max = delegate.GetMax();
// handle zoom and VScroll
if (container.Contains(io.MousePos))
{
if (fabsf(io.MouseWheel) > FLT_EPSILON)
{
const float r = (io.MousePos.y - offset.y) / ssize.y;
float ratioY = ImLerp(min.y, max.y, r);
auto scaleValue = [&](float v) {
v -= ratioY;
v *= (1.f - io.MouseWheel * 0.05f);
v += ratioY;
return v;
};
min.y = scaleValue(min.y);
max.y = scaleValue(max.y);
}
if (!scrollingV && ImGui::IsMouseDown(2))
{
scrollingV = true;
}
}
ImVec2 range = max - min + ImVec2(1.f, 0.f); // +1 because of inclusive last frame
const ImVec2 viewSize(size.x, -size.y);
const ImVec2 sizeOfPixel = ImVec2(1.f, 1.f) / viewSize;
const size_t curveCount = delegate.GetCurveCount();
if (scrollingV)
{
float deltaH = io.MouseDelta.y * range.y * sizeOfPixel.y;
min.y -= deltaH;
max.y -= deltaH;
if (!ImGui::IsMouseDown(2))
scrollingV = false;
}
draw_list->AddRectFilled(offset, offset + ssize, delegate.GetBackgroundColor());
auto pointToRange = [&](ImVec2 pt) { return (pt - min) / range; };
auto rangeToPoint = [&](ImVec2 pt) { return (pt * range) + min; };
draw_list->AddLine(ImVec2(-1.f, -min.y / range.y) * viewSize + offset, ImVec2(1.f, -min.y / range.y) * viewSize + offset, 0xFF000000, 1.5f);
bool overCurveOrPoint = false;
int localOverCurve = -1;
// make sure highlighted curve is rendered last
int* curvesIndex = (int*)_malloca(sizeof(int) * curveCount);
for (size_t c = 0; c < curveCount; c++)
curvesIndex[c] = int(c);
int highLightedCurveIndex = -1;
if (overCurve != -1 && curveCount)
{
ImSwap(curvesIndex[overCurve], curvesIndex[curveCount - 1]);
highLightedCurveIndex = overCurve;
}
for (size_t cur = 0; cur < curveCount; cur++)
{
int c = curvesIndex[cur];
if (!delegate.IsVisible(c))
continue;
const size_t ptCount = delegate.GetPointCount(c);
if (ptCount < 1)
continue;
CurveType curveType = delegate.GetCurveType(c);
if (curveType == CurveNone)
continue;
const ImVec2* pts = delegate.GetPoints(c);
uint32_t curveColor = delegate.GetCurveColor(c);
if ((c == highLightedCurveIndex && selection.empty() && !selectingQuad) || movingCurve == c)
curveColor = 0xFFFFFFFF;
for (size_t p = 0; p < ptCount - 1; p++)
{
const ImVec2 p1 = pointToRange(pts[p]);
const ImVec2 p2 = pointToRange(pts[p + 1]);
if (curveType == CurveSmooth || curveType == CurveLinear)
{
size_t subStepCount = (curveType == CurveSmooth) ? 20 : 2;
float step = 1.f / float(subStepCount - 1);
for (size_t substep = 0; substep < subStepCount - 1; substep++)
{
float t = float(substep) * step;
const ImVec2 sp1 = ImLerp(p1, p2, t);
const ImVec2 sp2 = ImLerp(p1, p2, t + step);
const float rt1 = smoothstep(p1.x, p2.x, sp1.x);
const float rt2 = smoothstep(p1.x, p2.x, sp2.x);
const ImVec2 pos1 = ImVec2(sp1.x, ImLerp(p1.y, p2.y, rt1)) * viewSize + offset;
const ImVec2 pos2 = ImVec2(sp2.x, ImLerp(p1.y, p2.y, rt2)) * viewSize + offset;
if (distance(io.MousePos.x, io.MousePos.y, pos1.x, pos1.y, pos2.x, pos2.y) < 8.f && !scrollingV)
{
localOverCurve = int(c);
overCurve = int(c);
overCurveOrPoint = true;
}
draw_list->AddLine(pos1, pos2, curveColor, 1.3f);
} // substep
}
else if (curveType == CurveDiscrete)
{
ImVec2 dp1 = p1 * viewSize + offset;
ImVec2 dp2 = ImVec2(p2.x, p1.y) * viewSize + offset;
ImVec2 dp3 = p2 * viewSize + offset;
draw_list->AddLine(dp1, dp2, curveColor, 1.3f);
draw_list->AddLine(dp2, dp3, curveColor, 1.3f);
if ((distance(io.MousePos.x, io.MousePos.y, dp1.x, dp1.y, dp3.x, dp1.y) < 8.f ||
distance(io.MousePos.x, io.MousePos.y, dp3.x, dp1.y, dp3.x, dp3.y) < 8.f)
/*&& localOverCurve == -1*/)
{
localOverCurve = int(c);
overCurve = int(c);
overCurveOrPoint = true;
}
}
} // point loop
for (size_t p = 0; p < ptCount; p++)
{
const int drawState = DrawPoint(draw_list, pointToRange(pts[p]), viewSize, offset, (selection.find({ int(c), int(p) }) != selection.end() && movingCurve == -1 && !scrollingV));
if (drawState && movingCurve == -1 && !selectingQuad)
{
overCurveOrPoint = true;
overSelectedPoint = true;
overCurve = -1;
if (drawState == 2)
{
if (!io.KeyShift && selection.find({ int(c), int(p) }) == selection.end())
selection.clear();
selection.insert({ int(c), int(p) });
}
}
}
} // curves loop
if (localOverCurve == -1)
overCurve = -1;
// move selection
static bool pointsMoved = false;
static ImVec2 mousePosOrigin;
static std::vector<ImVec2> originalPoints;
if (overSelectedPoint && io.MouseDown[0])
{
if ((fabsf(io.MouseDelta.x) > 0.f || fabsf(io.MouseDelta.y) > 0.f) && !selection.empty())
{
if (!pointsMoved)
{
delegate.BeginEdit(0);
mousePosOrigin = io.MousePos;
originalPoints.resize(selection.size());
int index = 0;
for (auto& sel : selection)
{
const ImVec2* pts = delegate.GetPoints(sel.curveIndex);
originalPoints[index++] = pts[sel.pointIndex];
}
}
pointsMoved = true;
ret = 1;
auto prevSelection = selection;
int originalIndex = 0;
for (auto& sel : prevSelection)
{
const ImVec2 p = rangeToPoint(pointToRange(originalPoints[originalIndex]) + (io.MousePos - mousePosOrigin) * sizeOfPixel);
const int newIndex = delegate.EditPoint(sel.curveIndex, sel.pointIndex, p);
if (newIndex != sel.pointIndex)
{
selection.erase(sel);
selection.insert({ sel.curveIndex, newIndex });
}
originalIndex++;
}
}
}
if (overSelectedPoint && !io.MouseDown[0])
{
overSelectedPoint = false;
if (pointsMoved)
{
pointsMoved = false;
delegate.EndEdit();
}
}
// add point
if (overCurve != -1 && io.MouseDoubleClicked[0])
{
const ImVec2 np = rangeToPoint((io.MousePos - offset) / viewSize);
delegate.BeginEdit(overCurve);
delegate.AddPoint(overCurve, np);
delegate.EndEdit();
ret = 1;
}
// move curve
if (movingCurve != -1)
{
const size_t ptCount = delegate.GetPointCount(movingCurve);
const ImVec2* pts = delegate.GetPoints(movingCurve);
if (!pointsMoved)
{
mousePosOrigin = io.MousePos;
pointsMoved = true;
originalPoints.resize(ptCount);
for (size_t index = 0; index < ptCount; index++)
{
originalPoints[index] = pts[index];
}
}
if (ptCount >= 1)
{
for (size_t p = 0; p < ptCount; p++)
{
delegate.EditPoint(movingCurve, int(p), rangeToPoint(pointToRange(originalPoints[p]) + (io.MousePos - mousePosOrigin) * sizeOfPixel));
}
ret = 1;
}
if (!io.MouseDown[0])
{
movingCurve = -1;
pointsMoved = false;
delegate.EndEdit();
}
}
if (movingCurve == -1 && overCurve != -1 && ImGui::IsMouseClicked(0) && selection.empty() && !selectingQuad)
{
movingCurve = overCurve;
delegate.BeginEdit(overCurve);
}
// quad selection
if (selectingQuad)
{
const ImVec2 bmin = ImMin(quadSelection, io.MousePos);
const ImVec2 bmax = ImMax(quadSelection, io.MousePos);
draw_list->AddRectFilled(bmin, bmax, 0x40FF0000, 1.f);
draw_list->AddRect(bmin, bmax, 0xFFFF0000, 1.f);
const ImRect selectionQuad(bmin, bmax);
if (!io.MouseDown[0])
{
if (!io.KeyShift)
selection.clear();
// select everythnig is quad
for (size_t c = 0; c < curveCount; c++)
{
if (!delegate.IsVisible(c))
continue;
const size_t ptCount = delegate.GetPointCount(c);
if (ptCount < 1)
continue;
const ImVec2* pts = delegate.GetPoints(c);
for (size_t p = 0; p < ptCount; p++)
{
const ImVec2 center = pointToRange(pts[p]) * viewSize + offset;
if (selectionQuad.Contains(center))
selection.insert({ int(c), int(p) });
}
}
// done
selectingQuad = false;
}
}
if (!overCurveOrPoint && ImGui::IsMouseClicked(0) && !selectingQuad && movingCurve == -1 && !overSelectedPoint && container.Contains(io.MousePos))
{
selectingQuad = true;
quadSelection = io.MousePos;
}
if (clippingRect)
draw_list->PopClipRect();
ImGui::EndChildFrame();
ImGui::PopStyleVar();
ImGui::PopStyleColor(1);
if (selectedPoints)
{
selectedPoints->resize(int(selection.size()));
int index = 0;
for (auto& point : selection)
(*selectedPoints)[index++] = point;
}
return ret;
}
}

View file

@ -0,0 +1,82 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
#include <stdint.h>
#include "imgui.h"
struct ImRect;
namespace ImCurveEdit
{
enum CurveType
{
CurveNone,
CurveDiscrete,
CurveLinear,
CurveSmooth,
CurveBezier,
};
struct EditPoint
{
int curveIndex;
int pointIndex;
bool operator <(const EditPoint& other) const
{
if (curveIndex < other.curveIndex)
return true;
if (curveIndex > other.curveIndex)
return false;
if (pointIndex < other.pointIndex)
return true;
return false;
}
};
struct Delegate
{
bool focused = false;
virtual size_t GetCurveCount() = 0;
virtual bool IsVisible(size_t /*curveIndex*/) { return true; }
virtual CurveType GetCurveType(size_t /*curveIndex*/) const { return CurveLinear; }
virtual ImVec2& GetMin() = 0;
virtual ImVec2& GetMax() = 0;
virtual size_t GetPointCount(size_t curveIndex) = 0;
virtual uint32_t GetCurveColor(size_t curveIndex) = 0;
virtual ImVec2* GetPoints(size_t curveIndex) = 0;
virtual int EditPoint(size_t curveIndex, int pointIndex, ImVec2 value) = 0;
virtual void AddPoint(size_t curveIndex, ImVec2 value) = 0;
virtual unsigned int GetBackgroundColor() { return 0xFF202020; }
// handle undo/redo thru this functions
virtual void BeginEdit(int /*index*/) {}
virtual void EndEdit() {}
virtual ~Delegate() = default;
};
int Edit(Delegate& delegate, const ImVec2& size, unsigned int id, const ImRect* clippingRect = NULL, ImVector<EditPoint>* selectedPoints = NULL);
}

View file

@ -0,0 +1,116 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include "ImGradient.h"
#include "imgui.h"
#include "imgui_internal.h"
namespace ImGradient
{
#ifndef IMGUI_DEFINE_MATH_OPERATORS
static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); }
static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); }
static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); }
static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); }
static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); }
#endif
static int DrawPoint(ImDrawList* draw_list, ImVec4 color, const ImVec2 size, bool editing, ImVec2 pos)
{
ImGuiIO& io = ImGui::GetIO();
ImVec2 p1 = ImLerp(pos, ImVec2(pos + ImVec2(size.x - size.y, 0.f)), color.w) + ImVec2(3, 3);
ImVec2 p2 = ImLerp(pos + ImVec2(size.y, size.y), ImVec2(pos + size), color.w) - ImVec2(3, 3);
ImRect rc(p1, p2);
color.w = 1.f;
draw_list->AddRectFilled(p1, p2, ImColor(color));
if (editing)
draw_list->AddRect(p1, p2, 0xFFFFFFFF, 2.f, 15, 2.5f);
else
draw_list->AddRect(p1, p2, 0x80FFFFFF, 2.f, 15, 1.25f);
if (rc.Contains(io.MousePos))
{
if (io.MouseClicked[0])
return 2;
return 1;
}
return 0;
}
bool Edit(Delegate& delegate, const ImVec2& size, int& selection)
{
bool ret = false;
ImGuiIO& io = ImGui::GetIO();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::BeginChildFrame(137, size);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
const ImVec2 offset = ImGui::GetCursorScreenPos();
const ImVec4* pts = delegate.GetPoints();
static int currentSelection = -1;
static int movingPt = -1;
if (currentSelection >= int(delegate.GetPointCount()))
currentSelection = -1;
if (movingPt != -1)
{
ImVec4 current = pts[movingPt];
current.w += io.MouseDelta.x / size.x;
current.w = ImClamp(current.w, 0.f, 1.f);
delegate.EditPoint(movingPt, current);
ret = true;
if (!io.MouseDown[0])
movingPt = -1;
}
for (size_t i = 0; i < delegate.GetPointCount(); i++)
{
int ptSel = DrawPoint(draw_list, pts[i], size, i == currentSelection, offset);
if (ptSel == 2)
{
currentSelection = int(i);
ret = true;
}
if (ptSel == 1 && io.MouseDown[0] && movingPt == -1)
{
movingPt = int(i);
}
}
ImRect rc(offset, offset + size);
if (rc.Contains(io.MousePos) && io.MouseDoubleClicked[0])
{
float t = (io.MousePos.x - offset.x) / size.x;
delegate.AddPoint(delegate.GetPoint(t));
ret = true;
}
ImGui::EndChildFrame();
ImGui::PopStyleVar();
selection = currentSelection;
return ret;
}
}

View file

@ -0,0 +1,45 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
#include <cstddef>
struct ImVec4;
struct ImVec2;
namespace ImGradient
{
struct Delegate
{
virtual size_t GetPointCount() = 0;
virtual ImVec4* GetPoints() = 0;
virtual int EditPoint(int pointIndex, ImVec4 value) = 0;
virtual ImVec4 GetPoint(float t) = 0;
virtual void AddPoint(ImVec4 value) = 0;
virtual ~Delegate() = default;
};
bool Edit(Delegate& delegate, const ImVec2& size, int& selection);
}

2996
source/engine/thirdparty/imgui/ImGuizmo.cpp vendored Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,272 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// -------------------------------------------------------------------------------------------
// History :
// 2019/11/03 View gizmo
// 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode.
// 2016/09/09 Hatched negative axis. Snapping. Documentation update.
// 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved
// 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results.
// 2016/08/31 First version
//
// -------------------------------------------------------------------------------------------
// Future (no order):
//
// - Multi view
// - display rotation/translation/scale infos in local/world space and not only local
// - finish local/world matrix application
// - OPERATION as bitmask
//
// -------------------------------------------------------------------------------------------
// Example
#if 0
void EditTransform(const Camera& camera, matrix_t& matrix)
{
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
if (ImGui::IsKeyPressed(90))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
if (ImGui::IsKeyPressed(69))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
if (ImGui::IsKeyPressed(82)) // r Key
mCurrentGizmoOperation = ImGuizmo::SCALE;
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
ImGui::SameLine();
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
mCurrentGizmoOperation = ImGuizmo::ROTATE;
ImGui::SameLine();
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
mCurrentGizmoOperation = ImGuizmo::SCALE;
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
ImGui::InputFloat3("Tr", matrixTranslation, 3);
ImGui::InputFloat3("Rt", matrixRotation, 3);
ImGui::InputFloat3("Sc", matrixScale, 3);
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
{
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
mCurrentGizmoMode = ImGuizmo::LOCAL;
ImGui::SameLine();
if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
mCurrentGizmoMode = ImGuizmo::WORLD;
}
static bool useSnap(false);
if (ImGui::IsKeyPressed(83))
useSnap = !useSnap;
ImGui::Checkbox("", &useSnap);
ImGui::SameLine();
vec_t snap;
switch (mCurrentGizmoOperation)
{
case ImGuizmo::TRANSLATE:
snap = config.mSnapTranslation;
ImGui::InputFloat3("Snap", &snap.x);
break;
case ImGuizmo::ROTATE:
snap = config.mSnapRotation;
ImGui::InputFloat("Angle Snap", &snap.x);
break;
case ImGuizmo::SCALE:
snap = config.mSnapScale;
ImGui::InputFloat("Scale Snap", &snap.x);
break;
}
ImGuiIO& io = ImGui::GetIO();
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
}
#endif
#pragma once
#ifdef USE_IMGUI_API
#include "imconfig.h"
#endif
#ifndef IMGUI_API
#define IMGUI_API
#endif
#ifndef IMGUIZMO_NAMESPACE
#define IMGUIZMO_NAMESPACE ImGuizmo
#endif
namespace IMGUIZMO_NAMESPACE
{
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
// Or pass a specific ImDrawList to draw to (e.g. ImGui::GetForegroundDrawList()).
IMGUI_API void SetDrawlist(ImDrawList* drawlist = nullptr);
// call BeginFrame right after ImGui_XXXX_NewFrame();
IMGUI_API void BeginFrame();
// this is necessary because when imguizmo is compiled into a dll, and imgui into another
// globals are not shared between them.
// More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam
// expose method to set imgui context
IMGUI_API void SetImGuiContext(ImGuiContext* ctx);
// return true if mouse cursor is over any gizmo control (axis, plan or screen component)
IMGUI_API bool IsOver();
// return true if mouse IsOver or if the gizmo is in moving state
IMGUI_API bool IsUsing();
// return true if any gizmo is in moving state
IMGUI_API bool IsUsingAny();
// enable/disable the gizmo. Stay in the state until next call to Enable.
// gizmo is rendered with gray half transparent color when disabled
IMGUI_API void Enable(bool enable);
// helper functions for manualy editing translation/rotation/scale with an input float
// translation, rotation and scale float points to 3 floats each
// Angles are in degrees (more suitable for human editing)
// example:
// float matrixTranslation[3], matrixRotation[3], matrixScale[3];
// ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
// ImGui::InputFloat3("Tr", matrixTranslation, 3);
// ImGui::InputFloat3("Rt", matrixRotation, 3);
// ImGui::InputFloat3("Sc", matrixScale, 3);
// ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
//
// These functions have some numerical stability issues for now. Use with caution.
IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale);
IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix);
IMGUI_API void SetRect(float x, float y, float width, float height);
// default is false
IMGUI_API void SetOrthographic(bool isOrthographic);
// Render a cube with face color corresponding to face normal. Usefull for debug/tests
IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount);
IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize);
// call it when you want a gizmo
// Needs view and projection matrices.
// matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional
// translation is applied in world space
enum OPERATION
{
TRANSLATE_X = (1u << 0),
TRANSLATE_Y = (1u << 1),
TRANSLATE_Z = (1u << 2),
ROTATE_X = (1u << 3),
ROTATE_Y = (1u << 4),
ROTATE_Z = (1u << 5),
ROTATE_SCREEN = (1u << 6),
SCALE_X = (1u << 7),
SCALE_Y = (1u << 8),
SCALE_Z = (1u << 9),
BOUNDS = (1u << 10),
SCALE_XU = (1u << 11),
SCALE_YU = (1u << 12),
SCALE_ZU = (1u << 13),
TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z,
ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN,
SCALE = SCALE_X | SCALE_Y | SCALE_Z,
SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal
UNIVERSAL = TRANSLATE | ROTATE | SCALEU
};
inline OPERATION operator|(OPERATION lhs, OPERATION rhs)
{
return static_cast<OPERATION>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
enum MODE
{
LOCAL,
WORLD
};
IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL);
//
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
// other software are using the same mechanics. But just in case, you are now warned!
//
IMGUI_API void ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
// use this version if you did not call Manipulate before and you are just using ViewManipulate
IMGUI_API void ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
IMGUI_API void SetID(int id);
// return true if the cursor is over the operation's gizmo
IMGUI_API bool IsOver(OPERATION op);
IMGUI_API void SetGizmoSizeClipSpace(float value);
// Allow axis to flip
// When true (default), the guizmo axis flip for better visibility
// When false, they always stay along the positive world/local axis
IMGUI_API void AllowAxisFlip(bool value);
// Configure the limit where axis are hidden
IMGUI_API void SetAxisLimit(float value);
// Configure the limit where planes are hiden
IMGUI_API void SetPlaneLimit(float value);
enum COLOR
{
DIRECTION_X, // directionColor[0]
DIRECTION_Y, // directionColor[1]
DIRECTION_Z, // directionColor[2]
PLANE_X, // planeColor[0]
PLANE_Y, // planeColor[1]
PLANE_Z, // planeColor[2]
SELECTION, // selectionColor
INACTIVE, // inactiveColor
TRANSLATION_LINE, // translationLineColor
SCALE_LINE,
ROTATION_USING_BORDER,
ROTATION_USING_FILL,
HATCHED_AXIS_LINES,
TEXT,
TEXT_SHADOW,
COUNT
};
struct Style
{
IMGUI_API Style();
float TranslationLineThickness; // Thickness of lines for translation gizmo
float TranslationLineArrowSize; // Size of arrow at the end of lines for translation gizmo
float RotationLineThickness; // Thickness of lines for rotation gizmo
float RotationOuterLineThickness; // Thickness of line surrounding the rotation gizmo
float ScaleLineThickness; // Thickness of lines for scale gizmo
float ScaleLineCircleSize; // Size of circle at the end of lines for scale gizmo
float HatchedAxisLineThickness; // Thickness of hatched axis lines
float CenterCircleSize; // Size of circle at the center of the translate/scale gizmo
ImVec4 Colors[COLOR::COUNT];
};
IMGUI_API Style& GetStyle();
}

View file

@ -0,0 +1,695 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include "ImSequencer.h"
#include "imgui.h"
#include "imgui_internal.h"
#include <cstdlib>
namespace ImSequencer
{
#ifndef IMGUI_DEFINE_MATH_OPERATORS
static ImVec2 operator+(const ImVec2& a, const ImVec2& b) {
return ImVec2(a.x + b.x, a.y + b.y);
}
#endif
static bool SequencerAddDelButton(ImDrawList* draw_list, ImVec2 pos, bool add = true)
{
ImGuiIO& io = ImGui::GetIO();
ImRect btnRect(pos, ImVec2(pos.x + 16, pos.y + 16));
bool overBtn = btnRect.Contains(io.MousePos);
bool containedClick = overBtn && btnRect.Contains(io.MouseClickedPos[0]);
bool clickedBtn = containedClick && io.MouseReleased[0];
int btnColor = overBtn ? 0xAAEAFFAA : 0x77A3B2AA;
if (containedClick && io.MouseDownDuration[0] > 0)
btnRect.Expand(2.0f);
float midy = pos.y + 16 / 2 - 0.5f;
float midx = pos.x + 16 / 2 - 0.5f;
draw_list->AddRect(btnRect.Min, btnRect.Max, btnColor, 4);
draw_list->AddLine(ImVec2(btnRect.Min.x + 3, midy), ImVec2(btnRect.Max.x - 3, midy), btnColor, 2);
if (add)
draw_list->AddLine(ImVec2(midx, btnRect.Min.y + 3), ImVec2(midx, btnRect.Max.y - 3), btnColor, 2);
return clickedBtn;
}
bool Sequencer(SequenceInterface* sequence, int* currentFrame, bool* expanded, int* selectedEntry, int* firstFrame, int sequenceOptions)
{
bool ret = false;
ImGuiIO& io = ImGui::GetIO();
int cx = (int)(io.MousePos.x);
int cy = (int)(io.MousePos.y);
static float framePixelWidth = 10.f;
static float framePixelWidthTarget = 10.f;
int legendWidth = 200;
static int movingEntry = -1;
static int movingPos = -1;
static int movingPart = -1;
int delEntry = -1;
int dupEntry = -1;
int ItemHeight = 20;
bool popupOpened = false;
int sequenceCount = sequence->GetItemCount();
if (!sequenceCount)
return false;
ImGui::BeginGroup();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
int firstFrameUsed = firstFrame ? *firstFrame : 0;
int controlHeight = sequenceCount * ItemHeight;
for (int i = 0; i < sequenceCount; i++)
controlHeight += int(sequence->GetCustomHeight(i));
int frameCount = ImMax(sequence->GetFrameMax() - sequence->GetFrameMin(), 1);
static bool MovingScrollBar = false;
static bool MovingCurrentFrame = false;
struct CustomDraw
{
int index;
ImRect customRect;
ImRect legendRect;
ImRect clippingRect;
ImRect legendClippingRect;
};
ImVector<CustomDraw> customDraws;
ImVector<CustomDraw> compactCustomDraws;
// zoom in/out
const int visibleFrameCount = (int)floorf((canvas_size.x - legendWidth) / framePixelWidth);
const float barWidthRatio = ImMin(visibleFrameCount / (float)frameCount, 1.f);
const float barWidthInPixels = barWidthRatio * (canvas_size.x - legendWidth);
ImRect regionRect(canvas_pos, canvas_pos + canvas_size);
static bool panningView = false;
static ImVec2 panningViewSource;
static int panningViewFrame;
if (ImGui::IsWindowFocused() && io.KeyAlt && io.MouseDown[2])
{
if (!panningView)
{
panningViewSource = io.MousePos;
panningView = true;
panningViewFrame = *firstFrame;
}
*firstFrame = panningViewFrame - int((io.MousePos.x - panningViewSource.x) / framePixelWidth);
*firstFrame = ImClamp(*firstFrame, sequence->GetFrameMin(), sequence->GetFrameMax() - visibleFrameCount);
}
if (panningView && !io.MouseDown[2])
{
panningView = false;
}
framePixelWidthTarget = ImClamp(framePixelWidthTarget, 0.1f, 50.f);
framePixelWidth = ImLerp(framePixelWidth, framePixelWidthTarget, 0.33f);
frameCount = sequence->GetFrameMax() - sequence->GetFrameMin();
if (visibleFrameCount >= frameCount && firstFrame)
*firstFrame = sequence->GetFrameMin();
// --
if (expanded && !*expanded)
{
ImGui::InvisibleButton("canvas", ImVec2(canvas_size.x - canvas_pos.x, (float)ItemHeight));
draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0);
char tmps[512];
ImFormatString(tmps, IM_ARRAYSIZE(tmps), sequence->GetCollapseFmt(), frameCount, sequenceCount);
draw_list->AddText(ImVec2(canvas_pos.x + 26, canvas_pos.y + 2), 0xFFFFFFFF, tmps);
}
else
{
bool hasScrollBar(true);
/*
int framesPixelWidth = int(frameCount * framePixelWidth);
if ((framesPixelWidth + legendWidth) >= canvas_size.x)
{
hasScrollBar = true;
}
*/
// test scroll area
ImVec2 headerSize(canvas_size.x, (float)ItemHeight);
ImVec2 scrollBarSize(canvas_size.x, 14.f);
ImGui::InvisibleButton("topBar", headerSize);
draw_list->AddRectFilled(canvas_pos, canvas_pos + headerSize, 0xFFFF0000, 0);
ImVec2 childFramePos = ImGui::GetCursorScreenPos();
ImVec2 childFrameSize(canvas_size.x, canvas_size.y - 8.f - headerSize.y - (hasScrollBar ? scrollBarSize.y : 0));
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
ImGui::BeginChildFrame(889, childFrameSize);
sequence->focused = ImGui::IsWindowFocused();
ImGui::InvisibleButton("contentBar", ImVec2(canvas_size.x, float(controlHeight)));
const ImVec2 contentMin = ImGui::GetItemRectMin();
const ImVec2 contentMax = ImGui::GetItemRectMax();
const ImRect contentRect(contentMin, contentMax);
const float contentHeight = contentMax.y - contentMin.y;
// full background
draw_list->AddRectFilled(canvas_pos, canvas_pos + canvas_size, 0xFF242424, 0);
// current frame top
ImRect topRect(ImVec2(canvas_pos.x + legendWidth, canvas_pos.y), ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + ItemHeight));
if (!MovingCurrentFrame && !MovingScrollBar && movingEntry == -1 && sequenceOptions & SEQUENCER_CHANGE_FRAME && currentFrame && *currentFrame >= 0 && topRect.Contains(io.MousePos) && io.MouseDown[0])
{
MovingCurrentFrame = true;
}
if (MovingCurrentFrame)
{
if (frameCount)
{
*currentFrame = (int)((io.MousePos.x - topRect.Min.x) / framePixelWidth) + firstFrameUsed;
if (*currentFrame < sequence->GetFrameMin())
*currentFrame = sequence->GetFrameMin();
if (*currentFrame >= sequence->GetFrameMax())
*currentFrame = sequence->GetFrameMax();
}
if (!io.MouseDown[0])
MovingCurrentFrame = false;
}
//header
draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0);
if (sequenceOptions & SEQUENCER_ADD)
{
if (SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + legendWidth - ItemHeight, canvas_pos.y + 2), true))
ImGui::OpenPopup("addEntry");
if (ImGui::BeginPopup("addEntry"))
{
for (int i = 0; i < sequence->GetItemTypeCount(); i++)
if (ImGui::Selectable(sequence->GetItemTypeName(i)))
{
sequence->Add(i);
*selectedEntry = sequence->GetItemCount() - 1;
}
ImGui::EndPopup();
popupOpened = true;
}
}
//header frame number and lines
int modFrameCount = 10;
int frameStep = 1;
while ((modFrameCount * framePixelWidth) < 150)
{
modFrameCount *= 2;
frameStep *= 2;
};
int halfModFrameCount = modFrameCount / 2;
auto drawLine = [&](int i, int regionHeight) {
bool baseIndex = ((i % modFrameCount) == 0) || (i == sequence->GetFrameMax() || i == sequence->GetFrameMin());
bool halfIndex = (i % halfModFrameCount) == 0;
int px = (int)canvas_pos.x + int(i * framePixelWidth) + legendWidth - int(firstFrameUsed * framePixelWidth);
int tiretStart = baseIndex ? 4 : (halfIndex ? 10 : 14);
int tiretEnd = baseIndex ? regionHeight : ItemHeight;
if (px <= (canvas_size.x + canvas_pos.x) && px >= (canvas_pos.x + legendWidth))
{
draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)tiretStart), ImVec2((float)px, canvas_pos.y + (float)tiretEnd - 1), 0xFF606060, 1);
draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)ItemHeight), ImVec2((float)px, canvas_pos.y + (float)regionHeight - 1), 0x30606060, 1);
}
if (baseIndex && px > (canvas_pos.x + legendWidth))
{
char tmps[512];
ImFormatString(tmps, IM_ARRAYSIZE(tmps), "%d", i);
draw_list->AddText(ImVec2((float)px + 3.f, canvas_pos.y), 0xFFBBBBBB, tmps);
}
};
auto drawLineContent = [&](int i, int /*regionHeight*/) {
int px = (int)canvas_pos.x + int(i * framePixelWidth) + legendWidth - int(firstFrameUsed * framePixelWidth);
int tiretStart = int(contentMin.y);
int tiretEnd = int(contentMax.y);
if (px <= (canvas_size.x + canvas_pos.x) && px >= (canvas_pos.x + legendWidth))
{
//draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)tiretStart), ImVec2((float)px, canvas_pos.y + (float)tiretEnd - 1), 0xFF606060, 1);
draw_list->AddLine(ImVec2(float(px), float(tiretStart)), ImVec2(float(px), float(tiretEnd)), 0x30606060, 1);
}
};
for (int i = sequence->GetFrameMin(); i <= sequence->GetFrameMax(); i += frameStep)
{
drawLine(i, ItemHeight);
}
drawLine(sequence->GetFrameMin(), ItemHeight);
drawLine(sequence->GetFrameMax(), ItemHeight);
/*
draw_list->AddLine(canvas_pos, ImVec2(canvas_pos.x, canvas_pos.y + controlHeight), 0xFF000000, 1);
draw_list->AddLine(ImVec2(canvas_pos.x, canvas_pos.y + ItemHeight), ImVec2(canvas_size.x, canvas_pos.y + ItemHeight), 0xFF000000, 1);
*/
// clip content
draw_list->PushClipRect(childFramePos, childFramePos + childFrameSize, true);
// draw item names in the legend rect on the left
size_t customHeight = 0;
for (int i = 0; i < sequenceCount; i++)
{
int type;
sequence->Get(i, NULL, NULL, &type, NULL);
ImVec2 tpos(contentMin.x + 3, contentMin.y + i * ItemHeight + 2 + customHeight);
draw_list->AddText(tpos, 0xFFFFFFFF, sequence->GetItemLabel(i));
if (sequenceOptions & SEQUENCER_DEL)
{
if (SequencerAddDelButton(draw_list, ImVec2(contentMin.x + legendWidth - ItemHeight + 2 - 10, tpos.y + 2), false))
delEntry = i;
if (SequencerAddDelButton(draw_list, ImVec2(contentMin.x + legendWidth - ItemHeight - ItemHeight + 2 - 10, tpos.y + 2), true))
dupEntry = i;
}
customHeight += sequence->GetCustomHeight(i);
}
// slots background
customHeight = 0;
for (int i = 0; i < sequenceCount; i++)
{
unsigned int col = (i & 1) ? 0xFF3A3636 : 0xFF413D3D;
size_t localCustomHeight = sequence->GetCustomHeight(i);
ImVec2 pos = ImVec2(contentMin.x + legendWidth, contentMin.y + ItemHeight * i + 1 + customHeight);
ImVec2 sz = ImVec2(canvas_size.x + canvas_pos.x, pos.y + ItemHeight - 1 + localCustomHeight);
if (!popupOpened && cy >= pos.y && cy < pos.y + (ItemHeight + localCustomHeight) && movingEntry == -1 && cx>contentMin.x && cx < contentMin.x + canvas_size.x)
{
col += 0x80201008;
pos.x -= legendWidth;
}
draw_list->AddRectFilled(pos, sz, col, 0);
customHeight += localCustomHeight;
}
draw_list->PushClipRect(childFramePos + ImVec2(float(legendWidth), 0.f), childFramePos + childFrameSize, true);
// vertical frame lines in content area
for (int i = sequence->GetFrameMin(); i <= sequence->GetFrameMax(); i += frameStep)
{
drawLineContent(i, int(contentHeight));
}
drawLineContent(sequence->GetFrameMin(), int(contentHeight));
drawLineContent(sequence->GetFrameMax(), int(contentHeight));
// selection
bool selected = selectedEntry && (*selectedEntry >= 0);
if (selected)
{
customHeight = 0;
for (int i = 0; i < *selectedEntry; i++)
customHeight += sequence->GetCustomHeight(i);
draw_list->AddRectFilled(ImVec2(contentMin.x, contentMin.y + ItemHeight * *selectedEntry + customHeight), ImVec2(contentMin.x + canvas_size.x, contentMin.y + ItemHeight * (*selectedEntry + 1) + customHeight), 0x801080FF, 1.f);
}
// slots
customHeight = 0;
for (int i = 0; i < sequenceCount; i++)
{
int* start, * end;
unsigned int color;
sequence->Get(i, &start, &end, NULL, &color);
size_t localCustomHeight = sequence->GetCustomHeight(i);
ImVec2 pos = ImVec2(contentMin.x + legendWidth - firstFrameUsed * framePixelWidth, contentMin.y + ItemHeight * i + 1 + customHeight);
ImVec2 slotP1(pos.x + *start * framePixelWidth, pos.y + 2);
ImVec2 slotP2(pos.x + *end * framePixelWidth + framePixelWidth, pos.y + ItemHeight - 2);
ImVec2 slotP3(pos.x + *end * framePixelWidth + framePixelWidth, pos.y + ItemHeight - 2 + localCustomHeight);
unsigned int slotColor = color | 0xFF000000;
unsigned int slotColorHalf = (color & 0xFFFFFF) | 0x40000000;
if (slotP1.x <= (canvas_size.x + contentMin.x) && slotP2.x >= (contentMin.x + legendWidth))
{
draw_list->AddRectFilled(slotP1, slotP3, slotColorHalf, 2);
draw_list->AddRectFilled(slotP1, slotP2, slotColor, 2);
}
if (ImRect(slotP1, slotP2).Contains(io.MousePos) && io.MouseDoubleClicked[0])
{
sequence->DoubleClick(i);
}
// Ensure grabbable handles
const float max_handle_width = slotP2.x - slotP1.x / 3.0f;
const float min_handle_width = ImMin(10.0f, max_handle_width);
const float handle_width = ImClamp(framePixelWidth / 2.0f, min_handle_width, max_handle_width);
ImRect rects[3] = { ImRect(slotP1, ImVec2(slotP1.x + handle_width, slotP2.y))
, ImRect(ImVec2(slotP2.x - handle_width, slotP1.y), slotP2)
, ImRect(slotP1, slotP2) };
const unsigned int quadColor[] = { 0xFFFFFFFF, 0xFFFFFFFF, slotColor + (selected ? 0 : 0x202020) };
if (movingEntry == -1 && (sequenceOptions & SEQUENCER_EDIT_STARTEND))// TODOFOCUS && backgroundRect.Contains(io.MousePos))
{
for (int j = 2; j >= 0; j--)
{
ImRect& rc = rects[j];
if (!rc.Contains(io.MousePos))
continue;
draw_list->AddRectFilled(rc.Min, rc.Max, quadColor[j], 2);
}
for (int j = 0; j < 3; j++)
{
ImRect& rc = rects[j];
if (!rc.Contains(io.MousePos))
continue;
if (!ImRect(childFramePos, childFramePos + childFrameSize).Contains(io.MousePos))
continue;
if (ImGui::IsMouseClicked(0) && !MovingScrollBar && !MovingCurrentFrame)
{
movingEntry = i;
movingPos = cx;
movingPart = j + 1;
sequence->BeginEdit(movingEntry);
break;
}
}
}
// custom draw
if (localCustomHeight > 0)
{
ImVec2 rp(canvas_pos.x, contentMin.y + ItemHeight * i + 1 + customHeight);
ImRect customRect(rp + ImVec2(legendWidth - (firstFrameUsed - sequence->GetFrameMin() - 0.5f) * framePixelWidth, float(ItemHeight)),
rp + ImVec2(legendWidth + (sequence->GetFrameMax() - firstFrameUsed - 0.5f + 2.f) * framePixelWidth, float(localCustomHeight + ItemHeight)));
ImRect clippingRect(rp + ImVec2(float(legendWidth), float(ItemHeight)), rp + ImVec2(canvas_size.x, float(localCustomHeight + ItemHeight)));
ImRect legendRect(rp + ImVec2(0.f, float(ItemHeight)), rp + ImVec2(float(legendWidth), float(localCustomHeight)));
ImRect legendClippingRect(canvas_pos + ImVec2(0.f, float(ItemHeight)), canvas_pos + ImVec2(float(legendWidth), float(localCustomHeight + ItemHeight)));
customDraws.push_back({ i, customRect, legendRect, clippingRect, legendClippingRect });
}
else
{
ImVec2 rp(canvas_pos.x, contentMin.y + ItemHeight * i + customHeight);
ImRect customRect(rp + ImVec2(legendWidth - (firstFrameUsed - sequence->GetFrameMin() - 0.5f) * framePixelWidth, float(0.f)),
rp + ImVec2(legendWidth + (sequence->GetFrameMax() - firstFrameUsed - 0.5f + 2.f) * framePixelWidth, float(ItemHeight)));
ImRect clippingRect(rp + ImVec2(float(legendWidth), float(0.f)), rp + ImVec2(canvas_size.x, float(ItemHeight)));
compactCustomDraws.push_back({ i, customRect, ImRect(), clippingRect, ImRect() });
}
customHeight += localCustomHeight;
}
// moving
if (/*backgroundRect.Contains(io.MousePos) && */movingEntry >= 0)
{
#if IMGUI_VERSION_NUM >= 18723
ImGui::SetNextFrameWantCaptureMouse(true);
#else
ImGui::CaptureMouseFromApp();
#endif
int diffFrame = int((cx - movingPos) / framePixelWidth);
if (std::abs(diffFrame) > 0)
{
int* start, * end;
sequence->Get(movingEntry, &start, &end, NULL, NULL);
if (selectedEntry)
*selectedEntry = movingEntry;
int& l = *start;
int& r = *end;
if (movingPart & 1)
l += diffFrame;
if (movingPart & 2)
r += diffFrame;
if (l < 0)
{
if (movingPart & 2)
r -= l;
l = 0;
}
if (movingPart & 1 && l > r)
l = r;
if (movingPart & 2 && r < l)
r = l;
movingPos += int(diffFrame * framePixelWidth);
}
if (!io.MouseDown[0])
{
// single select
if (!diffFrame && movingPart && selectedEntry)
{
*selectedEntry = movingEntry;
ret = true;
}
movingEntry = -1;
sequence->EndEdit();
}
}
// cursor
if (currentFrame && firstFrame && *currentFrame >= *firstFrame && *currentFrame <= sequence->GetFrameMax())
{
static const float cursorWidth = 8.f;
float cursorOffset = contentMin.x + legendWidth + (*currentFrame - firstFrameUsed) * framePixelWidth + framePixelWidth / 2 - cursorWidth * 0.5f;
draw_list->AddLine(ImVec2(cursorOffset, canvas_pos.y), ImVec2(cursorOffset, contentMax.y), 0xA02A2AFF, cursorWidth);
char tmps[512];
ImFormatString(tmps, IM_ARRAYSIZE(tmps), "%d", *currentFrame);
draw_list->AddText(ImVec2(cursorOffset + 10, canvas_pos.y + 2), 0xFF2A2AFF, tmps);
}
draw_list->PopClipRect();
draw_list->PopClipRect();
for (auto& customDraw : customDraws)
sequence->CustomDraw(customDraw.index, draw_list, customDraw.customRect, customDraw.legendRect, customDraw.clippingRect, customDraw.legendClippingRect);
for (auto& customDraw : compactCustomDraws)
sequence->CustomDrawCompact(customDraw.index, draw_list, customDraw.customRect, customDraw.clippingRect);
// copy paste
if (sequenceOptions & SEQUENCER_COPYPASTE)
{
ImRect rectCopy(ImVec2(contentMin.x + 100, canvas_pos.y + 2)
, ImVec2(contentMin.x + 100 + 30, canvas_pos.y + ItemHeight - 2));
bool inRectCopy = rectCopy.Contains(io.MousePos);
unsigned int copyColor = inRectCopy ? 0xFF1080FF : 0xFF000000;
draw_list->AddText(rectCopy.Min, copyColor, "Copy");
ImRect rectPaste(ImVec2(contentMin.x + 140, canvas_pos.y + 2)
, ImVec2(contentMin.x + 140 + 30, canvas_pos.y + ItemHeight - 2));
bool inRectPaste = rectPaste.Contains(io.MousePos);
unsigned int pasteColor = inRectPaste ? 0xFF1080FF : 0xFF000000;
draw_list->AddText(rectPaste.Min, pasteColor, "Paste");
if (inRectCopy && io.MouseReleased[0])
{
sequence->Copy();
}
if (inRectPaste && io.MouseReleased[0])
{
sequence->Paste();
}
}
//
ImGui::EndChildFrame();
ImGui::PopStyleColor();
if (hasScrollBar)
{
ImGui::InvisibleButton("scrollBar", scrollBarSize);
ImVec2 scrollBarMin = ImGui::GetItemRectMin();
ImVec2 scrollBarMax = ImGui::GetItemRectMax();
// ratio = number of frames visible in control / number to total frames
float startFrameOffset = ((float)(firstFrameUsed - sequence->GetFrameMin()) / (float)frameCount) * (canvas_size.x - legendWidth);
ImVec2 scrollBarA(scrollBarMin.x + legendWidth, scrollBarMin.y - 2);
ImVec2 scrollBarB(scrollBarMin.x + canvas_size.x, scrollBarMax.y - 1);
draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF222222, 0);
ImRect scrollBarRect(scrollBarA, scrollBarB);
bool inScrollBar = scrollBarRect.Contains(io.MousePos);
draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF101010, 8);
ImVec2 scrollBarC(scrollBarMin.x + legendWidth + startFrameOffset, scrollBarMin.y);
ImVec2 scrollBarD(scrollBarMin.x + legendWidth + barWidthInPixels + startFrameOffset, scrollBarMax.y - 2);
draw_list->AddRectFilled(scrollBarC, scrollBarD, (inScrollBar || MovingScrollBar) ? 0xFF606060 : 0xFF505050, 6);
ImRect barHandleLeft(scrollBarC, ImVec2(scrollBarC.x + 14, scrollBarD.y));
ImRect barHandleRight(ImVec2(scrollBarD.x - 14, scrollBarC.y), scrollBarD);
bool onLeft = barHandleLeft.Contains(io.MousePos);
bool onRight = barHandleRight.Contains(io.MousePos);
static bool sizingRBar = false;
static bool sizingLBar = false;
draw_list->AddRectFilled(barHandleLeft.Min, barHandleLeft.Max, (onLeft || sizingLBar) ? 0xFFAAAAAA : 0xFF666666, 6);
draw_list->AddRectFilled(barHandleRight.Min, barHandleRight.Max, (onRight || sizingRBar) ? 0xFFAAAAAA : 0xFF666666, 6);
ImRect scrollBarThumb(scrollBarC, scrollBarD);
static const float MinBarWidth = 44.f;
if (sizingRBar)
{
if (!io.MouseDown[0])
{
sizingRBar = false;
}
else
{
float barNewWidth = ImMax(barWidthInPixels + io.MouseDelta.x, MinBarWidth);
float barRatio = barNewWidth / barWidthInPixels;
framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio;
int newVisibleFrameCount = int((canvas_size.x - legendWidth) / framePixelWidthTarget);
int lastFrame = *firstFrame + newVisibleFrameCount;
if (lastFrame > sequence->GetFrameMax())
{
framePixelWidthTarget = framePixelWidth = (canvas_size.x - legendWidth) / float(sequence->GetFrameMax() - *firstFrame);
}
}
}
else if (sizingLBar)
{
if (!io.MouseDown[0])
{
sizingLBar = false;
}
else
{
if (fabsf(io.MouseDelta.x) > FLT_EPSILON)
{
float barNewWidth = ImMax(barWidthInPixels - io.MouseDelta.x, MinBarWidth);
float barRatio = barNewWidth / barWidthInPixels;
float previousFramePixelWidthTarget = framePixelWidthTarget;
framePixelWidthTarget = framePixelWidth = framePixelWidth / barRatio;
int newVisibleFrameCount = int(visibleFrameCount / barRatio);
int newFirstFrame = *firstFrame + newVisibleFrameCount - visibleFrameCount;
newFirstFrame = ImClamp(newFirstFrame, sequence->GetFrameMin(), ImMax(sequence->GetFrameMax() - visibleFrameCount, sequence->GetFrameMin()));
if (newFirstFrame == *firstFrame)
{
framePixelWidth = framePixelWidthTarget = previousFramePixelWidthTarget;
}
else
{
*firstFrame = newFirstFrame;
}
}
}
}
else
{
if (MovingScrollBar)
{
if (!io.MouseDown[0])
{
MovingScrollBar = false;
}
else
{
float framesPerPixelInBar = barWidthInPixels / (float)visibleFrameCount;
*firstFrame = int((io.MousePos.x - panningViewSource.x) / framesPerPixelInBar) - panningViewFrame;
*firstFrame = ImClamp(*firstFrame, sequence->GetFrameMin(), ImMax(sequence->GetFrameMax() - visibleFrameCount, sequence->GetFrameMin()));
}
}
else
{
if (scrollBarThumb.Contains(io.MousePos) && ImGui::IsMouseClicked(0) && firstFrame && !MovingCurrentFrame && movingEntry == -1)
{
MovingScrollBar = true;
panningViewSource = io.MousePos;
panningViewFrame = -*firstFrame;
}
if (!sizingRBar && onRight && ImGui::IsMouseClicked(0))
sizingRBar = true;
if (!sizingLBar && onLeft && ImGui::IsMouseClicked(0))
sizingLBar = true;
}
}
}
}
ImGui::EndGroup();
if (regionRect.Contains(io.MousePos))
{
bool overCustomDraw = false;
for (auto& custom : customDraws)
{
if (custom.customRect.Contains(io.MousePos))
{
overCustomDraw = true;
}
}
if (overCustomDraw)
{
}
else
{
#if 0
frameOverCursor = *firstFrame + (int)(visibleFrameCount * ((io.MousePos.x - (float)legendWidth - canvas_pos.x) / (canvas_size.x - legendWidth)));
//frameOverCursor = max(min(*firstFrame - visibleFrameCount / 2, frameCount - visibleFrameCount), 0);
/**firstFrame -= frameOverCursor;
*firstFrame *= framePixelWidthTarget / framePixelWidth;
*firstFrame += frameOverCursor;*/
if (io.MouseWheel < -FLT_EPSILON)
{
*firstFrame -= frameOverCursor;
*firstFrame = int(*firstFrame * 1.1f);
framePixelWidthTarget *= 0.9f;
*firstFrame += frameOverCursor;
}
if (io.MouseWheel > FLT_EPSILON)
{
*firstFrame -= frameOverCursor;
*firstFrame = int(*firstFrame * 0.9f);
framePixelWidthTarget *= 1.1f;
*firstFrame += frameOverCursor;
}
#endif
}
}
if (expanded)
{
if (SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + 2, canvas_pos.y + 2), !*expanded))
*expanded = !*expanded;
}
if (delEntry != -1)
{
sequence->Del(delEntry);
if (selectedEntry && (*selectedEntry == delEntry || *selectedEntry >= sequence->GetItemCount()))
*selectedEntry = -1;
}
if (dupEntry != -1)
{
sequence->Duplicate(dupEntry);
}
return ret;
}
}

View file

@ -0,0 +1,79 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
#include <cstddef>
struct ImDrawList;
struct ImRect;
namespace ImSequencer
{
enum SEQUENCER_OPTIONS
{
SEQUENCER_EDIT_NONE = 0,
SEQUENCER_EDIT_STARTEND = 1 << 1,
SEQUENCER_CHANGE_FRAME = 1 << 3,
SEQUENCER_ADD = 1 << 4,
SEQUENCER_DEL = 1 << 5,
SEQUENCER_COPYPASTE = 1 << 6,
SEQUENCER_EDIT_ALL = SEQUENCER_EDIT_STARTEND | SEQUENCER_CHANGE_FRAME
};
struct SequenceInterface
{
bool focused = false;
virtual int GetFrameMin() const = 0;
virtual int GetFrameMax() const = 0;
virtual int GetItemCount() const = 0;
virtual void BeginEdit(int /*index*/) {}
virtual void EndEdit() {}
virtual int GetItemTypeCount() const { return 0; }
virtual const char* GetItemTypeName(int /*typeIndex*/) const { return ""; }
virtual const char* GetItemLabel(int /*index*/) const { return ""; }
virtual const char* GetCollapseFmt() const { return "%d Frames / %d entries"; }
virtual void Get(int index, int** start, int** end, int* type, unsigned int* color) = 0;
virtual void Add(int /*type*/) {}
virtual void Del(int /*index*/) {}
virtual void Duplicate(int /*index*/) {}
virtual void Copy() {}
virtual void Paste() {}
virtual size_t GetCustomHeight(int /*index*/) { return 0; }
virtual void DoubleClick(int /*index*/) {}
virtual void CustomDraw(int /*index*/, ImDrawList* /*draw_list*/, const ImRect& /*rc*/, const ImRect& /*legendRect*/, const ImRect& /*clippingRect*/, const ImRect& /*legendClippingRect*/) {}
virtual void CustomDrawCompact(int /*index*/, ImDrawList* /*draw_list*/, const ImRect& /*rc*/, const ImRect& /*clippingRect*/) {}
virtual ~SequenceInterface() = default;
};
// return true if selection is made
bool Sequencer(SequenceInterface* sequence, int* currentFrame, bool* expanded, int* selectedEntry, int* firstFrame, int sequenceOptions);
}

View file

@ -0,0 +1,245 @@
// https://github.com/CedricGuillemet/ImGuizmo
// v 1.89 WIP
//
// The MIT License(MIT)
//
// Copyright(c) 2021 Cedric Guillemet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
namespace ImZoomSlider
{
typedef int ImGuiZoomSliderFlags;
enum ImGuiPopupFlags_
{
ImGuiZoomSliderFlags_None = 0,
ImGuiZoomSliderFlags_Vertical = 1,
ImGuiZoomSliderFlags_NoAnchors = 2,
ImGuiZoomSliderFlags_NoMiddleCarets = 4,
ImGuiZoomSliderFlags_NoWheel = 8,
};
template<typename T> bool ImZoomSlider(const T lower, const T higher, T& viewLower, T& viewHigher, float wheelRatio = 0.01f, ImGuiZoomSliderFlags flags = ImGuiZoomSliderFlags_None)
{
bool interacted = false;
ImGuiIO& io = ImGui::GetIO();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
static const float handleSize = 12;
static const float roundRadius = 3.f;
static const char* controlName = "ImZoomSlider";
static bool movingScrollBarSvg = false;
static bool sizingRBarSvg = false;
static bool sizingLBarSvg = false;
static ImGuiID editingId = (ImGuiID)-1;
static float scrollingSource = 0.f;
static float saveViewLower;
static float saveViewHigher;
const bool isVertical = flags & ImGuiZoomSliderFlags_Vertical;
const ImVec2 canvasPos = ImGui::GetCursorScreenPos();
const ImVec2 canvasSize = ImGui::GetContentRegionAvail();
const float canvasSizeLength = isVertical ? ImGui::GetItemRectSize().y : canvasSize.x;
const ImVec2 scrollBarSize = isVertical ? ImVec2(14.f, canvasSizeLength) : ImVec2(canvasSizeLength, 14.f);
ImGui::InvisibleButton(controlName, scrollBarSize);
const ImGuiID currentId = ImGui::GetID(controlName);
const bool usingEditingId = currentId == editingId;
const bool canUseControl = usingEditingId || editingId == -1;
const bool movingScrollBar = usingEditingId ? movingScrollBarSvg : false;
const bool sizingRBar = usingEditingId ? sizingRBarSvg : false;
const bool sizingLBar = usingEditingId ? sizingLBarSvg : false;
const int componentIndex = isVertical ? 1 : 0;
const ImVec2 scrollBarMin = ImGui::GetItemRectMin();
const ImVec2 scrollBarMax = ImGui::GetItemRectMax();
const ImVec2 scrollBarA = ImVec2(scrollBarMin.x, scrollBarMin.y) - (isVertical ? ImVec2(2,0) : ImVec2(0,2));
const ImVec2 scrollBarB = isVertical ? ImVec2(scrollBarMax.x - 1.f, scrollBarMin.y + canvasSizeLength) : ImVec2(scrollBarMin.x + canvasSizeLength, scrollBarMax.y - 1.f);
const float scrollStart = ((viewLower - lower) / (higher - lower)) * canvasSizeLength + scrollBarMin[componentIndex];
const float scrollEnd = ((viewHigher - lower) / (higher - lower)) * canvasSizeLength + scrollBarMin[componentIndex];
const float screenSize = scrollEnd - scrollStart;
const ImVec2 scrollTopLeft = isVertical ? ImVec2(scrollBarMin.x, scrollStart) : ImVec2(scrollStart, scrollBarMin.y);
const ImVec2 scrollBottomRight = isVertical ? ImVec2(scrollBarMax.x - 2.f, scrollEnd) : ImVec2(scrollEnd, scrollBarMax.y - 2.f);
const bool inScrollBar = canUseControl && ImRect(scrollTopLeft, scrollBottomRight).Contains(io.MousePos);
const ImRect scrollBarRect(scrollBarA, scrollBarB);
const float deltaScreen = io.MousePos[componentIndex] - scrollingSource;
const float deltaView = ((higher - lower) / canvasSizeLength) * deltaScreen;
const uint32_t barColor = ImGui::GetColorU32((inScrollBar || movingScrollBar) ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
const float middleCoord = (scrollStart + scrollEnd) * 0.5f;
const bool insideControl = canUseControl && ImRect(scrollBarMin, scrollBarMax).Contains(io.MousePos);
const bool hasAnchors = !(flags & ImGuiZoomSliderFlags_NoAnchors);
const float viewMinSize = ((3.f * handleSize) / canvasSizeLength) * (higher - lower);
const auto ClipView = [lower, higher, &viewLower, &viewHigher]() {
if (viewLower < lower)
{
const float deltaClip = lower - viewLower;
viewLower += deltaClip;
viewHigher += deltaClip;
}
if (viewHigher > higher)
{
const float deltaClip = viewHigher - higher;
viewLower -= deltaClip;
viewHigher -= deltaClip;
}
};
bool onLeft = false;
bool onRight = false;
draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF101010, roundRadius);
draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF222222, 0);
draw_list->AddRectFilled(scrollTopLeft, scrollBottomRight, barColor, roundRadius);
if (!(flags & ImGuiZoomSliderFlags_NoMiddleCarets))
{
for (float i = 0.5f; i < 3.f; i += 1.f)
{
const float coordA = middleCoord - handleSize * 0.5f;
const float coordB = middleCoord + handleSize * 0.5f;
ImVec2 base = scrollBarMin;
base.x += scrollBarSize.x * 0.25f * i;
base.y += scrollBarSize.y * 0.25f * i;
if (isVertical)
{
draw_list->AddLine(ImVec2(base.x, coordA), ImVec2(base.x, coordB), ImGui::GetColorU32(ImGuiCol_SliderGrab));
}
else
{
draw_list->AddLine(ImVec2(coordA, base.y), ImVec2(coordB, base.y), ImGui::GetColorU32(ImGuiCol_SliderGrab));
}
}
}
// Mouse wheel
if (io.MouseClicked[0] && insideControl && !inScrollBar)
{
const float ratio = (io.MousePos[componentIndex] - scrollBarMin[componentIndex]) / (scrollBarMax[componentIndex] - scrollBarMin[componentIndex]);
const float size = (higher - lower);
const float halfViewSize = (viewHigher - viewLower) * 0.5f;
const float middle = ratio * size + lower;
viewLower = middle - halfViewSize;
viewHigher = middle + halfViewSize;
ClipView();
interacted = true;
}
if (!(flags & ImGuiZoomSliderFlags_NoWheel) && inScrollBar && fabsf(io.MouseWheel) > 0.f)
{
const float ratio = (io.MousePos[componentIndex] - scrollStart) / (scrollEnd - scrollStart);
const float amount = io.MouseWheel * wheelRatio * (viewHigher - viewLower);
viewLower -= ratio * amount;
viewHigher += (1.f - ratio) * amount;
ClipView();
interacted = true;
}
if (screenSize > handleSize * 2.f && hasAnchors)
{
const ImRect barHandleLeft(scrollTopLeft, isVertical ? ImVec2(scrollBottomRight.x, scrollTopLeft.y + handleSize) : ImVec2(scrollTopLeft.x + handleSize, scrollBottomRight.y));
const ImRect barHandleRight(isVertical ? ImVec2(scrollTopLeft.x, scrollBottomRight.y - handleSize) : ImVec2(scrollBottomRight.x - handleSize, scrollTopLeft.y), scrollBottomRight);
onLeft = barHandleLeft.Contains(io.MousePos);
onRight = barHandleRight.Contains(io.MousePos);
draw_list->AddRectFilled(barHandleLeft.Min, barHandleLeft.Max, ImGui::GetColorU32((onLeft || sizingLBar) ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), roundRadius);
draw_list->AddRectFilled(barHandleRight.Min, barHandleRight.Max, ImGui::GetColorU32((onRight || sizingRBar) ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), roundRadius);
}
if (sizingRBar)
{
if (!io.MouseDown[0])
{
sizingRBarSvg = false;
editingId = (ImGuiID)-1;
}
else
{
viewHigher = ImMin(saveViewHigher + deltaView, higher);
}
}
else if (sizingLBar)
{
if (!io.MouseDown[0])
{
sizingLBarSvg = false;
editingId = (ImGuiID)-1;
}
else
{
viewLower = ImMax(saveViewLower + deltaView, lower);
}
}
else
{
if (movingScrollBar)
{
if (!io.MouseDown[0])
{
movingScrollBarSvg = false;
editingId = (ImGuiID)-1;
}
else
{
viewLower = saveViewLower + deltaView;
viewHigher = saveViewHigher + deltaView;
ClipView();
}
}
else
{
if (inScrollBar && ImGui::IsMouseClicked(0))
{
movingScrollBarSvg = true;
scrollingSource = io.MousePos[componentIndex];
saveViewLower = viewLower;
saveViewHigher = viewHigher;
editingId = currentId;
}
if (!sizingRBar && onRight && ImGui::IsMouseClicked(0) && hasAnchors)
{
sizingRBarSvg = true;
editingId = currentId;
}
if (!sizingLBar && onLeft && ImGui::IsMouseClicked(0) && hasAnchors)
{
sizingLBarSvg = true;
editingId = currentId;
}
}
}
// minimal size check
if ((viewHigher - viewLower) < viewMinSize)
{
const float middle = (viewLower + viewHigher) * 0.5f;
viewLower = middle - viewMinSize * 0.5f;
viewHigher = middle + viewMinSize * 0.5f;
ClipView();
}
return movingScrollBar || sizingRBar || sizingLBar || interacted;
}
} // namespace

View file

@ -160,7 +160,7 @@ namespace ImGui
timelineXmin + context.Size.x - context.ValuesWidth
};
const auto hovered = ItemHoverable(pointerRect, GetCurrentWindow()->GetID("##_top_selector_neo"));
const auto hovered = ItemHoverable(pointerRect, GetCurrentWindow()->GetID("##_top_selector_neo"), 0);
context.CurrentFrameColor = GetStyleNeoSequencerColorVec4(ImGuiNeoSequencerCol_FramePointer);
@ -369,7 +369,7 @@ namespace ImGui
const ImGuiID id = getKeyframeID(frame);
bool hovered = ItemHoverable(bb, id);
bool hovered = ItemHoverable(bb, id, 0);
if (context.SelectionEnabled && context.Selection.contains(id) &&
(context.StateOfSelection != SelectionState::Selecting))
@ -575,7 +575,7 @@ namespace ImGui
const auto viewWidth = (uint32_t) ((float) totalFrames / context.Zoom);
const bool hovered = ItemHoverable(bb, GetCurrentWindow()->GetID("##zoom_slider"));
const bool hovered = ItemHoverable(bb, GetCurrentWindow()->GetID("##zoom_slider"), 0);
if (hovered)
{

View file

@ -147,7 +147,7 @@ namespace ImGui {
#ifdef __cplusplus
// C++ helper
IMGUI_API bool BeginNeoTimeline(const char* label,std::vector<int32_t> & keyframes ,bool * open = nullptr, ImGuiNeoTimelineFlags flags = ImGuiNeoTimelineFlags_None);
// IMGUI_API bool BeginNeoTimeline(const char* label,std::vector<int32_t> & keyframes ,bool * open = nullptr, ImGuiNeoTimelineFlags flags = ImGuiNeoTimelineFlags_None);
#endif
}