|
@@ -8,6 +8,7 @@ Index of this file:
|
|
|
// [SECTION] STB libraries implementation
|
|
|
// [SECTION] Style functions
|
|
|
// [SECTION] ImDrawList
|
|
|
+// [SECTION] ImDrawList concave polygon fill
|
|
|
// [SECTION] ImDrawListSplitter
|
|
|
// [SECTION] ImDrawData
|
|
|
// [SECTION] Helpers ShadeVertsXXX functions
|
|
@@ -1700,6 +1701,439 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi
|
|
|
PopTextureID();
|
|
|
}
|
|
|
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+// [SECTION] ImDrawList concave polygon fill
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+
|
|
|
+static bool ImPathIsConvex(const ImVec2& a, const ImVec2& b, const ImVec2& c)
|
|
|
+{
|
|
|
+ const float dx0 = b.x - a.x;
|
|
|
+ const float dy0 = b.y - a.y;
|
|
|
+
|
|
|
+ const float dx1 = c.x - b.x;
|
|
|
+ const float dy1 = c.y - b.y;
|
|
|
+
|
|
|
+ return dx0 * dy1 - dx1 * dy0 > 0.0f;
|
|
|
+}
|
|
|
+
|
|
|
+struct ImTriangulator
|
|
|
+{
|
|
|
+ struct Triangle { int Index[3]; };
|
|
|
+
|
|
|
+ static int EstimateTriangleCount(int points_count);
|
|
|
+ static int EstimateScratchBufferSize(int points_count);
|
|
|
+
|
|
|
+ ImTriangulator(const ImVec2* points, int points_count, void* scratch_buffer);
|
|
|
+ ImTriangulator(const ImVec2* points, int points_count, int points_stride_bytes, void* scratch_buffer);
|
|
|
+
|
|
|
+ bool HasNext() const;
|
|
|
+
|
|
|
+ Triangle Next();
|
|
|
+
|
|
|
+private:
|
|
|
+ enum Type { Convex, Ear, Reflex };
|
|
|
+
|
|
|
+ struct Node;
|
|
|
+ struct alignas(void*) Span
|
|
|
+ {
|
|
|
+ Node** Data = nullptr;
|
|
|
+ int Size = 0;
|
|
|
+
|
|
|
+ void PushBack(Node* node);
|
|
|
+ void RemoveByIndex(int index);
|
|
|
+ };
|
|
|
+
|
|
|
+ void BuildNodes();
|
|
|
+ void BuildReflexes();
|
|
|
+ void BuildEars();
|
|
|
+ void FlipNodeList();
|
|
|
+ bool IsEar(const Node* node, int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const;
|
|
|
+ Type ClassifyNode(const Node* node) const;
|
|
|
+ void ReclasifyNode(Node* node);
|
|
|
+
|
|
|
+ const ImVec2* _Points = nullptr;
|
|
|
+ int _PointsCount = 0;
|
|
|
+ int _PointsStrideBytes = 0;
|
|
|
+ int _TrianglesLeft = 0;
|
|
|
+
|
|
|
+ Node* _Nodes = nullptr;
|
|
|
+ Span _Ears;
|
|
|
+ Span _Reflexes;
|
|
|
+};
|
|
|
+
|
|
|
+struct alignas(void*) ImTriangulator::Node
|
|
|
+{
|
|
|
+ Type Type = Convex;
|
|
|
+ int Index = 0;
|
|
|
+ const ImVec2* Point = nullptr;
|
|
|
+ Node* Next = nullptr;
|
|
|
+ Node* Prev = nullptr;
|
|
|
+
|
|
|
+ void Unlink()
|
|
|
+ {
|
|
|
+ Next->Prev = Prev;
|
|
|
+ Prev->Next = Next;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+int ImTriangulator::EstimateTriangleCount(int points_count)
|
|
|
+{
|
|
|
+ if (points_count < 3)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return points_count - 2;
|
|
|
+}
|
|
|
+
|
|
|
+int ImTriangulator::EstimateScratchBufferSize(int points_count)
|
|
|
+{
|
|
|
+ return sizeof(Node*) * points_count * 2 + sizeof(Node) * points_count;
|
|
|
+}
|
|
|
+
|
|
|
+ImTriangulator::ImTriangulator(const ImVec2* points, int points_count, void* scratch_buffer)
|
|
|
+ : ImTriangulator(points, points_count, sizeof(ImVec2), scratch_buffer)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+ImTriangulator::ImTriangulator(const ImVec2* points, int points_count, int points_stride_bytes, void* scratch_buffer)
|
|
|
+ : _Points(points)
|
|
|
+ , _PointsCount(points_count)
|
|
|
+ , _PointsStrideBytes(points_stride_bytes)
|
|
|
+ , _TrianglesLeft(EstimateTriangleCount(points_count))
|
|
|
+{
|
|
|
+ IM_ASSERT(scratch_buffer != nullptr && "Must provide scratch buffer.");
|
|
|
+ IM_ASSERT(points_count >= 3);
|
|
|
+
|
|
|
+ // Disable triangulator if scratch buffer isn't provided.
|
|
|
+ if (scratch_buffer == nullptr)
|
|
|
+ {
|
|
|
+ _TrianglesLeft = 0;
|
|
|
+ points_count = 0;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Distribute storage for nodes, ears and reflexes.
|
|
|
+ _Nodes = reinterpret_cast<Node*>(scratch_buffer);
|
|
|
+ _Ears.Data = reinterpret_cast<Node**>(_Nodes + points_count);
|
|
|
+ _Reflexes.Data = _Ears.Data + points_count;
|
|
|
+
|
|
|
+ BuildNodes();
|
|
|
+ BuildReflexes();
|
|
|
+ BuildEars();
|
|
|
+}
|
|
|
+
|
|
|
+void ImTriangulator::BuildNodes()
|
|
|
+{
|
|
|
+# define IM_POINT_PTR(idx) reinterpret_cast<const ImVec2*>(reinterpret_cast<const ImU8*>(_Points) + (idx) * _PointsStrideBytes)
|
|
|
+
|
|
|
+ for (int i = 0; i < _PointsCount; ++i)
|
|
|
+ {
|
|
|
+ _Nodes[i].Type = Convex;
|
|
|
+ _Nodes[i].Index = static_cast<int>(i);
|
|
|
+ _Nodes[i].Point = IM_POINT_PTR(i);
|
|
|
+ _Nodes[i].Next = _Nodes + i + 1;
|
|
|
+ _Nodes[i].Prev = _Nodes + i - 1;
|
|
|
+ }
|
|
|
+ _Nodes[0].Prev = _Nodes + _PointsCount - 1;
|
|
|
+ _Nodes[_PointsCount - 1].Next = _Nodes;
|
|
|
+
|
|
|
+# undef IM_POINT_PTR
|
|
|
+}
|
|
|
+
|
|
|
+void ImTriangulator::BuildReflexes()
|
|
|
+{
|
|
|
+ Node* node = _Nodes;
|
|
|
+ for (int i = 0; i < _TrianglesLeft; ++i, node = node->Next)
|
|
|
+ {
|
|
|
+ const ImVec2& v0 = *node->Prev->Point;
|
|
|
+ const ImVec2& v1 = *node->Point;
|
|
|
+ const ImVec2& v2 = *node->Next->Point;
|
|
|
+
|
|
|
+ if (ImPathIsConvex(v0, v1, v2))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ node->Type = Reflex;
|
|
|
+ _Reflexes.PushBack(node);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ImTriangulator::BuildEars()
|
|
|
+{
|
|
|
+ Node* node = _Nodes;
|
|
|
+ for (int i = 0; i < _TrianglesLeft; ++i, node = node->Next)
|
|
|
+ {
|
|
|
+ if (node->Type != Convex)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ const int i0 = node->Prev->Index;
|
|
|
+ const int i1 = node->Index;
|
|
|
+ const int i2 = node->Next->Index;
|
|
|
+
|
|
|
+ const ImVec2& v0 = *node->Prev->Point;
|
|
|
+ const ImVec2& v1 = *node->Point;
|
|
|
+ const ImVec2& v2 = *node->Next->Point;
|
|
|
+
|
|
|
+ if (!IsEar(node, i0, i1, i2, v0, v1, v2))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ node->Type = Ear;
|
|
|
+ _Ears.PushBack(node);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool ImTriangulator::HasNext() const
|
|
|
+{
|
|
|
+ return _TrianglesLeft > 0;
|
|
|
+}
|
|
|
+
|
|
|
+ImTriangulator::Triangle ImTriangulator::Next()
|
|
|
+{
|
|
|
+ IM_ASSERT(_TrianglesLeft > 0 && "Do not call Next() until HasNext() return true");
|
|
|
+
|
|
|
+ if (_Ears.Size == 0)
|
|
|
+ {
|
|
|
+ FlipNodeList();
|
|
|
+
|
|
|
+ Node* node = _Nodes;
|
|
|
+ for (int i = 0; i < _TrianglesLeft; ++i, node = node->Next)
|
|
|
+ node->Type = Convex;
|
|
|
+ _Reflexes.Size = 0;
|
|
|
+ BuildReflexes();
|
|
|
+ BuildEars();
|
|
|
+
|
|
|
+ // If we still don't have ears, it means geometry is degenerated.
|
|
|
+ if (_Ears.Size == 0)
|
|
|
+ {
|
|
|
+ IM_ASSERT(_TrianglesLeft > 0 && "Geometry is degenerated");
|
|
|
+
|
|
|
+ // Return first triangle available, mimicking the behavior of convex fill.
|
|
|
+ _Ears.Data[0] = _Nodes;
|
|
|
+ _Ears.Size = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Node* ear = _Ears.Data[--_Ears.Size];
|
|
|
+
|
|
|
+ const int i0 = ear->Prev->Index;
|
|
|
+ const int i1 = ear->Index;
|
|
|
+ const int i2 = ear->Next->Index;
|
|
|
+
|
|
|
+ ear->Unlink();
|
|
|
+ if (ear == _Nodes)
|
|
|
+ _Nodes = ear->Next;
|
|
|
+
|
|
|
+ ReclasifyNode(ear->Prev);
|
|
|
+ ReclasifyNode(ear->Next);
|
|
|
+
|
|
|
+ --_TrianglesLeft;
|
|
|
+
|
|
|
+ return Triangle{ { i0, i1, i2 } };
|
|
|
+}
|
|
|
+
|
|
|
+void ImTriangulator::Span::PushBack(Node* node)
|
|
|
+{
|
|
|
+ Data[Size++] = node;
|
|
|
+}
|
|
|
+
|
|
|
+void ImTriangulator::Span::RemoveByIndex(int index)
|
|
|
+{
|
|
|
+ for (int i = Size - 1; i >= 0; --i)
|
|
|
+ {
|
|
|
+ if (Data[i]->Index == index)
|
|
|
+ {
|
|
|
+ Data[i] = Data[Size - 1];
|
|
|
+ --Size;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ImTriangulator::FlipNodeList()
|
|
|
+{
|
|
|
+ Node* prev = _Nodes;
|
|
|
+ Node* temp = _Nodes;
|
|
|
+ Node* current = _Nodes->Next;
|
|
|
+
|
|
|
+ prev->Next = prev;
|
|
|
+ prev->Prev = prev;
|
|
|
+
|
|
|
+ while (current != _Nodes)
|
|
|
+ {
|
|
|
+ temp = current->Next;
|
|
|
+
|
|
|
+ current->Next = prev;
|
|
|
+ prev->Prev = current;
|
|
|
+ _Nodes->Next = current;
|
|
|
+ current->Prev = _Nodes;
|
|
|
+
|
|
|
+ prev = current;
|
|
|
+ current = temp;
|
|
|
+ }
|
|
|
+
|
|
|
+ _Nodes = prev;
|
|
|
+}
|
|
|
+
|
|
|
+bool ImTriangulator::IsEar(const Node* node, int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const
|
|
|
+{
|
|
|
+ for (int i = 0; i < _Reflexes.Size; ++i)
|
|
|
+ {
|
|
|
+ Node* reflex = _Reflexes.Data[i];
|
|
|
+
|
|
|
+ if (reflex->Index == i0 || reflex->Index == i1 || reflex->Index == i2)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (ImTriangleContainsPoint(v0, v1, v2, *reflex->Point))
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+ImTriangulator::Type ImTriangulator::ClassifyNode(const Node* node) const
|
|
|
+{
|
|
|
+ const int i0 = node->Prev->Index;
|
|
|
+ const int i1 = node->Index;
|
|
|
+ const int i2 = node->Next->Index;
|
|
|
+
|
|
|
+ const ImVec2& v0 = *node->Prev->Point;
|
|
|
+ const ImVec2& v1 = *node->Point;
|
|
|
+ const ImVec2& v2 = *node->Next->Point;
|
|
|
+
|
|
|
+ if (ImPathIsConvex(v0, v1, v2))
|
|
|
+ {
|
|
|
+ if (IsEar(node, i0, i1, i2, v0, v1, v2))
|
|
|
+ return Ear;
|
|
|
+ else
|
|
|
+ return Convex;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return Reflex;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ImTriangulator::ReclasifyNode(Node* node)
|
|
|
+{
|
|
|
+ Type type = ClassifyNode(node);
|
|
|
+
|
|
|
+ if (type == node->Type)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (node->Type == Reflex)
|
|
|
+ _Reflexes.RemoveByIndex(node->Index);
|
|
|
+ else if (node->Type == Ear)
|
|
|
+ _Ears.RemoveByIndex(node->Index);
|
|
|
+
|
|
|
+ if (type == Reflex)
|
|
|
+ _Reflexes.PushBack(node);
|
|
|
+ else if (type == Ear)
|
|
|
+ _Ears.PushBack(node);
|
|
|
+
|
|
|
+ node->Type = type;
|
|
|
+}
|
|
|
+
|
|
|
+void ImDrawList::AddConcavePolyFilled(const ImVec2* points, const int points_count, ImU32 col)
|
|
|
+{
|
|
|
+ if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ // coarse culling against viewport to avoid processing triangles outside of the visible area
|
|
|
+ ImVec2 bounds_min = ImVec2(FLT_MAX, FLT_MAX);
|
|
|
+ ImVec2 bounds_max = ImVec2(-FLT_MAX, -FLT_MAX);
|
|
|
+
|
|
|
+ for (int i = 0; i < points_count; ++i)
|
|
|
+ {
|
|
|
+ const ImVec2& pos = points[i];
|
|
|
+
|
|
|
+ bounds_min = ImMin(bounds_min, pos);
|
|
|
+ bounds_max = ImMax(bounds_max, pos);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ImRect(_ClipRectStack.back()).Overlaps(ImRect(bounds_min, bounds_max)))
|
|
|
+ return;
|
|
|
+
|
|
|
+ const ImVec2 uv = _Data->TexUvWhitePixel;
|
|
|
+
|
|
|
+ if (Flags & ImDrawListFlags_AntiAliasedFill)
|
|
|
+ {
|
|
|
+ // Anti-aliased Fill
|
|
|
+ const float AA_SIZE = _FringeScale;
|
|
|
+ const ImU32 col_trans = col & ~IM_COL32_A_MASK;
|
|
|
+ const int idx_count = (points_count - 2)*3 + points_count * 6;
|
|
|
+ const int vtx_count = (points_count * 2);
|
|
|
+ PrimReserve(idx_count, vtx_count);
|
|
|
+
|
|
|
+ // Add indexes for fill
|
|
|
+ unsigned int vtx_inner_idx = _VtxCurrentIdx;
|
|
|
+ unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;
|
|
|
+
|
|
|
+ _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
|
|
|
+ ImTriangulator triangulator = ImTriangulator(points, points_count, _Data->TempBuffer.Data);
|
|
|
+ while (triangulator.HasNext())
|
|
|
+ {
|
|
|
+ ImTriangulator::Triangle triangle = triangulator.Next();
|
|
|
+ _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (triangle.Index[0] << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (triangle.Index[1] << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (triangle.Index[2] << 1));
|
|
|
+ _IdxWritePtr += 3;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Compute normals
|
|
|
+ _Data->TempBuffer.reserve_discard(points_count);
|
|
|
+ ImVec2* temp_normals = _Data->TempBuffer.Data;
|
|
|
+ for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
|
|
|
+ {
|
|
|
+ const ImVec2& p0 = points[i0];
|
|
|
+ const ImVec2& p1 = points[i1];
|
|
|
+ float dx = p1.x - p0.x;
|
|
|
+ float dy = p1.y - p0.y;
|
|
|
+ IM_NORMALIZE2F_OVER_ZERO(dx, dy);
|
|
|
+ temp_normals[i0].x = dy;
|
|
|
+ temp_normals[i0].y = -dx;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
|
|
|
+ {
|
|
|
+ // Average normals
|
|
|
+ const ImVec2& n0 = temp_normals[i0];
|
|
|
+ const ImVec2& n1 = temp_normals[i1];
|
|
|
+ float dm_x = (n0.x + n1.x) * 0.5f;
|
|
|
+ float dm_y = (n0.y + n1.y) * 0.5f;
|
|
|
+ IM_FIXNORMAL2F(dm_x, dm_y);
|
|
|
+ dm_x *= AA_SIZE * 0.5f;
|
|
|
+ dm_y *= AA_SIZE * 0.5f;
|
|
|
+
|
|
|
+ // Add vertices
|
|
|
+ _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner
|
|
|
+ _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer
|
|
|
+ _VtxWritePtr += 2;
|
|
|
+
|
|
|
+ // Add indexes for fringes
|
|
|
+ _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
|
|
|
+ _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
|
|
|
+ _IdxWritePtr += 6;
|
|
|
+ }
|
|
|
+ _VtxCurrentIdx += (ImDrawIdx)vtx_count;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Non Anti-aliased Fill
|
|
|
+ const int idx_count = (points_count - 2)*3;
|
|
|
+ const int vtx_count = points_count;
|
|
|
+ PrimReserve(idx_count, vtx_count);
|
|
|
+ for (int i = 0; i < vtx_count; i++)
|
|
|
+ {
|
|
|
+ _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
|
|
|
+ _VtxWritePtr++;
|
|
|
+ }
|
|
|
+ _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
|
|
|
+ ImTriangulator triangulator = ImTriangulator(points, points_count, _Data->TempBuffer.Data);
|
|
|
+ while (triangulator.HasNext())
|
|
|
+ {
|
|
|
+ ImTriangulator::Triangle triangle = triangulator.Next();
|
|
|
+ _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + triangle.Index[0]); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + triangle.Index[1]); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + triangle.Index[2]);
|
|
|
+ _IdxWritePtr += 3;
|
|
|
+ }
|
|
|
+ _VtxCurrentIdx += (ImDrawIdx)vtx_count;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// [SECTION] ImDrawListSplitter
|