Port ImTriangulator + upstream AddConcavePolyFilled (AA fringe, no per-triangle seams); [Luau] handles CCW winding and duplicate/collinear points that upstream mis-triangulates

This commit is contained in:
4DBug
2026-10-15 07:43:21 -05:00
parent 93a3c4faf5
commit f2c694bac5
7 changed files with 1164 additions and 242 deletions
+344 -36
View File
@@ -138,7 +138,7 @@ if not utf8 then
end
local ImGui, ImVec2, ImVec4, ImRect, HelpMarker, ImVec2_Copy, IMGUI_DEMO_MARKER, ImGuiTableFlags, ImStd, ImVector, ImGuiKey, ImDrawFlags, ImGuiInputFlags, ImRect_Copy, ImGuiDataType, ImStb, IM_TRUNC, ImVec2_CopyV, ImGuiStyleVarInfo, IM_ASSERT_USER_ERROR, ImGuiHoveredFlags, ImFormatString, IM_ASSERT_USER_ERROR_RET, ImHashStr, ImS32, ImGuiColorEditFlags, ImIsPowerOfTwo, ImAbs, ImVec4_Copy, ExampleAppConsoleMT, IM_ROUND, ImGuiTableColumnFlags, ImGuiTreeNodeFlags, PopStyleCompact, PushStyleCompact, IM_ASSERT_PARANOID, ImU16, ExampleNames, ImGuiWindowFlags, ImGuiItemFlags, stbte, ImColor, ImCos, ImGuiButtonFlags, ImGuiDebugLogFlags, ImGuiLocKey, ImU32, ExampleTreeFuncs, ImGuiListClipper, ImS16, _PathArcToFastExXY, ImGuiMultiSelectFlags, ImSin, ImGuiTabBarFlags, ImUpperPowerOfTwo, IM_F32_TO_INT8_SAT, ImFontAtlasPackGetRect, ImGuiMouseCursor, DockSettingsRenameNodeReferences, ExampleDualListBoxMT, IM_DRAWLIST_TEX_LINES_WIDTH_MAX, ImCeil, ImGuiCol, ImGuiSelectionBasicStorage, ImLengthSqr, ImS8, ImU8, ExampleAppPropertyEditorMT, ImFabs, ImGuiScrollFlags, ImGui_ImplLOVE_GetBackendData, ImLog, ExampleSelectionWithDeletionMT, CustomConstraints, ExampleAppLogMT, ImFontAtlasBuildInit, ImGuiComboFlags, ImGuiPopupFlags, ImGuiSliderFlags, ImTextClassifierSetCharClass, MyDocument, ShowExampleMenuFile, CalcCornerArcStepAndCount, ImFontAtlasBakedAddFontGlyph, ImFontAtlasPackAddRect, ImFontBaked_BuildLoadGlyph, ImGuiInputTextFlags, ImGuiStyle_Copy, ExampleAsset_s_current_sort_specs, ExampleMemberInfo, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC, ImFontAtlasBuildRenderBitmapFromString, ImFontAtlasFontDestroyOutput, ImFontAtlasFontSourceAddToFont, ImFontAtlasFontSourceInit, ImFontAtlasTextureAdd, ImFontAtlasTextureBlockQueueUpload, ImFontBaked_BuildGrowIndex, ImGuiTableColumn, stb_textedit_key, stbtt_GetGlyphBitmapBoxSubpixel, IM_FREE, ImDrawListSplitter, ImGuiDragDropFlags, ImGuiFocusedFlags, stb_text_locate_coord, stb_text_makeundo_insert, stb_text_makeundo_replace, stb_textedit_find_charpos, stbtt_GetGlyphShape, IM_ALLOC, ImGuiTableColumnSortSpecs, ImGuiTableInstanceData, ImGuiTableSettings, stb_text_makeundo_delete, stb_text_redo, stb_text_undo, stb_textedit_click, stb_textedit_drag, stbtt_FindGlyphIndex, stbtt_GetFontOffsetForIndex, stbtt_GetFontVMetrics, stbtt_GetGlyphBitmapBox, stbtt_GetGlyphHMetrics, stbtt_InitFont, stbtt_MakeGlyphBitmapSubpixelPrefilter, stbtt_ScaleForPixelHeight, stbtt__GetGlyphInfoT2, stbtt__GetGlyphShapeT2, stbtt_fontinfo, ImGuiOldColumnData, ImGuiOldColumnFlags, ImGuiOldColumns, ImGuiTable, ImGuiTableColumnSettings, ImGuiTableSortSpecs, ImGuiTableTempData, ImGui_ImplLOVE_CreateTexture, ImGui_ImplLOVE_DestroyTexture, ImGui_ImplLOVE_Init, ImGui_ImplLOVE_NewFrame, ImGui_ImplLOVE_RenderDrawData, ImGui_ImplLOVE_Shutdown, ImGui_ImplLOVE_UpdateTexture, stbrp_init_target, IMGUI_TABLE_MAX_COLUMNS, IMSTB_TEXTEDIT_UNDOSTATECOUNT, IM_COL32_DISABLE, ImGui_ImplLove, STB_TEXTEDIT_K_BACKSPACE, STB_TEXTEDIT_K_DELETE, STB_TEXTEDIT_K_DOWN, STB_TEXTEDIT_K_LEFT, STB_TEXTEDIT_K_LINEEND, STB_TEXTEDIT_K_LINESTART, STB_TEXTEDIT_K_PGDOWN, STB_TEXTEDIT_K_PGUP, STB_TEXTEDIT_K_REDO, STB_TEXTEDIT_K_RIGHT, STB_TEXTEDIT_K_SHIFT
local ImGui, ImVec2, ImVec4, ImRect, HelpMarker, ImVec2_Copy, IMGUI_DEMO_MARKER, ImGuiTableFlags, ImStd, ImVector, ImGuiKey, ImDrawFlags, ImGuiInputFlags, ImRect_Copy, ImGuiDataType, ImStb, IM_TRUNC, ImVec2_CopyV, ImGuiStyleVarInfo, IM_ASSERT_USER_ERROR, ImGuiHoveredFlags, ImFormatString, IM_ASSERT_USER_ERROR_RET, ImHashStr, ImS32, ImGuiColorEditFlags, ImIsPowerOfTwo, ImAbs, ImVec4_Copy, ExampleAppConsoleMT, IM_ROUND, ImGuiTableColumnFlags, ImGuiTreeNodeFlags, PopStyleCompact, PushStyleCompact, IM_ASSERT_PARANOID, ImU16, ExampleNames, ImGuiWindowFlags, ImGuiItemFlags, stbte, ImColor, ImCos, ImGuiButtonFlags, ImGuiDebugLogFlags, ImGuiLocKey, ImU32, ExampleTreeFuncs, ImGuiListClipper, ImS16, _PathArcToFastExXY, ImGuiMultiSelectFlags, ImSin, ImGuiTabBarFlags, ImUpperPowerOfTwo, IM_F32_TO_INT8_SAT, ImFontAtlasPackGetRect, ImGuiMouseCursor, DockSettingsRenameNodeReferences, ExampleDualListBoxMT, IM_DRAWLIST_TEX_LINES_WIDTH_MAX, ImCeil, ImFabs, ImGuiCol, ImGuiSelectionBasicStorage, ImLengthSqr, ImS8, ImU8, ExampleAppPropertyEditorMT, ImGuiScrollFlags, ImGui_ImplLOVE_GetBackendData, ImLog, ExampleSelectionWithDeletionMT, CustomConstraints, ExampleAppLogMT, ImFontAtlasBuildInit, ImGuiComboFlags, ImGuiPopupFlags, ImGuiSliderFlags, ImTextClassifierSetCharClass, MyDocument, ShowExampleMenuFile, CalcCornerArcStepAndCount, ImFontAtlasBakedAddFontGlyph, ImFontAtlasPackAddRect, ImFontBaked_BuildLoadGlyph, ImGuiInputTextFlags, ImGuiStyle_Copy, ExampleAsset_s_current_sort_specs, ExampleMemberInfo, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC, ImFontAtlasBuildRenderBitmapFromString, ImFontAtlasFontDestroyOutput, ImFontAtlasFontSourceAddToFont, ImFontAtlasFontSourceInit, ImFontAtlasTextureAdd, ImFontAtlasTextureBlockQueueUpload, ImFontBaked_BuildGrowIndex, ImGuiTableColumn, stb_textedit_key, stbtt_GetGlyphBitmapBoxSubpixel, IM_FREE, ImDrawListSplitter, ImGuiDragDropFlags, ImGuiFocusedFlags, stb_text_locate_coord, stb_text_makeundo_insert, stb_text_makeundo_replace, stb_textedit_find_charpos, stbtt_GetGlyphShape, IM_ALLOC, ImGuiTableColumnSortSpecs, ImGuiTableInstanceData, ImGuiTableSettings, stb_text_makeundo_delete, stb_text_redo, stb_text_undo, stb_textedit_click, stb_textedit_drag, stbtt_FindGlyphIndex, stbtt_GetFontOffsetForIndex, stbtt_GetFontVMetrics, stbtt_GetGlyphBitmapBox, stbtt_GetGlyphHMetrics, stbtt_InitFont, stbtt_MakeGlyphBitmapSubpixelPrefilter, stbtt_ScaleForPixelHeight, stbtt__GetGlyphInfoT2, stbtt__GetGlyphShapeT2, stbtt_fontinfo, ImGuiOldColumnData, ImGuiOldColumnFlags, ImGuiOldColumns, ImGuiTable, ImGuiTableColumnSettings, ImGuiTableSortSpecs, ImGuiTableTempData, ImGui_ImplLOVE_CreateTexture, ImGui_ImplLOVE_DestroyTexture, ImGui_ImplLOVE_Init, ImGui_ImplLOVE_NewFrame, ImGui_ImplLOVE_RenderDrawData, ImGui_ImplLOVE_Shutdown, ImGui_ImplLOVE_UpdateTexture, stbrp_init_target, IMGUI_TABLE_MAX_COLUMNS, IMSTB_TEXTEDIT_UNDOSTATECOUNT, IM_COL32_DISABLE, ImGui_ImplLove, STB_TEXTEDIT_K_BACKSPACE, STB_TEXTEDIT_K_DELETE, STB_TEXTEDIT_K_DOWN, STB_TEXTEDIT_K_LEFT, STB_TEXTEDIT_K_LINEEND, STB_TEXTEDIT_K_LINESTART, STB_TEXTEDIT_K_PGDOWN, STB_TEXTEDIT_K_PGUP, STB_TEXTEDIT_K_REDO, STB_TEXTEDIT_K_RIGHT, STB_TEXTEDIT_K_SHIFT
local type, mathMin, mathMax, mathFloor, mathCeil, bitOr, bitLShift = type, math.min, math.max, math.floor, math.ceil, bit32.bor, bit32.lshift
local function IM_COL32(R, G, B, A) return (bitOr(bitLShift(A, 24 --[[IM_COL32_A_SHIFT]]), bitLShift(B, 16 --[[IM_COL32_B_SHIFT]]), bitLShift(G, 8 --[[IM_COL32_G_SHIFT]]), bitLShift(R, 0 --[[IM_COL32_R_SHIFT]]))) end
@@ -40909,47 +40909,341 @@ function MT.ImDrawList:PathBezierQuadraticCurveTo(p2, p3, num_segments)
end
end
-- ponytail: ear-clipping, no anti-aliased fringe (upstream's AA triangulator not ported). O(n^2) is fine for UI polygons.
--------------------------------------------------------------------------------
-- [SECTION] ImTriangulator, ImDrawList concave polygon fill
--------------------------------------------------------------------------------
-- Triangulate concave polygons. Based on "Triangulation by Ear Clipping" paper, O(N^2) complexity.
-- Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
-- Provided as a convenience for user but not used by main library.
--------------------------------------------------------------------------------
-- - ImTriangulator [Internal]
-- - AddConcavePolyFilled()
--------------------------------------------------------------------------------
local ImTriangulatorNodeType =
{
Convex = 0,
Ear = 1,
Reflex = 2,
}
-- [Luau] No pointers: a node is an int handle (1-based slot) into the parallel arrays of its ImTriangulator
-- (tri.Type[n], tri.Index[n], tri.Pos[n], tri.Next[n], tri.Prev[n]). node->Index (0-based, as upstream) == n - 1.
local function ImTriangulatorNode_Unlink(tri, n) tri.Prev[tri.Next[n]] = tri.Prev[n]; tri.Next[tri.Prev[n]] = tri.Next[n] end
-- [Luau] Deviation: upstream classifies a node as reflex when '!ImTriangleIsClockwise()', i.e. also when its corner is
-- degenerate (collinear or duplicate points). A duplicate point then sits as a reflex on top of every candidate ear's vertex
-- and blocks it, ending in the degenerate fallback with overlapping triangles. Only strictly counter-clockwise corners are
-- reflex here: degenerate corners are clipped as zero-area ears.
local function ImTriangulator_IsReflex(a, b, c) return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) < 0.0 end
local function ImTriangulatorNodeSpan()
return { Data = {}, Size = 0 } -- Data: node handles, 1-based
end
MT.ImTriangulatorNodeSpan = {}
MT.ImTriangulatorNodeSpan.__index = MT.ImTriangulatorNodeSpan
function MT.ImTriangulatorNodeSpan:push_back(node) self.Size = self.Size + 1; self.Data[self.Size] = node end
function MT.ImTriangulatorNodeSpan:find_erase_unsorted(idx) for i = self.Size, 1, -1 do if self.Data[i] - 1 == idx then self.Data[i] = self.Data[self.Size]; self.Size = self.Size - 1; return end end end
-- [Luau] The node/ears/reflexes storage (upstream: carved out of _Data->TempBuffer via scratch_buffer) is owned by the
-- triangulator itself and only ever grows. AddConcavePolyFilled() reuses a single module-level instance (no per-call allocations).
local function ImTriangulator()
return setmetatable({
_TrianglesLeft = 0,
_Nodes = 0, -- node handle (head of the circular list)
_Ears = setmetatable(ImTriangulatorNodeSpan(), MT.ImTriangulatorNodeSpan),
_Reflexes = setmetatable(ImTriangulatorNodeSpan(), MT.ImTriangulatorNodeSpan),
Type = {}, Index = {}, Pos = {}, Next = {}, Prev = {}, -- [Luau] ImTriangulatorNode fields, one array each
}, MT.ImTriangulator)
end
MT.ImTriangulator = {}
MT.ImTriangulator.__index = MT.ImTriangulator
function MT.ImTriangulator.EstimateTriangleCount(points_count) return (points_count < 3) and 0 or points_count - 2 end
-- [Luau] In elements (nodes + ears + reflexes) rather than bytes. Unused by the port: arrays grow on demand.
function MT.ImTriangulator.EstimateScratchBufferSize(points_count) return points_count + points_count * 2 end
-- Distribute storage for nodes, ears and reflexes.
-- FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer
-- (this would require first building reflexes to bail to convex if empty, without even building nodes)
--- @param points ImVec2[] # 1-based
function MT.ImDrawList:AddConcavePolyFilled(points, points_count, col)
if points_count < 3 or bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then return end
local idx = {}
local area = 0.0
function MT.ImTriangulator:Init(points, points_count)
assert(points_count >= 3)
self._TrianglesLeft = MT.ImTriangulator.EstimateTriangleCount(points_count)
self._Nodes = 1
self._Ears.Size = 0
self._Reflexes.Size = 0
self:BuildNodes(points, points_count)
-- [Luau] Deviation: upstream only flips the node list when no ear is found, so a counter-clockwise *concave* polygon
-- (whose reflex corners look like ears) yields triangles outside of it. Normalize to clockwise order up-front (O(N)).
local area2 = 0.0
local p0 = points[points_count]
for i = 1, points_count do
idx[i] = i
local a, b = points[i], points[i % points_count + 1]
area = area + (a.x * b.y - b.x * a.y)
local p1 = points[i]
area2 = area2 + (p0.x * p1.y - p1.x * p0.y)
p0 = p1
end
local sign = (area >= 0) and 1 or -1
local function cross(o, a, b) return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x) end
local guard = 0
while #idx > 3 and guard < points_count * points_count do
guard = guard + 1
local n = #idx
local clipped = false
for i = 1, n do
local ia, ib, ic = idx[(i - 2) % n + 1], idx[i], idx[i % n + 1]
local a, b, c = points[ia], points[ib], points[ic]
if cross(a, b, c) * sign > 0 then
local inside = false
for j = 1, n do
local p = idx[j]
if p ~= ia and p ~= ib and p ~= ic then
local q = points[p]
if cross(a, b, q) * sign >= 0 and cross(b, c, q) * sign >= 0 and cross(c, a, q) * sign >= 0 then inside = true; break end
end
end
if not inside then
self:AddTriangleFilled(a, b, c, col)
table.remove(idx, i)
clipped = true
break
end
if area2 < 0.0 then
self:FlipNodeList()
end
self:BuildReflexes()
self:BuildEars()
end
function MT.ImTriangulator:BuildNodes(points, points_count)
local Type, Index, Pos, Next, Prev = self.Type, self.Index, self.Pos, self.Next, self.Prev
for i = 1, points_count do
Type[i] = ImTriangulatorNodeType.Convex
Index[i] = i - 1
Pos[i] = points[i] -- [Luau] reference, not a copy (points are not modified while triangulating)
Next[i] = i + 1
Prev[i] = i - 1
end
Prev[1] = points_count
Next[points_count] = 1
end
function MT.ImTriangulator:BuildReflexes()
local Pos, Next, Prev = self.Pos, self.Next, self.Prev
local n1 = self._Nodes
for _ = self._TrianglesLeft, 0, -1 do
if ImTriangulator_IsReflex(Pos[Prev[n1]], Pos[n1], Pos[Next[n1]]) then
self.Type[n1] = ImTriangulatorNodeType.Reflex
self._Reflexes:push_back(n1)
end
n1 = Next[n1]
end
end
function MT.ImTriangulator:BuildEars()
local Index, Pos, Next, Prev = self.Index, self.Pos, self.Next, self.Prev
local n1 = self._Nodes
for _ = self._TrianglesLeft, 0, -1 do
if self.Type[n1] == ImTriangulatorNodeType.Convex then
local n0, n2 = Prev[n1], Next[n1]
if self:IsEar(Index[n0], Index[n1], Index[n2], Pos[n0], Pos[n1], Pos[n2]) then
self.Type[n1] = ImTriangulatorNodeType.Ear
self._Ears:push_back(n1)
end
end
if not clipped then break end
n1 = Next[n1]
end
end
-- [Luau] out_triangle[3] is returned as 3 values (relative 0-based indexes)
function MT.ImTriangulator:GetNextTriangle()
if self._Ears.Size == 0 then
self:FlipNodeList()
local node = self._Nodes
for _ = self._TrianglesLeft, 0, -1 do
self.Type[node] = ImTriangulatorNodeType.Convex
node = self.Next[node]
end
self._Reflexes.Size = 0
self:BuildReflexes()
self:BuildEars()
-- If we still don't have ears, it means geometry is degenerated.
if self._Ears.Size == 0 then
-- Return first triangle available, mimicking the behavior of convex fill.
assert(self._TrianglesLeft > 0) -- Geometry is degenerated
self._Ears.Data[1] = self._Nodes
self._Ears.Size = 1
end
end
local ear = self._Ears.Data[self._Ears.Size]
self._Ears.Size = self._Ears.Size - 1
local Index, Next, Prev = self.Index, self.Next, self.Prev
local t0, t1, t2 = Index[Prev[ear]], Index[ear], Index[Next[ear]]
ImTriangulatorNode_Unlink(self, ear)
if ear == self._Nodes then
self._Nodes = Next[ear]
end
self:ReclassifyNode(Prev[ear])
self:ReclassifyNode(Next[ear])
self._TrianglesLeft = self._TrianglesLeft - 1
return t0, t1, t2
end
function MT.ImTriangulator:FlipNodeList()
local Next, Prev = self.Next, self.Prev
local nodes = self._Nodes
local prev = nodes
local temp = nodes
local current = Next[nodes]
Next[prev] = prev
Prev[prev] = prev
while current ~= nodes do
temp = Next[current]
Next[current] = prev
Prev[prev] = current
Next[nodes] = current
Prev[current] = nodes
prev = current
current = temp
end
self._Nodes = prev
end
-- A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm)
function MT.ImTriangulator:IsEar(i0, i1, i2, v0, v1, v2)
local Index, Pos = self.Index, self.Pos
local data = self._Reflexes.Data
for p = 1, self._Reflexes.Size do
local reflex = data[p]
local idx = Index[reflex]
if idx ~= i0 and idx ~= i1 and idx ~= i2 then
if ImStd.ImTriangleContainsPoint(v0, v1, v2, Pos[reflex]) then
return false
end
end
end
return true
end
function MT.ImTriangulator:ReclassifyNode(n1)
-- Classify node
local type
local n0 = self.Prev[n1]
local n2 = self.Next[n1]
local Index, Pos = self.Index, self.Pos
if ImTriangulator_IsReflex(Pos[n0], Pos[n1], Pos[n2]) then
type = ImTriangulatorNodeType.Reflex
elseif self:IsEar(Index[n0], Index[n1], Index[n2], Pos[n0], Pos[n1], Pos[n2]) then
type = ImTriangulatorNodeType.Ear
else
type = ImTriangulatorNodeType.Convex
end
-- Update lists when a type changes
local n1_type = self.Type[n1]
if type == n1_type then
return
end
if n1_type == ImTriangulatorNodeType.Reflex then
self._Reflexes:find_erase_unsorted(Index[n1])
elseif n1_type == ImTriangulatorNodeType.Ear then
self._Ears:find_erase_unsorted(Index[n1])
end
if type == ImTriangulatorNodeType.Reflex then
self._Reflexes:push_back(n1)
elseif type == ImTriangulatorNodeType.Ear then
self._Ears:push_back(n1)
end
self.Type[n1] = type
end
local concave_triangulator = ImTriangulator() -- [Luau] reused scratch (see ImTriangulator())
-- Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes).
-- (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer!
-- It is up to caller to ensure not making costly calls that will be outside of visible area.
-- As concave fill is noticeably more expensive than other primitives, be mindful of this...
-- Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false')
--- @param points ImVec2[] # 1-based
--- @param points_count int
--- @param col ImU32
function MT.ImDrawList:AddConcavePolyFilled(points, points_count, col)
if points_count < 3 or bit32.band(col, 4278190080 --[[IM_COL32_A_MASK]]) == 0 then
return
end
local uv = self._Data.TexUvWhitePixel
local uv_x, uv_y = uv.x, uv.y
local triangulator = concave_triangulator
if bit32.band(self.Flags, 4096 --[[ImDrawFlags.AAFill]]) ~= 0 then
-- Anti-aliased Fill
local AA_SIZE = self._FringeScale
local col_trans = bit32.band(col, bit32.bnot(4278190080 --[[IM_COL32_A_MASK]]))
local idx_count = (points_count - 2) * 3 + points_count * 6
local vtx_count = (points_count * 2)
PrimReserve(self, idx_count, vtx_count)
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr
local iw = self._IdxWritePtr
-- Add indexes for fill
local vtx_inner_idx = self._VtxCurrentIdx
local vtx_outer_idx = self._VtxCurrentIdx + 1
triangulator:Init(points, points_count)
while triangulator._TrianglesLeft > 0 do
local t0, t1, t2 = triangulator:GetNextTriangle()
ib[iw + 0] = vtx_inner_idx + t0 * 2; ib[iw + 1] = vtx_inner_idx + t1 * 2; ib[iw + 2] = vtx_inner_idx + t2 * 2
iw = iw + 3
end
-- Compute normals
-- [Luau] scratch arrays (see AddPolyline) instead of _Data->TempBuffer. Indexes below are 1-based.
local temp_normals_x, temp_normals_y = poly_normals_x, poly_normals_y
local i0 = points_count
for i1 = 1, points_count do
local p0 = points[i0]
local p1 = points[i1]
local dx, dy = IM_NORMALIZE2F_OVER_ZERO(p1.x - p0.x, p1.y - p0.y)
temp_normals_x[i0] = dy
temp_normals_y[i0] = -dx
i0 = i1
end
i0 = points_count
for i1 = 1, points_count do
-- Average normals
local dm_x, dm_y = IM_FIXNORMAL2F((temp_normals_x[i0] + temp_normals_x[i1]) * 0.5, (temp_normals_y[i0] + temp_normals_y[i1]) * 0.5)
dm_x = dm_x * AA_SIZE * 0.5
dm_y = dm_y * AA_SIZE * 0.5
-- Add vertices
local p1 = points[i1]
WV(vb, vw, p1.x - dm_x, p1.y - dm_y, uv_x, uv_y, col) -- Inner
WV(vb, vw + 1, p1.x + dm_x, p1.y + dm_y, uv_x, uv_y, col_trans) -- Outer
vw = vw + 2
-- Add indexes for fringes
local e0, e1 = (i0 - 1) * 2, (i1 - 1) * 2
ib[iw + 0] = vtx_inner_idx + e1; ib[iw + 1] = vtx_inner_idx + e0; ib[iw + 2] = vtx_outer_idx + e0
ib[iw + 3] = vtx_outer_idx + e0; ib[iw + 4] = vtx_outer_idx + e1; ib[iw + 5] = vtx_inner_idx + e1
iw = iw + 6
i0 = i1
end
self._VtxWritePtr = vw
self._IdxWritePtr = iw
self._VtxCurrentIdx = self._VtxCurrentIdx + vtx_count
else
-- Non Anti-aliased Fill
local idx_count = (points_count - 2) * 3
local vtx_count = points_count
PrimReserve(self, idx_count, vtx_count)
local vb = self.VtxBuffer.Buf
local ib = self.IdxBuffer.Data
local vw = self._VtxWritePtr
local iw = self._IdxWritePtr
for i = 1, vtx_count do
local p = points[i]
WV(vb, vw, p.x, p.y, uv_x, uv_y, col)
vw = vw + 1
end
local base_idx = self._VtxCurrentIdx
triangulator:Init(points, points_count)
while triangulator._TrianglesLeft > 0 do
local t0, t1, t2 = triangulator:GetNextTriangle()
ib[iw + 0] = base_idx + t0; ib[iw + 1] = base_idx + t1; ib[iw + 2] = base_idx + t2
iw = iw + 3
end
self._VtxWritePtr = vw
self._IdxWritePtr = iw
self._VtxCurrentIdx = base_idx + vtx_count
end
if #idx == 3 then self:AddTriangleFilled(points[idx[1]], points[idx[2]], points[idx[3]], col) end
end
function MT.ImDrawList:PathFillConcave(col)
@@ -44870,6 +45164,18 @@ function ImMul(lhs, rhs)
return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y)
end
--- @param a ImVec2
--- @param b ImVec2
--- @param c ImVec2
--- @return float
function ImTriangleArea(a, b, c) return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5 end
--- @param a ImVec2
--- @param b ImVec2
--- @param c ImVec2
--- @return boolean
function ImTriangleIsClockwise(a, b, c) return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) > 0.0 end
--- @param str char[]
--- @param begin? int
function ImStd.ImStrlen(str, begin)
@@ -70856,6 +71162,8 @@ return {
ImTextureRef = ImTextureRef,
ImTextureStatus = ImTextureStatus,
ImToUpper = ImToUpper,
ImTriangleArea = ImTriangleArea,
ImTriangleIsClockwise = ImTriangleIsClockwise,
ImTrunc = ImTrunc,
ImTrunc64 = ImTrunc64,
ImU16 = ImU16,