diff --git a/README.md b/README.md index eab7373..14c3f75 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,27 @@ function() -- Draw image at the bottom left corner of the screen but scaled to 50x50. d2d.image(image, 0, screen_h - 50, 50, 50) + + -- x, y, width, height, corner round x, corner round y, thickness, color + d2d.rounded_rect(400, 500, 80, 40, 5, 15, 5, 0xFF00FFFF) + -- x, y, width, height, corner round x, corner round y, color + d2d.fill_rounded_rect(400, 500, 80, 40, 5, 15, 0xFF00FFFF) + + -- x, y, radius x, radius y, color + d2d.fill_circle(600, 500, 50, 50, 0xFF00FFFF) + d2d.fill_circle(700, 500, 50, 80, 0xFF00FFFF) + + -- x, y, radius x, radius y, thickness, color + d2d.circle(800, 500, 50, 50, 5, 0xFF00FFFF) + d2d.circle(900, 500, 50, 80, 5, 0xFF00FFFF) + + -- x, y, radius, start angle, sweep angle, color + d2d.pie(1000, 500, 50, 0, 240, 0xFF00FFFF) + d2d.pie(1100, 500, 50, 60, 240, 0xFF00FFFF) + + -- x, y, outer radius, inner radius, start angle, sweep angle, color + d2d.ring(1200, 500, 50, 30, 0, 240, 0xFF00FFFF) + d2d.ring(1300, 500, 50, 30, 60, 240, 0xFF00FFFF) end) ``` diff --git a/src/D2DPainter.cpp b/src/D2DPainter.cpp index 18dce8a..246cafc 100644 --- a/src/D2DPainter.cpp +++ b/src/D2DPainter.cpp @@ -77,6 +77,16 @@ void D2DPainter::outline_rect(float x, float y, float w, float h, float thicknes m_context->DrawRectangle({x, y, x + w, y + h}, m_brush.Get(), thickness); } +void D2DPainter::rounded_rect(float x, float y, float w, float h, float radiusX, float radiusY, float thickness, unsigned int color) { + set_color(color); + m_context->DrawRoundedRectangle({x, y, x + w, y + h, radiusX, radiusY}, m_brush.Get(), thickness); +} + +void D2DPainter::fill_rounded_rect(float x, float y, float w, float h, float radiusX, float radiusY, unsigned int color) { + set_color(color); + m_context->FillRoundedRectangle({x, y, x + w, y + h, radiusX, radiusY}, m_brush.Get()); +} + void D2DPainter::line(float x1, float y1, float x2, float y2, float thickness, unsigned int color) { set_color(color); m_context->DrawLine({x1, y1}, {x2, y2}, m_brush.Get(), thickness); @@ -90,3 +100,137 @@ void D2DPainter::image(std::shared_ptr& image, float x, float y) { void D2DPainter::image(std::shared_ptr& image, float x, float y, float w, float h) { m_context->DrawBitmap(image->bitmap().Get(), {x, y, x + w, y + h}); } + +void D2DPainter::fill_circle(float centerX, float centerY, float radius, unsigned int color) { + set_color(color); + D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(centerX, centerY), radius, radius); + m_context->FillEllipse(ellipse, m_brush.Get()); +} + +void D2DPainter::fill_circle(float centerX, float centerY, float radiusX, float radiusY, unsigned int color) { + set_color(color); + D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(centerX, centerY), radiusX, radiusY); + m_context->FillEllipse(ellipse, m_brush.Get()); +} + +void D2DPainter::circle(float centerX, float centerY, float radius, int thickness, unsigned int color) { + set_color(color); + D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(centerX, centerY), radius, radius); + m_context->DrawEllipse(ellipse, m_brush.Get(), thickness); +} + +void D2DPainter::circle(float centerX, float centerY, float radiusX, float radiusY, int thickness, unsigned int color) { + set_color(color); + D2D1_ELLIPSE ellipse = D2D1::Ellipse(D2D1::Point2F(centerX, centerY), radiusX, radiusY); + m_context->DrawEllipse(ellipse, m_brush.Get(), thickness); +} + +void D2DPainter::pie(float centerX, float centerY, float radius, float startAngle, float sweepAngle, unsigned int color) { + startAngle = std::clamp(startAngle, 0.0f, 360.0f); + sweepAngle = std::clamp(sweepAngle, 0.0f, 360.0f); + if (sweepAngle == 0.0f) { + return; + } + if (sweepAngle == 360.0f) { + return this->fill_circle(centerX, centerY, radius, color); + } + + ComPtr pathGeometry; + m_d2d1->CreatePathGeometry(&pathGeometry); + + ComPtr geometrySink; + pathGeometry->Open(&geometrySink); + + const float startRadians = startAngle * (3.14159265f / 180.0f); + const float endRadians = (startAngle + sweepAngle) * (3.14159265f / 180.0f); + + D2D1_POINT_2F circleCenter = D2D1::Point2F(centerX, centerY); + + // circle center -> arc start + D2D1_POINT_2F arcStart = D2D1::Point2F(centerX + radius * cosf(startRadians), centerY + radius * sinf(startRadians)); + geometrySink->BeginFigure(circleCenter, D2D1_FIGURE_BEGIN_FILLED); + geometrySink->AddLine(arcStart); + + // arc start -> arc end + D2D1_POINT_2F arcEnd = D2D1::Point2F(centerX + radius * cosf(endRadians), centerY + radius * sinf(endRadians)); + geometrySink->AddArc(D2D1::ArcSegment(arcEnd, D2D1::SizeF(radius, radius), 0.0f, D2D1_SWEEP_DIRECTION_CLOCKWISE, + (sweepAngle > 180.0f) ? D2D1_ARC_SIZE_LARGE : D2D1_ARC_SIZE_SMALL)); + + // arc end -> circle center + geometrySink->AddLine(circleCenter); + + // end + geometrySink->EndFigure(D2D1_FIGURE_END_CLOSED); + geometrySink->Close(); + + set_color(color); + m_context->FillGeometry(pathGeometry.Get(), m_brush.Get()); +} + +void D2DPainter::ring(float centerX, float centerY, float outerRadius, float innerRadius, unsigned int color) { + ComPtr outerCircle; + m_d2d1->CreateEllipseGeometry(D2D1::Ellipse(D2D1::Point2F(centerX, centerY), outerRadius, outerRadius), &outerCircle); + ComPtr innerCircle; + m_d2d1->CreateEllipseGeometry(D2D1::Ellipse(D2D1::Point2F(centerX, centerY), innerRadius, innerRadius), &innerCircle); + + ComPtr pathGeometry; + m_d2d1->CreatePathGeometry(&pathGeometry); + ComPtr sink; + pathGeometry->Open(&sink); + + outerCircle->CombineWithGeometry(innerCircle.Get(), D2D1_COMBINE_MODE_EXCLUDE, NULL, sink.Get()); + sink->Close(); + + set_color(color); + m_context->FillGeometry(pathGeometry.Get(), m_brush.Get()); +} + +void D2DPainter::ring(float centerX, float centerY, float outerRadius, float innerRadius, float startAngle, float sweepAngle, unsigned int color) { + startAngle = std::clamp(startAngle, 0.0f, 360.0f); + sweepAngle = std::clamp(sweepAngle, 0.0f, 360.0f); + if (sweepAngle == 0.0f) { + return; + } + if (sweepAngle == 360.0f) { + return this->ring(centerX, centerY, outerRadius, innerRadius, color); + } + + set_color(color); + + ComPtr pathGeometry; + m_d2d1->CreatePathGeometry(&pathGeometry); + + ComPtr sink; + pathGeometry->Open(&sink); + + const float startRadians = startAngle * (3.14159265f / 180.0f); + const float endRadians = (startAngle + sweepAngle) * (3.14159265f / 180.0f); + + // outer arc start + D2D1_POINT_2F outerStart = D2D1::Point2F(centerX + outerRadius * std::cos(startRadians), centerY + outerRadius * std::sin(startRadians)); + sink->BeginFigure(outerStart, D2D1_FIGURE_BEGIN_FILLED); + + // outer arc start -> outer arc end + D2D1_POINT_2F outerEnd = D2D1::Point2F(centerX + outerRadius * std::cos(endRadians), centerY + outerRadius * std::sin(endRadians)); + sink->AddArc(D2D1::ArcSegment(outerEnd, D2D1::SizeF(outerRadius, outerRadius), 0.0f, D2D1_SWEEP_DIRECTION_CLOCKWISE, + (sweepAngle > 180.0f) ? D2D1_ARC_SIZE_LARGE : D2D1_ARC_SIZE_SMALL)); + + // outer arc end -> inner arc end + D2D1_POINT_2F innerEnd = D2D1::Point2F(centerX + innerRadius * std::cos(endRadians), centerY + innerRadius * std::sin(endRadians)); + sink->AddLine(innerEnd); + + // inner arc end -> inner arc start + D2D1_POINT_2F innerStart = D2D1::Point2F(centerX + innerRadius * std::cos(startRadians), centerY + innerRadius * std::sin(startRadians)); + sink->AddArc(D2D1::ArcSegment(innerStart, D2D1::SizeF(innerRadius, innerRadius), 0.0f, D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, + (sweepAngle > 180.0f) ? D2D1_ARC_SIZE_LARGE : D2D1_ARC_SIZE_SMALL)); + + // inner arc start -> outer arc start + sink->AddLine(outerStart); + + // end + sink->EndFigure(D2D1_FIGURE_END_CLOSED); + sink->Close(); + + set_color(color); + m_context->FillGeometry(pathGeometry.Get(), m_brush.Get()); +} diff --git a/src/D2DPainter.hpp b/src/D2DPainter.hpp index eff4da4..75b9973 100644 --- a/src/D2DPainter.hpp +++ b/src/D2DPainter.hpp @@ -27,9 +27,18 @@ class D2DPainter { void text(std::shared_ptr& font, const std::string& text, float x, float y, unsigned int color); void fill_rect(float x, float y, float w, float h, unsigned int color); void outline_rect(float x, float y, float w, float h, float thickness, unsigned int color); + void rounded_rect(float x, float y, float w, float h, float radiusX, float radiusY, float thickness, unsigned int color); + void fill_rounded_rect(float x, float y, float w, float h, float radiusX, float radiusY, unsigned int color); void line(float x1, float y1, float x2, float y2, float thickness, unsigned int color); void image(std::shared_ptr& image, float x, float y); void image(std::shared_ptr& image, float x, float y, float w, float h); + void fill_circle(float centerX, float centerY, float radius, unsigned int color); + void fill_circle(float centerX, float centerY, float radiusX, float radiusY, unsigned int color); + void circle(float centerX, float centerY, float radius, int thickness, unsigned int color); + void circle(float centerX, float centerY, float radiusX, float radiusY, int thickness, unsigned int color); + void pie(float centerX, float centerY, float radius, float startAngle, float sweepAngle, unsigned int color); + void ring(float centerX, float centerY, float outerRadius, float innerRadius, unsigned int color); + void ring(float centerX, float centerY, float outerRadius, float innerRadius, float startAngle, float sweepAngle, unsigned int color); auto surface_size() const { return std::make_tuple(m_rt_desc.Width, m_rt_desc.Height); } diff --git a/src/DrawList.cpp b/src/DrawList.cpp index 585070a..42319a2 100644 --- a/src/DrawList.cpp +++ b/src/DrawList.cpp @@ -34,6 +34,33 @@ void DrawList::CommandLock::outline_rect(float x, float y, float w, float h, flo commands.emplace_back(std::move(cmd)); } +void DrawList::CommandLock::rounded_rect(float x, float y, float w, float h, float rX, float rY, float thickness, unsigned int color) { + Command cmd{}; + cmd.type = CommandType::ROUNDED_RECT; + cmd.rounded_rect.x = x; + cmd.rounded_rect.y = y; + cmd.rounded_rect.w = w; + cmd.rounded_rect.h = h; + cmd.rounded_rect.rX = rX; + cmd.rounded_rect.rY = rY; + cmd.rounded_rect.thickness = thickness; + cmd.rounded_rect.color = color; + commands.emplace_back(std::move(cmd)); +} + +void DrawList::CommandLock::fill_rounded_rect(float x, float y, float w, float h, float rX, float rY, unsigned int color) { + Command cmd{}; + cmd.type = CommandType::FILL_ROUNDED_RECT; + cmd.rounded_rect.x = x; + cmd.rounded_rect.y = y; + cmd.rounded_rect.w = w; + cmd.rounded_rect.h = h; + cmd.rounded_rect.rX = rX; + cmd.rounded_rect.rY = rY; + cmd.rounded_rect.color = color; + commands.emplace_back(std::move(cmd)); +} + void DrawList::CommandLock::line(float x1, float y1, float x2, float y2, float thickness, unsigned int color) { Command cmd{}; cmd.type = CommandType::LINE; @@ -56,3 +83,51 @@ void DrawList::CommandLock::image(std::shared_ptr& image, float x, flo cmd.image_resource = image; commands.emplace_back(std::move(cmd)); } + +void DrawList::CommandLock::fill_circle(float x, float y, float radiusX, float radiusY, unsigned int color) { + Command cmd{}; + cmd.type = CommandType::FILL_CIRCLE; + cmd.fill_circle.x = x; + cmd.fill_circle.y = y; + cmd.fill_circle.radiusX = radiusX; + cmd.fill_circle.radiusY = radiusY; + cmd.fill_circle.color = color; + commands.emplace_back(std::move(cmd)); +} + +void DrawList::CommandLock::circle(float x, float y, float radiusX, float radiusY, float thickness, unsigned int color) { + Command cmd{}; + cmd.type = CommandType::CIRCLE; + cmd.circle.x = x; + cmd.circle.y = y; + cmd.circle.radiusX = radiusX; + cmd.circle.radiusY = radiusY; + cmd.circle.thickness = thickness; + cmd.circle.color = color; + commands.emplace_back(std::move(cmd)); +} + +void DrawList::CommandLock::pie(float x, float y, float r, float startAngle, float sweepAngle, unsigned int color) { + Command cmd{}; + cmd.type = CommandType::PIE; + cmd.pie.x = x; + cmd.pie.y = y; + cmd.pie.r = r; + cmd.pie.startAngle = startAngle; + cmd.pie.sweepAngle = sweepAngle; + cmd.pie.color = color; + commands.emplace_back(std::move(cmd)); +} + +void DrawList::CommandLock::ring(float x, float y, float outerRadius, float innerRadius, float startAngle, float sweepAngle, unsigned int color) { + Command cmd{}; + cmd.type = CommandType::RING; + cmd.ring.x = x; + cmd.ring.y = y; + cmd.ring.outerRadius = outerRadius; + cmd.ring.innerRadius = innerRadius; + cmd.ring.startAngle = startAngle; + cmd.ring.sweepAngle = sweepAngle; + cmd.ring.color = color; + commands.emplace_back(std::move(cmd)); +} diff --git a/src/DrawList.hpp b/src/DrawList.hpp index 1c8a626..55fbf5a 100644 --- a/src/DrawList.hpp +++ b/src/DrawList.hpp @@ -10,7 +10,7 @@ class DrawList { public: - enum class CommandType { TEXT, FILL_RECT, OUTLINE_RECT, LINE, IMAGE }; + enum class CommandType { TEXT, FILL_RECT, OUTLINE_RECT, ROUNDED_RECT, FILL_ROUNDED_RECT, LINE, IMAGE, FILL_CIRCLE, CIRCLE, PIE, RING }; struct Command { CommandType type; @@ -35,6 +35,25 @@ class DrawList { float thickness{}; unsigned int color{}; } outline_rect; + struct { + float x{}; + float y{}; + float w{}; + float h{}; + float rX{}; + float rY{}; + float thickness{}; + unsigned int color{}; + } rounded_rect; + struct { + float x{}; + float y{}; + float w{}; + float h{}; + float rX{}; + float rY{}; + unsigned int color{}; + } fill_rounded_rect; struct { float x1{}; float y1{}; @@ -49,6 +68,38 @@ class DrawList { float w{}; float h{}; } image; + struct { + float x{}; + float y{}; + float radiusX{}; + float radiusY{}; + unsigned int color{}; + } fill_circle; + struct { + float x{}; + float y{}; + float radiusX{}; + float radiusY{}; + float thickness{}; + unsigned int color{}; + } circle; + struct { + float x{}; + float y{}; + float r{}; + float startAngle{}; + float sweepAngle{}; + unsigned int color{}; + } pie; + struct { + float x{}; + float y{}; + float outerRadius{}; + float innerRadius{}; + float startAngle{}; + float sweepAngle{}; + unsigned int color{}; + } ring; }; std::string str{}; std::shared_ptr font_resource{}; @@ -62,8 +113,14 @@ class DrawList { void text(std::shared_ptr& font, std::string text, float x, float y, unsigned int color); void fill_rect(float x, float y, float w, float h, unsigned int color); void outline_rect(float x, float y, float w, float h, float thickness, unsigned int color); + void rounded_rect(float x, float y, float w, float h, float rX, float rY, float thickness, unsigned int color); + void fill_rounded_rect(float x, float y, float w, float h, float rX, float rY, unsigned int color); void line(float x1, float y1, float x2, float y2, float thickness, unsigned int color); void image(std::shared_ptr& image, float x, float y, float w, float h); + void fill_circle(float x, float y, float radiusX, float radiusY, unsigned int color); + void circle(float x, float y, float radiusX, float radiusY, float thickness, unsigned int color); + void pie(float x, float y, float r, float startAngle, float sweepAngle, unsigned int color); + void ring(float x, float y, float outerRadius, float innerRadius, float startAngle, float sweepAngle, unsigned int color); }; auto acquire() { return CommandLock{m_commands, std::scoped_lock{m_commands_mux}}; } diff --git a/src/Plugin.cpp b/src/Plugin.cpp index 846cbf0..4f195ae 100644 --- a/src/Plugin.cpp +++ b/src/Plugin.cpp @@ -132,6 +132,12 @@ void on_ref_lua_state_created(lua_State* l) try { d2d["outline_rect"] = [](float x, float y, float w, float h, float thickness, unsigned int color) { g_plugin->cmds->outline_rect(x, y, w, h, thickness, color); }; + d2d["rounded_rect"] = [](float x, float y, float w, float h, float rX, float rY, float thickness, unsigned int color) { + g_plugin->cmds->rounded_rect(x, y, w, h, rX, rY, thickness, color); + }; + d2d["fill_rounded_rect"] = [](float x, float y, float w, float h, float rX, float rY, unsigned int color) { + g_plugin->cmds->fill_rounded_rect(x, y, w, h, rX, rY, color); + }; d2d["line"] = [](float x1, float y1, float x2, float y2, float thickness, unsigned int color) { g_plugin->cmds->line(x1, y1, x2, y2, thickness, color); }; @@ -148,6 +154,18 @@ void on_ref_lua_state_created(lua_State* l) try { g_plugin->cmds->image(image, x, y, w, h); }; + d2d["fill_circle"] = [](float x, float y, float rX, float rY, unsigned int color) { + g_plugin->cmds->fill_circle(x, y, rX, rY, color); + }; + d2d["circle"] = [](float x, float y, float rX, float rY, float thickness, unsigned int color) { + g_plugin->cmds->circle(x, y, rX, rY, thickness, color); + }; + d2d["pie"] = [](float x, float y, float r, float startAngle, float sweepAngle, unsigned int color) { + g_plugin->cmds->pie(x, y, r, startAngle, sweepAngle, color); + }; + d2d["ring"] = [](float x, float y, float outerR, float innerR, float startAngle, float sweepAngle, unsigned int color) { + g_plugin->cmds->ring(x, y, outerR, innerR, startAngle, sweepAngle, color); + }; d2d["surface_size"] = [](sol::this_state s) { auto [w, h] = g_plugin->d2d->surface_size(); sol::variadic_results results{}; @@ -219,6 +237,16 @@ void on_ref_frame() try { cmd.outline_rect.thickness, cmd.outline_rect.color); break; + case DrawList::CommandType::ROUNDED_RECT: + g_plugin->d2d->rounded_rect(cmd.rounded_rect.x, cmd.rounded_rect.y, cmd.rounded_rect.w, cmd.rounded_rect.h, + cmd.rounded_rect.rX, cmd.rounded_rect.rY, cmd.rounded_rect.thickness, cmd.rounded_rect.color); + break; + + case DrawList::CommandType::FILL_ROUNDED_RECT: + g_plugin->d2d->fill_rounded_rect(cmd.rounded_rect.x, cmd.rounded_rect.y, cmd.rounded_rect.w, cmd.rounded_rect.h, + cmd.rounded_rect.rX, cmd.rounded_rect.rY, cmd.rounded_rect.color); + break; + case DrawList::CommandType::LINE: g_plugin->d2d->line(cmd.line.x1, cmd.line.y1, cmd.line.x2, cmd.line.y2, cmd.line.thickness, cmd.line.color); break; @@ -226,6 +254,23 @@ void on_ref_frame() try { case DrawList::CommandType::IMAGE: g_plugin->d2d->image(cmd.image_resource, cmd.image.x, cmd.image.y, cmd.image.w, cmd.image.h); break; + + case DrawList::CommandType::FILL_CIRCLE: + g_plugin->d2d->fill_circle( + cmd.fill_circle.x, cmd.fill_circle.y, cmd.fill_circle.radiusX, cmd.fill_circle.radiusY, cmd.fill_circle.color); + break; + + case DrawList::CommandType::CIRCLE: + g_plugin->d2d->circle(cmd.circle.x, cmd.circle.y, cmd.circle.radiusX, cmd.circle.radiusY, cmd.circle.thickness, cmd.circle.color); + break; + + case DrawList::CommandType::PIE: + g_plugin->d2d->pie(cmd.pie.x, cmd.pie.y, cmd.pie.r, cmd.pie.startAngle, cmd.pie.sweepAngle, cmd.pie.color); + break; + + case DrawList::CommandType::RING: + g_plugin->d2d->ring(cmd.ring.x, cmd.ring.y, cmd.ring.outerRadius, cmd.ring.innerRadius, cmd.ring.startAngle, cmd.ring.sweepAngle, cmd.ring.color); + break; } } },