CARVIEW |
Select Language
HTTP/2 200
date: Sat, 19 Jul 2025 16:30:19 GMT
content-type: text/html; charset=utf-8
cache-control: max-age=0, private, must-revalidate
content-security-policy: default-src 'none'; base-uri 'self'; child-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/; connect-src 'self' uploads.github.com www.githubstatus.com collector.github.com raw.githubusercontent.com api.github.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com *.rel.tunnels.api.visualstudio.com wss://*.rel.tunnels.api.visualstudio.com objects-origin.githubusercontent.com copilot-proxy.githubusercontent.com proxy.individual.githubcopilot.com proxy.business.githubcopilot.com proxy.enterprise.githubcopilot.com *.actions.githubusercontent.com wss://*.actions.githubusercontent.com productionresultssa0.blob.core.windows.net/ productionresultssa1.blob.core.windows.net/ productionresultssa2.blob.core.windows.net/ productionresultssa3.blob.core.windows.net/ productionresultssa4.blob.core.windows.net/ productionresultssa5.blob.core.windows.net/ productionresultssa6.blob.core.windows.net/ productionresultssa7.blob.core.windows.net/ productionresultssa8.blob.core.windows.net/ productionresultssa9.blob.core.windows.net/ productionresultssa10.blob.core.windows.net/ productionresultssa11.blob.core.windows.net/ productionresultssa12.blob.core.windows.net/ productionresultssa13.blob.core.windows.net/ productionresultssa14.blob.core.windows.net/ productionresultssa15.blob.core.windows.net/ productionresultssa16.blob.core.windows.net/ productionresultssa17.blob.core.windows.net/ productionresultssa18.blob.core.windows.net/ productionresultssa19.blob.core.windows.net/ github-production-repository-image-32fea6.s3.amazonaws.com github-production-release-asset-2e65be.s3.amazonaws.com insights.github.com wss://alive.github.com api.githubcopilot.com api.individual.githubcopilot.com api.business.githubcopilot.com api.enterprise.githubcopilot.com; font-src github.githubassets.com; form-action 'self' github.com gist.github.com copilot-workspace.githubnext.com objects-origin.githubusercontent.com; frame-ancestors 'none'; frame-src viewscreen.githubusercontent.com notebooks.githubusercontent.com; img-src 'self' data: blob: github.githubassets.com media.githubusercontent.com camo.githubusercontent.com identicons.github.com avatars.githubusercontent.com private-avatars.githubusercontent.com github-cloud.s3.amazonaws.com objects.githubusercontent.com release-assets.githubusercontent.com secured-user-images.githubusercontent.com/ user-images.githubusercontent.com/ private-user-images.githubusercontent.com opengraph.githubassets.com copilotprodattachments.blob.core.windows.net/github-production-copilot-attachments/ github-production-user-asset-6210df.s3.amazonaws.com customer-stories-feed.github.com spotlights-feed.github.com objects-origin.githubusercontent.com *.githubusercontent.com; manifest-src 'self'; media-src github.com user-images.githubusercontent.com/ secured-user-images.githubusercontent.com/ private-user-images.githubusercontent.com github-production-user-asset-6210df.s3.amazonaws.com gist.github.com; script-src github.githubassets.com; style-src 'unsafe-inline' github.githubassets.com; upgrade-insecure-requests; worker-src github.githubassets.com github.com/assets-cdn/worker/ github.com/assets/ gist.github.com/assets-cdn/worker/
link: ; rel=preload; as=fetch; crossorigin=use-credentials
referrer-policy: no-referrer-when-downgrade
server-timing: issue_layout-fragment;desc="issue_layout fragment";dur=223.895867,issue_conversation_content-fragment;desc="issue_conversation_content fragment";dur=617.098162,issue_conversation_sidebar-fragment;desc="issue_conversation_sidebar fragment";dur=36.883996,nginx;desc="NGINX";dur=1.063995,glb;desc="GLB";dur=96.203071
strict-transport-security: max-age=31536000; includeSubdomains; preload
vary: X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, X-Requested-With, Accept,Accept-Encoding, Accept, X-Requested-With
x-content-type-options: nosniff
x-frame-options: deny
x-voltron-version: fd8fbbc
x-xss-protection: 0
server: github.com
content-encoding: gzip
accept-ranges: bytes
set-cookie: _gh_sess=v8ZWoy1XxS%2BRbn9U4%2FR3R9vTfT5Uj2Be%2Fmfat9Z%2FfWA0OLplA0QC40LqXjuiXcTmL7lsPKSzb1XRh06ZrRLgUB1XK6Xno00fh11fvhVG1wy63ZGoiPczx%2BszSYyMdtEauKAgZGdwPwIkjLFzxl2rN6uY2qphz7cj9%2F9NnBJd8U2FKmlZsnVLfVleW%2FF39Z5Bb58fZjJEDig%2BEotYxyysdW0URlKbI%2FGY%2FPVFl5CLFOCmyeWknZDSeAVtn1JfHJfhX%2B32SfwJTztyjwe6uHQNLg%3D%3D--KBsTGC8I%2B2RTCN%2FI--DvfytgA38HvdEcq3ZGvjXg%3D%3D; Path=/; HttpOnly; Secure; SameSite=Lax
set-cookie: _octo=GH1.1.903166689.1752942618; Path=/; Domain=github.com; Expires=Sun, 19 Jul 2026 16:30:18 GMT; Secure; SameSite=Lax
set-cookie: logged_in=no; Path=/; Domain=github.com; Expires=Sun, 19 Jul 2026 16:30:18 GMT; HttpOnly; Secure; SameSite=Lax
x-github-request-id: E7C2:110E0E:681A1D:8290C7:687BC81A
Cubic Bezier widget / Curve editor · Issue #786 · ocornut/imgui · GitHub
No one assignedNo projectsNo milestoneNone yetNo branches or pull requests
Skip to content
Navigation Menu
{{ message }}
-
-
Notifications
You must be signed in to change notification settings - Fork 11k
Open
Labels
Description
Based on #55.
Feat. some code ideas by @nem0 and @jarikomppa
Thanks everybody!
// ImGui Bezier widget. @r-lyeh, public domain
// v1.02: add BezierValue(); comments; usage
// v1.01: out-of-bounds coord snapping; custom border width; spacing; cosmetics
// v1.00: initial version
//
// [ref] https://robnapier.net/faster-bezier
// [ref] https://easings.net/es#easeInSine
//
// Usage:
// { static float v[] = { 0.390f, 0.575f, 0.565f, 1.000f };
// ImGui::Bezier( "easeOutSine", v ); // draw
// float y = ImGui::BezierValue( 0.5f, v ); // x delta in [0..1] range
// }
//#define IMGUI_DEFINE_MATH_OPERATORS
//#include <imgui.h>
//#include <imgui_internal.h>
namespace ImGui
{
template<int steps>
void bezier_table(ImVec2 P[4], ImVec2 results[steps+1] ) {
static float C[ (steps+1) * 4 ], *K = 0;
if( !K ) {
K = C;
for (unsigned step = 0; step <= steps; ++step) {
float t = (float)step/(float)steps;
C[step*4+0] = (1-t)*(1-t)*(1-t); // * P0
C[step*4+1] = 3 * (1-t)*(1-t) * t; // * P1
C[step*4+2] = 3 * (1-t) * t*t; // * P2
C[step*4+3] = t*t*t; // * P3
}
}
for (unsigned step = 0; step <= steps; ++step) {
ImVec2 point = {
K[step*4+0]*P[0].x + K[step*4+1]*P[1].x + K[step*4+2]*P[2].x + K[step*4+3]*P[3].x,
K[step*4+0]*P[0].y + K[step*4+1]*P[1].y + K[step*4+2]*P[2].y + K[step*4+3]*P[3].y
};
results[step] = point;
}
}
float BezierValue( float dt01, float P[4] ) {
enum { STEPS = 256 };
ImVec2 Q[4] = { { 0, 0 }, { P[0], P[1] }, { P[2], P[3] }, { 1, 1 } };
ImVec2 results[STEPS+1];
bezier_table<STEPS>( Q, results );
return results[ (int)((dt01 < 0 ? 0 : dt01 > 1 ? 1 : dt01) * STEPS) ].y;
}
int Bezier( const char *label, float P[4] ) {
// visuals
enum { SMOOTHNESS = 64 }; // curve smoothness: the higher number of segments, the smoother curve
enum { CURVE_WIDTH = 4 }; // main curved line width
enum { LINE_WIDTH = 1 }; // handlers: small lines width
enum { GRAB_RADIUS = 6 }; // handlers: circle radius
enum { GRAB_BORDER = 2 }; // handlers: circle border width
const ImGuiStyle& Style = GetStyle();
const ImGuiIO& IO = GetIO();
ImDrawList* DrawList = GetWindowDrawList();
ImGuiWindow* Window = GetCurrentWindow();
if (Window->SkipItems)
return false;
// header and spacing
int changed = SliderFloat4(label, P, 0, 1, "%.3f", 1.0f);
int hovered = IsItemActive() || IsItemHovered(); // IsItemDragged() ?
Dummy(ImVec2(0,3));
// prepare canvas
const float avail = GetContentRegionAvailWidth();
const float dim = ImMin(avail, 128.f);
ImVec2 Canvas(dim, dim);
ImRect bb(Window->DC.CursorPos, Window->DC.CursorPos + Canvas);
ItemSize(bb);
if (!ItemAdd(bb, NULL))
return changed;
const ImGuiID id = Window->GetID(label);
hovered |= 0 != IsHovered(ImRect(bb.Min, bb.Min + ImVec2(avail,dim)), id);
RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg, 1), true, Style.FrameRounding);
// background grid
for (int i = 0; i <= Canvas.x; i += (Canvas.x/4)) {
DrawList->AddLine(
ImVec2(bb.Min.x + i, bb.Min.y),
ImVec2(bb.Min.x + i, bb.Max.y),
GetColorU32(ImGuiCol_TextDisabled));
}
for (int i = 0; i <= Canvas.y; i += (Canvas.y/4)) {
DrawList->AddLine(
ImVec2(bb.Min.x, bb.Min.y + i),
ImVec2(bb.Max.x, bb.Min.y + i),
GetColorU32(ImGuiCol_TextDisabled));
}
// eval curve
ImVec2 Q[4] = { { 0, 0 }, { P[0], P[1] }, { P[2], P[3] }, { 1, 1 } };
ImVec2 results[SMOOTHNESS+1];
bezier_table<SMOOTHNESS>( Q, results );
// control points: 2 lines and 2 circles
{
char buf[128];
sprintf(buf, "0##%s", label);
// handle grabbers
for(int i = 0; i < 2; ++i)
{
ImVec2 pos = ImVec2( P[i*2+0], 1 - P[i*2+1] ) * (bb.Max - bb.Min) + bb.Min;
SetCursorScreenPos(pos - ImVec2(GRAB_RADIUS, GRAB_RADIUS));
InvisibleButton((buf[0]++, buf), ImVec2(2 * GRAB_RADIUS, 2 * GRAB_RADIUS));
if (IsItemActive() || IsItemHovered())
{
SetTooltip("(%4.3f, %4.3f)", P[i*2+0], P[i*2+1]);
}
if (IsItemActive() && IsMouseDragging(0))
{
P[i*2+0] += GetIO().MouseDelta.x / Canvas.x;
P[i*2+1] -= GetIO().MouseDelta.y / Canvas.y;
changed = true;
}
}
if(hovered||changed) DrawList->PushClipRectFullScreen();
// draw curve
{
ImColor color( GetStyle().Colors[ ImGuiCol_PlotLines ] );
for( int i = 0; i < SMOOTHNESS; ++i ) {
ImVec2 p = { results[i+0].x, 1 - results[i+0].y };
ImVec2 q = { results[i+1].x, 1 - results[i+1].y };
ImVec2 r( p.x * (bb.Max.x - bb.Min.x) + bb.Min.x, p.y * (bb.Max.y - bb.Min.y) + bb.Min.y);
ImVec2 s( q.x * (bb.Max.x - bb.Min.x) + bb.Min.x, q.y * (bb.Max.y - bb.Min.y) + bb.Min.y);
DrawList->AddLine(r, s, color, CURVE_WIDTH);
}
}
// draw lines and grabbers
float luma = IsItemActive() || IsItemHovered() ? 0.5f : 1.0f;
ImVec4 pink( 1.00f, 0.00f, 0.75f, luma ), cyan( 0.00f, 0.75f, 1.00f, luma );
ImVec4 white( GetStyle().Colors[ImGuiCol_Text] );
ImVec2 p1 = ImVec2( P[0], 1 - P[1] ) * (bb.Max - bb.Min) + bb.Min;
ImVec2 p2 = ImVec2( P[2], 1 - P[3] ) * (bb.Max - bb.Min) + bb.Min;
DrawList->AddLine(ImVec2(bb.Min.x,bb.Max.y), p1, ImColor(white), LINE_WIDTH);
DrawList->AddLine(ImVec2(bb.Max.x,bb.Min.y), p2, ImColor(white), LINE_WIDTH);
DrawList->AddCircleFilled(p1, GRAB_RADIUS, ImColor(white));
DrawList->AddCircleFilled(p1, GRAB_RADIUS-GRAB_BORDER, ImColor(pink));
DrawList->AddCircleFilled(p2, GRAB_RADIUS, ImColor(white));
DrawList->AddCircleFilled(p2, GRAB_RADIUS-GRAB_BORDER, ImColor(cyan));
if(hovered||changed) DrawList->PopClipRect();
// restore cursor pos
SetCursorScreenPos(ImVec2(bb.Min.x,bb.Max.y + GRAB_RADIUS)); // :P
}
return changed;
}
void ShowBezierDemo()
{
{ static float v[] = { 0.000f, 0.000f, 1.000f, 1.000f }; Bezier("easeLinear", v ); }
{ static float v[] = { 0.470f, 0.000f, 0.745f, 0.715f }; Bezier("easeInSine", v ); }
{ static float v[] = { 0.390f, 0.575f, 0.565f, 1.000f }; Bezier("easeOutSine", v ); }
{ static float v[] = { 0.445f, 0.050f, 0.550f, 0.950f }; Bezier("easeInOutSine", v ); }
{ static float v[] = { 0.550f, 0.085f, 0.680f, 0.530f }; Bezier("easeInQuad", v ); }
{ static float v[] = { 0.250f, 0.460f, 0.450f, 0.940f }; Bezier("easeOutQuad", v ); }
{ static float v[] = { 0.455f, 0.030f, 0.515f, 0.955f }; Bezier("easeInOutQuad", v ); }
{ static float v[] = { 0.550f, 0.055f, 0.675f, 0.190f }; Bezier("easeInCubic", v ); }
{ static float v[] = { 0.215f, 0.610f, 0.355f, 1.000f }; Bezier("easeOutCubic", v ); }
{ static float v[] = { 0.645f, 0.045f, 0.355f, 1.000f }; Bezier("easeInOutCubic", v ); }
{ static float v[] = { 0.895f, 0.030f, 0.685f, 0.220f }; Bezier("easeInQuart", v ); }
{ static float v[] = { 0.165f, 0.840f, 0.440f, 1.000f }; Bezier("easeOutQuart", v ); }
{ static float v[] = { 0.770f, 0.000f, 0.175f, 1.000f }; Bezier("easeInOutQuart", v ); }
{ static float v[] = { 0.755f, 0.050f, 0.855f, 0.060f }; Bezier("easeInQuint", v ); }
{ static float v[] = { 0.230f, 1.000f, 0.320f, 1.000f }; Bezier("easeOutQuint", v ); }
{ static float v[] = { 0.860f, 0.000f, 0.070f, 1.000f }; Bezier("easeInOutQuint", v ); }
{ static float v[] = { 0.950f, 0.050f, 0.795f, 0.035f }; Bezier("easeInExpo", v ); }
{ static float v[] = { 0.190f, 1.000f, 0.220f, 1.000f }; Bezier("easeOutExpo", v ); }
{ static float v[] = { 1.000f, 0.000f, 0.000f, 1.000f }; Bezier("easeInOutExpo", v ); }
{ static float v[] = { 0.600f, 0.040f, 0.980f, 0.335f }; Bezier("easeInCirc", v ); }
{ static float v[] = { 0.075f, 0.820f, 0.165f, 1.000f }; Bezier("easeOutCirc", v ); }
{ static float v[] = { 0.785f, 0.135f, 0.150f, 0.860f }; Bezier("easeInOutCirc", v ); }
{ static float v[] = { 0.600f, -0.28f, 0.735f, 0.045f }; Bezier("easeInBack", v ); }
{ static float v[] = { 0.175f, 0.885f, 0.320f, 1.275f }; Bezier("easeOutBack", v ); }
{ static float v[] = { 0.680f, -0.55f, 0.265f, 1.550f }; Bezier("easeInOutBack", v ); }
// easeInElastic: not a bezier
// easeOutElastic: not a bezier
// easeInOutElastic: not a bezier
// easeInBounce: not a bezier
// easeOutBounce: not a bezier
// easeInOutBounce: not a bezier
}
}
ocornut, Flix01, nem0, ongamex, RobertoMalatesta and 26 moreAutoplay1999, stebett and tay10r
Metadata
Metadata
Assignees
Labels
Projects
Milestone
Relationships
Development
Issue actions
You can’t perform that action at this time.