draw: Ngon/Ellipse/Bezier/concave fill; demo: custom rendering app
This commit is contained in:
@@ -686,4 +686,269 @@ function ShowExampleAppWindowTitles(p_open)
|
||||
return p_open
|
||||
end
|
||||
|
||||
----------------------------------------------------------------
|
||||
-- [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
|
||||
----------------------------------------------------------------
|
||||
|
||||
local CRS = { sz = 42.0, base_rounding = 8.0, base_thickness = 3.0, animate_rounding = false, animate_thickness = false, ngon_segments = 6,
|
||||
circle_override = false, circle_override_v = 12, curve_override = false, curve_override_v = 8,
|
||||
colf_stroke = ImVec4(1.000, 0.384, 0.169, 1.000), colf_fill = ImVec4(0.416, 0.378, 0.420, 1.000),
|
||||
points = {}, scrolling = ImVec2(0.0, 0.0), opt_enable_grid = true, opt_enable_context_menu = true, adding_line = false,
|
||||
draw_bg = true, draw_fg = true }
|
||||
|
||||
local function CustomRenderingPrimitives()
|
||||
local s = CRS
|
||||
ImGui.PushItemWidth(-ImGui.GetFontSize() * 15)
|
||||
ImGui.PushItemFlag(ImGuiItemFlags.LiveEditOnInput, true)
|
||||
local draw_list = ImGui.GetWindowDrawList()
|
||||
|
||||
ImGui.Text("Gradients")
|
||||
local gradient_size = ImVec2(ImGui.CalcItemWidth(), ImGui.GetFrameHeight())
|
||||
ImGui.InvisibleButton("##gradient1", gradient_size)
|
||||
local col_a, col_b = ImGui.GetColorU32(IM_COL32(0, 0, 0, 255), nil, true), ImGui.GetColorU32(IM_COL32(255, 255, 255, 255), nil, true)
|
||||
draw_list:AddRectFilledMultiColor(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), col_a, col_b, col_b, col_a)
|
||||
ImGui.InvisibleButton("##gradient2", gradient_size)
|
||||
col_a, col_b = ImGui.GetColorU32(IM_COL32(0, 255, 0, 255), nil, true), ImGui.GetColorU32(IM_COL32(255, 0, 0, 255), nil, true)
|
||||
draw_list:AddRectFilledMultiColor(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), col_a, col_b, col_b, col_a)
|
||||
|
||||
ImGui.Text("All primitives")
|
||||
s.sz = ImGui.DragFloat("Size", s.sz, 0.2, 0.2, 100.0, "%.0f")
|
||||
local t = ImGui.GetTime()
|
||||
local rounding_wave = 1.0 - math.abs((t % 3.0) * (2.0 / 3.0) - 1.0)
|
||||
local rounding = s.animate_rounding and (math.floor(rounding_wave * s.base_rounding * 10.0) / 10.0) or s.base_rounding
|
||||
s.base_rounding = ImGui.DragFloat("Rounding", s.base_rounding, 0.02, 0.0, 32.0, "%.1f"); ImGui.SameLine()
|
||||
_, s.animate_rounding = ImGui.Checkbox("Animate##rounding", s.animate_rounding); ImGui.SameLine()
|
||||
ImGui.Text("%.2f", rounding)
|
||||
local thickness_wave = 1.0 - math.abs((t % 5.0) * (2.0 / 5.0) - 1.0)
|
||||
local thickness = s.animate_thickness and (thickness_wave * s.base_thickness) or s.base_thickness
|
||||
s.base_thickness = ImGui.DragFloat("Thickness", s.base_thickness, 0.02, 0.0, 32.0, "%.02f"); ImGui.SameLine()
|
||||
_, s.animate_thickness = ImGui.Checkbox("Animate##thickness", s.animate_thickness); ImGui.SameLine()
|
||||
ImGui.Text("%.2f", thickness)
|
||||
s.ngon_segments = ImGui.SliderInt("N-gon sides", s.ngon_segments, 3, 12)
|
||||
local changed
|
||||
_, s.circle_override = ImGui.Checkbox("##CircleSegmentOverride", s.circle_override)
|
||||
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
|
||||
s.circle_override_v, changed = ImGui.SliderInt("Circle segments override", s.circle_override_v, 3, 30)
|
||||
s.circle_override = s.circle_override or changed
|
||||
_, s.curve_override = ImGui.Checkbox("##CurvesSegmentOverride", s.curve_override)
|
||||
ImGui.SameLine(0.0, ImGui.GetStyle().ItemInnerSpacing.x)
|
||||
s.curve_override_v, changed = ImGui.SliderInt("Curves segments override", s.curve_override_v, 3, 30)
|
||||
s.curve_override = s.curve_override or changed
|
||||
ImGui.ColorEdit4("Stroke Color", s.colf_stroke)
|
||||
ImGui.ColorEdit4("Fill Color", s.colf_fill)
|
||||
ImGui.SeparatorText("Per primitive flags (AddXXX functions)")
|
||||
ImGui.TextDisabled("(stroke placement/AA flags: not ported, this port uses the 1.92 stroker)")
|
||||
ImGui.Spacing()
|
||||
|
||||
local sz = s.sz
|
||||
local start_pos = ImGui.GetCursorScreenPos()
|
||||
local pi = 3.141592
|
||||
local step = sz + 10.0
|
||||
local corners_tl_br = bit32.bor(ImDrawFlags.RoundCornersTopLeft, ImDrawFlags.RoundCornersBottomRight)
|
||||
local half_sz = sz * 0.5
|
||||
local circle_segments = s.circle_override and s.circle_override_v or 0
|
||||
local curve_segments = s.curve_override and s.curve_override_v or 0
|
||||
local cp3 = { ImVec2(0.0, sz * 0.6), ImVec2(sz * 0.5, -sz * 0.4), ImVec2(sz, sz) }
|
||||
local cp4 = { ImVec2(0.0, 0.0), ImVec2(sz * 1.3, sz * 0.3), ImVec2(sz - sz * 1.3, sz - sz * 0.3), ImVec2(sz, sz) }
|
||||
local concave_shape = { { 0.0, 0.0 }, { 0.3, 0.0 }, { 0.3, 0.7 }, { 0.7, 0.7 }, { 0.7, 0.0 }, { 1.0, 0.0 }, { 1.0, 1.0 }, { 0.0, 1.0 } }
|
||||
local zigzag_shape = { { 0.0, 0.0 }, { 0.9, 0.0 }, { 1.0, 0.1 }, { 1.0, 0.9 }, { 0.9, 1.0 }, { 0.3, 1.0 }, { 0.3, 0.4 }, { 0.9, 0.4 } }
|
||||
local rotating_square = {}
|
||||
for side = 0, 3 do
|
||||
local a = t * 0.1 + side * pi * 0.5
|
||||
rotating_square[side + 1] = ImVec2(math.cos(a) * half_sz, math.sin(a) * half_sz)
|
||||
end
|
||||
local function Shape(shape, x, y)
|
||||
for _, p in ipairs(shape) do draw_list:PathLineTo(ImVec2(x + math.floor(sz * p[1]), y + math.floor(sz * p[2]))) end
|
||||
end
|
||||
|
||||
local y = start_pos.y
|
||||
for row = 0, 3 do
|
||||
local x = start_pos.x
|
||||
local draw_fill = (row == 2 or row == 3)
|
||||
local draw_strokes = (row == 0 or row == 1 or row == 3)
|
||||
if draw_fill then
|
||||
local col = ImGui.ColorConvertFloat4ToU32(s.colf_fill)
|
||||
draw_list:AddNgonFilled(ImVec2(x + half_sz, y + half_sz), half_sz, col, s.ngon_segments); x = x + step
|
||||
draw_list:AddCircleFilled(ImVec2(x + half_sz, y + half_sz), half_sz, col, circle_segments); x = x + step
|
||||
draw_list:AddEllipseFilled(ImVec2(x + half_sz, y + half_sz), ImVec2(half_sz, sz * 0.3), col, -0.3, circle_segments); x = x + step
|
||||
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x = x + step
|
||||
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding); x = x + step
|
||||
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br); x = x + step
|
||||
draw_list:AddTriangleFilled(ImVec2(x + sz * 0.5, y), ImVec2(x + sz, y + sz), ImVec2(x, y + sz), col); x = x + step
|
||||
draw_list:AddTriangleFilled(ImVec2(x + sz * 0.2, y), ImVec2(x + sz * 0.4, y + sz), ImVec2(x, y + sz), col); x = x + step - math.floor(sz * 0.6)
|
||||
Shape(concave_shape, x, y); draw_list:PathFillConcave(col); x = x + step
|
||||
Shape(zigzag_shape, x, y); draw_list:PathFillConcave(col); x = x + step
|
||||
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x = x + step
|
||||
draw_list:AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x = x + step - math.floor(half_sz)
|
||||
if not (draw_fill and draw_strokes) then
|
||||
local block_sz, off = 1, 0
|
||||
while block_sz < 16 and off + block_sz <= sz do
|
||||
draw_list:AddRectFilled(ImVec2(x, y + off), ImVec2(x + block_sz, y + off + block_sz), col)
|
||||
off = off + block_sz + 1
|
||||
block_sz = block_sz + 1
|
||||
end
|
||||
end
|
||||
x = x + step - math.floor(half_sz)
|
||||
for n = 1, 4 do draw_list:PathLineTo(ImVec2(x + half_sz + rotating_square[n].x, y + half_sz + rotating_square[n].y)) end
|
||||
draw_list:PathFillConvex(col); x = x + step
|
||||
draw_list:PathArcTo(ImVec2(x + half_sz, y + half_sz), half_sz, pi * -0.5, pi * 1.1)
|
||||
draw_list:PathFillConvex(col); x = x + step
|
||||
draw_list:PathLineTo(ImVec2(x + cp3[1].x, y + cp3[1].y))
|
||||
draw_list:PathBezierQuadraticCurveTo(ImVec2(x + cp3[2].x, y + cp3[2].y), ImVec2(x + cp3[3].x, y + cp3[3].y), curve_segments)
|
||||
draw_list:PathFillConvex(col); x = x + step
|
||||
if not (draw_fill and draw_strokes) then
|
||||
draw_list:AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255))
|
||||
x = x + step
|
||||
end
|
||||
end
|
||||
if draw_fill and draw_strokes then x = start_pos.x end
|
||||
if draw_strokes then
|
||||
local col = ImGui.ColorConvertFloat4ToU32(s.colf_stroke)
|
||||
local th = (row == 0) and 1.0 or thickness
|
||||
draw_list:AddNgon(ImVec2(x + half_sz, y + half_sz), half_sz, col, s.ngon_segments, th); x = x + step
|
||||
draw_list:AddCircle(ImVec2(x + half_sz, y + half_sz), half_sz, col, circle_segments, th); x = x + step
|
||||
draw_list:AddEllipse(ImVec2(x + half_sz, y + half_sz), ImVec2(half_sz, sz * 0.3), col, -0.3, circle_segments, th); x = x + step
|
||||
draw_list:AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0, th); x = x + step
|
||||
draw_list:AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th); x = x + step
|
||||
draw_list:AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th, corners_tl_br); x = x + step
|
||||
draw_list:AddTriangle(ImVec2(x + sz * 0.5, y), ImVec2(x + sz, y + sz), ImVec2(x, y + sz), col, th); x = x + step
|
||||
draw_list:AddTriangle(ImVec2(x + sz * 0.2, y), ImVec2(x + sz * 0.4, y + sz), ImVec2(x, y + sz), col, th); x = x + step - math.floor(sz * 0.6)
|
||||
Shape(concave_shape, x, y); draw_list:PathStroke(col, th, ImDrawFlags.Closed); x = x + step
|
||||
Shape(zigzag_shape, x, y); draw_list:PathStroke(col, th, ImDrawFlags.Closed); x = x + step
|
||||
local off = math.floor(sz * 0.4)
|
||||
draw_list:AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th)
|
||||
draw_list:AddLine(ImVec2(x, y + off), ImVec2(x + sz, y + off), col, th); x = x + step
|
||||
draw_list:AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th)
|
||||
draw_list:AddLine(ImVec2(x + off, y + sz), ImVec2(x + off, y), col, th); x = x + step - math.floor(half_sz)
|
||||
if not (draw_fill and draw_strokes) then
|
||||
draw_list:AddLine(ImVec2(x, y), ImVec2(x + half_sz, y + sz), col, th)
|
||||
draw_list:AddLine(ImVec2(x, y + sz), ImVec2(x + half_sz, y), col, th)
|
||||
end
|
||||
x = x + step - math.floor(half_sz)
|
||||
for n = 1, 4 do draw_list:PathLineTo(ImVec2(x + half_sz + rotating_square[n].x, y + half_sz + rotating_square[n].y)) end
|
||||
draw_list:PathStroke(col, th, ImDrawFlags.Closed); x = x + step
|
||||
draw_list:PathArcTo(ImVec2(x + half_sz, y + half_sz), half_sz, pi * -0.5, pi * 1.1)
|
||||
draw_list:PathStroke(col, th); x = x + step
|
||||
draw_list:AddBezierQuadratic(ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), ImVec2(x + cp3[3].x, y + cp3[3].y), col, th, curve_segments); x = x + step
|
||||
if not (draw_fill and draw_strokes) then
|
||||
draw_list:AddBezierCubic(ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), ImVec2(x + cp4[4].x, y + cp4[4].y), col, th, curve_segments)
|
||||
x = x + step
|
||||
end
|
||||
end
|
||||
y = y + step
|
||||
end
|
||||
ImGui.Dummy(ImVec2(step * 15.5, step * 4.0))
|
||||
ImGui.Text("ImDrawList Vector Rendering Reference:")
|
||||
ImGui.TextLinkOpenURL("https://github.com/ocornut/imgui/wiki/Draw-List")
|
||||
ImGui.PopItemFlag()
|
||||
ImGui.PopItemWidth()
|
||||
end
|
||||
|
||||
local function CustomRenderingCanvas()
|
||||
local s = CRS
|
||||
_, s.opt_enable_grid = ImGui.Checkbox("Enable grid", s.opt_enable_grid)
|
||||
_, s.opt_enable_context_menu = ImGui.Checkbox("Enable context menu", s.opt_enable_context_menu)
|
||||
ImGui.Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.")
|
||||
local canvas_p0 = ImGui.GetCursorScreenPos()
|
||||
local canvas_sz = ImGui.GetContentRegionAvail()
|
||||
if canvas_sz.x < 50.0 then canvas_sz.x = 50.0 end
|
||||
if canvas_sz.y < 50.0 then canvas_sz.y = 50.0 end
|
||||
local canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y)
|
||||
local io = ImGui.GetIO()
|
||||
local draw_list = ImGui.GetWindowDrawList()
|
||||
draw_list:AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255))
|
||||
draw_list:AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255))
|
||||
|
||||
ImGui.InvisibleButton("canvas", canvas_sz, bit32.bor(ImGuiButtonFlags.MouseButtonLeft, ImGuiButtonFlags.MouseButtonRight))
|
||||
local is_hovered = ImGui.IsItemHovered()
|
||||
local is_active = ImGui.IsItemActive()
|
||||
local origin = ImVec2(canvas_p0.x + s.scrolling.x, canvas_p0.y + s.scrolling.y)
|
||||
local mouse_pos_in_canvas = ImVec2(io.MousePos.x - origin.x, io.MousePos.y - origin.y)
|
||||
local points = s.points
|
||||
if is_hovered and not s.adding_line and ImGui.IsMouseClicked(ImGuiMouseButton.Left) then
|
||||
points[#points + 1] = ImVec2(mouse_pos_in_canvas.x, mouse_pos_in_canvas.y)
|
||||
points[#points + 1] = ImVec2(mouse_pos_in_canvas.x, mouse_pos_in_canvas.y)
|
||||
s.adding_line = true
|
||||
end
|
||||
if s.adding_line then
|
||||
points[#points] = ImVec2(mouse_pos_in_canvas.x, mouse_pos_in_canvas.y)
|
||||
if not ImGui.IsMouseDown(ImGuiMouseButton.Left) then s.adding_line = false end
|
||||
end
|
||||
local mouse_threshold_for_pan = s.opt_enable_context_menu and -1.0 or 0.0
|
||||
if is_active and ImGui.IsMouseDragging(ImGuiMouseButton.Right, mouse_threshold_for_pan) then
|
||||
s.scrolling.x = s.scrolling.x + io.MouseDelta.x
|
||||
s.scrolling.y = s.scrolling.y + io.MouseDelta.y
|
||||
end
|
||||
local drag_delta = ImGui.GetMouseDragDelta(ImGuiMouseButton.Right)
|
||||
if s.opt_enable_context_menu and drag_delta.x == 0.0 and drag_delta.y == 0.0 then
|
||||
ImGui.OpenPopupOnItemClick("context", ImGuiPopupFlags.MouseButtonRight)
|
||||
end
|
||||
if ImGui.BeginPopup("context") then
|
||||
if s.adding_line then points[#points] = nil; points[#points] = nil end
|
||||
s.adding_line = false
|
||||
if ImGui.MenuItem("Remove one", nil, false, #points > 0) then points[#points] = nil; points[#points] = nil end
|
||||
if ImGui.MenuItem("Remove all", nil, false, #points > 0) then table.clear(points) end
|
||||
ImGui.EndPopup()
|
||||
end
|
||||
draw_list:PushClipRect(canvas_p0, canvas_p1, true)
|
||||
if s.opt_enable_grid then
|
||||
local GRID_STEP = 64.0
|
||||
local x = math.fmod(s.scrolling.x, GRID_STEP)
|
||||
while x < canvas_sz.x do draw_list:AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40)); x = x + GRID_STEP end
|
||||
local y = math.fmod(s.scrolling.y, GRID_STEP)
|
||||
while y < canvas_sz.y do draw_list:AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40)); y = y + GRID_STEP end
|
||||
end
|
||||
for n = 1, #points - 1, 2 do
|
||||
draw_list:AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0)
|
||||
end
|
||||
draw_list:PopClipRect()
|
||||
end
|
||||
|
||||
function ShowExampleAppCustomRendering(p_open)
|
||||
local visible
|
||||
p_open, visible = ImGui.Begin("Example: Custom rendering", p_open)
|
||||
if not visible then ImGui.End(); return p_open end
|
||||
local s = CRS
|
||||
if ImGui.BeginTabBar("##TabBar") then
|
||||
if ImGui.BeginTabItem("Primitives") then CustomRenderingPrimitives(); ImGui.EndTabItem() end
|
||||
if ImGui.BeginTabItem("Canvas") then CustomRenderingCanvas(); ImGui.EndTabItem() end
|
||||
if ImGui.BeginTabItem("BG/FG draw lists") then
|
||||
_, s.draw_bg = ImGui.Checkbox("Draw in Background draw list", s.draw_bg)
|
||||
ImGui.SameLine(); HelpMarker("The Background draw list will be rendered below every Dear ImGui windows.")
|
||||
_, s.draw_fg = ImGui.Checkbox("Draw in Foreground draw list", s.draw_fg)
|
||||
ImGui.SameLine(); HelpMarker("The Foreground draw list will be rendered over every Dear ImGui windows.")
|
||||
local window_pos, window_size = ImGui.GetWindowPos(), ImGui.GetWindowSize()
|
||||
local window_center = ImVec2(window_pos.x + window_size.x * 0.5, window_pos.y + window_size.y * 0.5)
|
||||
if s.draw_bg then ImGui.GetBackgroundDrawList():AddCircle(window_center, window_size.x * 0.6, IM_COL32(255, 0, 0, 200), 0, 10 + 4) end
|
||||
if s.draw_fg then ImGui.GetForegroundDrawList():AddCircle(window_center, window_size.y * 0.6, IM_COL32(0, 255, 0, 200), 0, 10) end
|
||||
ImGui.EndTabItem()
|
||||
end
|
||||
if ImGui.BeginTabItem("Draw Channels") then
|
||||
local draw_list = ImGui.GetWindowDrawList()
|
||||
ImGui.Text("Blue shape is drawn first: appears in back")
|
||||
ImGui.Text("Red shape is drawn after: appears in front")
|
||||
local p0 = ImGui.GetCursorScreenPos()
|
||||
draw_list:AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255))
|
||||
draw_list:AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255))
|
||||
ImGui.Dummy(ImVec2(75, 75))
|
||||
ImGui.Separator()
|
||||
ImGui.Text("Blue shape is drawn first, into channel 1: appears in front")
|
||||
ImGui.Text("Red shape is drawn after, into channel 0: appears in back")
|
||||
local p1 = ImGui.GetCursorScreenPos()
|
||||
draw_list:ChannelsSplit(2)
|
||||
draw_list:ChannelsSetCurrent(1)
|
||||
draw_list:AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255))
|
||||
draw_list:ChannelsSetCurrent(0)
|
||||
draw_list:AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255))
|
||||
draw_list:ChannelsMerge()
|
||||
ImGui.Dummy(ImVec2(75, 75))
|
||||
ImGui.Text("After reordering, contents of channel 0 appears below channel 1.")
|
||||
ImGui.EndTabItem()
|
||||
end
|
||||
ImGui.EndTabBar()
|
||||
end
|
||||
ImGui.End()
|
||||
return p_open
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user