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:
+344
-36
@@ -1,7 +1,7 @@
|
||||
--!optimize 2
|
||||
-- GENERATED by tools/bundle.py from *.lua, backends/, fonts/ - do not edit
|
||||
|
||||
local ImGui, ImVec2, ImVec4, ImRect, HelpMarker, ImVec2_Copy, IMGUI_DEMO_MARKER, ImGuiTableFlags, ImStd, ImVector, ImDrawFlags, ImGuiInputFlags, ImRect_Copy, ImGuiDataType, ImStb, IM_TRUNC, ImGuiKey, 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, ImGui_ImplRoblox_GetBackendData, stbte, ImColor, ImCos, ImGuiButtonFlags, ImGuiDebugLogFlags, ImGuiLocKey, ImU32, ExampleTreeFuncs, ImGuiListClipper, ImS16, _PathArcToFastExXY, ImGuiMultiSelectFlags, ImSin, ImGuiTabBarFlags, ImUpperPowerOfTwo, IM_F32_TO_INT8_SAT, ImFontAtlasPackGetRect, DockSettingsRenameNodeReferences, ExampleDualListBoxMT, IM_DRAWLIST_TEX_LINES_WIDTH_MAX, ImCeil, ImGuiCol, ImGuiSelectionBasicStorage, ImLengthSqr, ImS8, ImU8, ExampleAppPropertyEditorMT, ImFabs, ImGuiChildFlags, ImGuiLastItemData_Copy, ImGuiScrollFlags, ImLog, ImMemchr, ImStrByte, ExampleSelectionWithDeletionMT, IMGUI_DEBUG_LOG_VIEWPORT, ImGuiInputEvent, ImGuiTextBuffer, ImPow, ImTrunc64, CustomConstraints, ExampleAppLogMT, IMGUI_DEBUG_LOG_FONT, IMGUI_DEBUG_LOG_SELECTION, ImAddClampOverflow, ImFontAtlasBuildInit, ImFontCalcWordWrapPositionEx, ImGuiComboFlags, ImGuiLastItemData, ImGuiPayload_IsDataType, ImGuiPopupFlags, ImGuiSliderFlags, ImGuiTextFilter, ImRotate, ImSqrt, ImSubClampOverflow, ImTextClassifierSetCharClass, MyDocument, ShowExampleMenuFile, CalcCornerArcStepAndCount, CalcNextScrollFromScrollTargetAndClamp, IMGUI_PAYLOAD_TYPE_WINDOW, IMGUI_VERSION, ImCharIsBlankW, ImCharIsSeparatorW, ImFmod, ImFontAtlasBakedAddFontGlyph, ImFontAtlasPackAddRect, ImFontBaked_BuildLoadGlyph, ImGuiInputTextFlags, ImGuiNavMoveFlags, ImGuiStyle_Copy, ImHashData, ImVec1_Copy, ExampleAsset_s_current_sort_specs, ExampleMemberInfo, ExampleTree_CreateNode, IMGUI_PAYLOAD_TYPE_COLOR_4F, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC, IM_F32_TO_INT8_UNBOUND, IM_PI, ImCharIsBlankA, ImFontAtlasBuildRenderBitmapFromString, ImFontAtlasFontDestroyOutput, ImFontAtlasFontDiscardBakes, ImFontAtlasFontSourceAddToFont, ImFontAtlasFontSourceInit, ImFontAtlasRectId_GetIndex, ImFontAtlasTextureAdd, ImFontAtlasTextureBlockQueueUpload, ImFontAtlasTextureGrow, ImFontBaked_BuildGrowIndex, ImFontConfig, ImFontGlyph, ImGuiTableColumn, ImGuiWindowClass, ImGui_ImplRoblox_RenderDrawData, ImParseFormatFindStart, ImRect_CopyFromV4, ImTextureRect, ImTextureRef, MyItem_s_current_sort_specs, stb_textedit_key, stbtt_GetGlyphBitmapBoxSubpixel, DockSettingsHandler_ClearAll, EditTableSizingFlags, ExampleSelectionWithDeletion, IMGUI_DEBUG_LOG_TABLE, IMGUI_PAYLOAD_TYPE_COLOR_3F, IM_FREE, ImDrawListSplitter, ImFontAtlasBakedDiscard, ImFontAtlasBuildDestroy, ImFontAtlasBuildNotifySetFont, ImFontAtlasBuildSetTexture, ImFontAtlasBuildSetupFontBakedFallback, ImFontAtlasFontDestroySourceData, ImFontAtlasPackInit, ImFontAtlasRect
|
||||
local ImGui, ImVec2, ImVec4, ImRect, HelpMarker, ImVec2_Copy, IMGUI_DEMO_MARKER, ImGuiTableFlags, ImStd, ImVector, ImDrawFlags, ImGuiInputFlags, ImRect_Copy, ImGuiDataType, ImStb, IM_TRUNC, ImGuiKey, 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, ImGui_ImplRoblox_GetBackendData, stbte, ImColor, ImCos, ImGuiButtonFlags, ImGuiDebugLogFlags, ImGuiLocKey, ImU32, ExampleTreeFuncs, ImGuiListClipper, ImS16, _PathArcToFastExXY, ImGuiMultiSelectFlags, ImSin, ImGuiTabBarFlags, ImUpperPowerOfTwo, IM_F32_TO_INT8_SAT, ImFontAtlasPackGetRect, DockSettingsRenameNodeReferences, ExampleDualListBoxMT, IM_DRAWLIST_TEX_LINES_WIDTH_MAX, ImCeil, ImFabs, ImGuiCol, ImGuiSelectionBasicStorage, ImLengthSqr, ImS8, ImU8, ExampleAppPropertyEditorMT, ImGuiChildFlags, ImGuiLastItemData_Copy, ImGuiScrollFlags, ImLog, ImMemchr, ImStrByte, ExampleSelectionWithDeletionMT, IMGUI_DEBUG_LOG_VIEWPORT, ImGuiInputEvent, ImGuiTextBuffer, ImPow, ImTrunc64, CustomConstraints, ExampleAppLogMT, IMGUI_DEBUG_LOG_FONT, IMGUI_DEBUG_LOG_SELECTION, ImAddClampOverflow, ImFontAtlasBuildInit, ImFontCalcWordWrapPositionEx, ImGuiComboFlags, ImGuiLastItemData, ImGuiPayload_IsDataType, ImGuiPopupFlags, ImGuiSliderFlags, ImGuiTextFilter, ImRotate, ImSqrt, ImSubClampOverflow, ImTextClassifierSetCharClass, MyDocument, ShowExampleMenuFile, CalcCornerArcStepAndCount, CalcNextScrollFromScrollTargetAndClamp, IMGUI_PAYLOAD_TYPE_WINDOW, IMGUI_VERSION, ImCharIsBlankW, ImCharIsSeparatorW, ImFmod, ImFontAtlasBakedAddFontGlyph, ImFontAtlasPackAddRect, ImFontBaked_BuildLoadGlyph, ImGuiInputTextFlags, ImGuiNavMoveFlags, ImGuiStyle_Copy, ImHashData, ImVec1_Copy, ExampleAsset_s_current_sort_specs, ExampleMemberInfo, ExampleTree_CreateNode, IMGUI_PAYLOAD_TYPE_COLOR_4F, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC, IM_F32_TO_INT8_UNBOUND, IM_PI, ImCharIsBlankA, ImFontAtlasBuildRenderBitmapFromString, ImFontAtlasFontDestroyOutput, ImFontAtlasFontDiscardBakes, ImFontAtlasFontSourceAddToFont, ImFontAtlasFontSourceInit, ImFontAtlasRectId_GetIndex, ImFontAtlasTextureAdd, ImFontAtlasTextureBlockQueueUpload, ImFontAtlasTextureGrow, ImFontBaked_BuildGrowIndex, ImFontConfig, ImFontGlyph, ImGuiTableColumn, ImGuiWindowClass, ImGui_ImplRoblox_RenderDrawData, ImParseFormatFindStart, ImRect_CopyFromV4, ImTextureRect, ImTextureRef, MyItem_s_current_sort_specs, stb_textedit_key, stbtt_GetGlyphBitmapBoxSubpixel, DockSettingsHandler_ClearAll, EditTableSizingFlags, ExampleSelectionWithDeletion, IMGUI_DEBUG_LOG_TABLE, IMGUI_PAYLOAD_TYPE_COLOR_3F, IM_FREE, ImDrawListSplitter, ImFontAtlasBakedDiscard, ImFontAtlasBuildDestroy, ImFontAtlasBuildNotifySetFont, ImFontAtlasBuildSetTexture, ImFontAtlasBuildSetupFontBakedFallback, ImFontAtlasFontDestroySourceData, ImFontAtlasPackInit, ImFontAtlasRect
|
||||
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
|
||||
@native
|
||||
@@ -40772,47 +40772,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
|
||||
@native
|
||||
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)
|
||||
@@ -52244,6 +52538,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)
|
||||
@@ -78237,6 +78543,8 @@ return {
|
||||
ImTextureRef = ImTextureRef,
|
||||
ImTextureStatus = ImTextureStatus,
|
||||
ImToUpper = ImToUpper,
|
||||
ImTriangleArea = ImTriangleArea,
|
||||
ImTriangleIsClockwise = ImTriangleIsClockwise,
|
||||
ImTrunc = ImTrunc,
|
||||
ImTrunc64 = ImTrunc64,
|
||||
ImU16 = ImU16,
|
||||
|
||||
Reference in New Issue
Block a user