Skip to content

Commit

Permalink
feat: add circle and quad funcs (#14)
Browse files Browse the repository at this point in the history
Signed-off-by: LingSamuel <[email protected]>
  • Loading branch information
lingsamuel authored Nov 27, 2024
1 parent 391599d commit db6a831
Show file tree
Hide file tree
Showing 6 changed files with 643 additions and 1 deletion.
165 changes: 165 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ function()
d2d.fill_rect(500, 100, w, h, 0xFFFFFFFF)
d2d.text(font, str, 500, 100, 0xFF000000)
d2d.outline_rect(500, 100, w, h, 5, 0xFF00FFFF)
d2d.quad(100, 100, 500, 100, 500, 500, 400, 500, 5, 0xFF00FFFF)
d2d.fill_quad(1100, 1100, 1500, 1100, 1500, 1500, 1400, 1500, 0xFF00FFFF)

local screen_w, screen_h = d2d.surface_size()
local img_w, img_h = image:size()
Expand All @@ -42,6 +44,39 @@ 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, color
d2d.fill_circle(600, 500, 50, 0xFF00FFFF)
-- x, y, radius x, radius y, color
d2d.fill_oval(700, 500, 50, 80, 0xFF00FFFF)

-- x, y, radius, thickness, color
d2d.circle(800, 500, 50, 5, 0xFF00FFFF)
-- x, y, radius x, radius y, thickness, color
d2d.oval(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)
-- negative start angle equals +360 degree
d2d.pie(1200, 100, 50, -90, 240, 0xFF00FFFF)
d2d.pie(1200, 200, 50, 270, 240, 0xFF00FFFF)
-- with clockwise=false
d2d.pie(1300, 100, 50, -90, 240, 0xFF00FFFF, false)

-- 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)
-- negative start angle equals +360 degree
d2d.ring(1600, 100, 50, 30, -90, 240, 0xFF00FFFF)
d2d.ring(1600, 200, 50, 30, 270, 240, 0xFF00FFFF)
-- with clockwise=false
d2d.ring(1700, 100, 50, 30, -90, 240, 0xFF00FFFF, false)
end)
```

Expand Down Expand Up @@ -121,6 +156,60 @@ Draws the outline of a rectangle

---

### `d2d.rounded_rect(x, y, w, h, rX, rY, thickness, color)`
Draws the outline of a rounded rectangle

#### Params
* `x` the horizontal position on the screen
* `y` the vertical position on the screen
* `w` the width of the rectangle
* `h` the height of the rectangle
* `rX` the corner radius X
* `rY` the corner radius Y
* `thickness` the thickness of the outline
* `color` the ARGB color of the rectangle

---

### `d2d.fill_rounded_rect(x, y, w, h, rX, rY, color)`
Draws a filled in a rounded rectangle

#### Params
* `x` the horizontal position on the screen
* `y` the vertical position on the screen
* `w` the width of the rectangle
* `h` the height of the rectangle
* `rX` the corner radius X
* `rY` the corner radius Y
* `color` the ARGB color of the rectangle

---

### `d2d.quad(x1, y1, x2, y2, x3, y3, x4, y4, thickness, color)`
Draws the outline of a quad

#### Params
* `x1, y1` the first coordinate
* `x2, y2` the second coordinate
* `x3, y3` the third coordinate
* `x4, y4` the fourth coordinate
* `thickness` the thickness of the outline
* `color` the ARGB color of the quad

---

### `d2d.fill_quad(x1, y1, x2, y2, x3, y3, x4, y4, color)`
Draws a filled in a quad

#### Params
* `x1, y1` the first coordinate
* `x2, y2` the second coordinate
* `x3, y3` the third coordinate
* `x4, y4` the fourth coordinate
* `color` the ARGB color of the quad

---

### `d2d.line(x1, y1, x2, y2, thickness, color)`
Draws a line between two points

Expand All @@ -134,6 +223,82 @@ Draws a line between two points

---

### `d2d.circle(x, y, r, thickness, color)`
Draws the outline of a circle

#### Params
* `x` the horizontal center on the screen
* `y` the vertical center on the screen
* `r` the radius of the circle
* `thickness` the thickness of the outline
* `color` the ARGB color of the circle

---

### `d2d.fill_circle(x, y, r, color)`
Draws a filled in a circle

#### Params
* `x` the horizontal center on the screen
* `y` the vertical center on the screen
* `r` the radius of the circle
* `color` the ARGB color of the circle

---

### `d2d.oval(x, y, rX, rY, thickness, color)`
Draws the outline of a oval

#### Params
* `x` the horizontal center on the screen
* `y` the vertical center on the screen
* `rX` the horizontal radius of the oval
* `rY` the vertical radius of the oval
* `thickness` the thickness of the outline
* `color` the ARGB color of the oval

---

### `d2d.fill_oval(x, y, rX, rY, color)`
Draws a filled in a oval

#### Params
* `x` the horizontal center on the screen
* `y` the vertical center on the screen
* `rX` the horizontal radius of the oval
* `rY` the vertical radius of the oval
* `color` the ARGB color of the oval

---

### `d2d.pie(x, y, r, startAngle, sweepAngle, color, clockwise)`
Draws a filled pie

#### Params
* `x` the horizontal center on the screen
* `y` the vertical center on the screen
* `startAngle` the pie start angle, range from -360 to 360.
* `sweepAngle` the pie sweep angle, range from 0 to 360.
* `color` the ARGB color of the pie
* `clockwise` by default is true, clockwise. Set false to counter clockwise.

---

### `d2d.ring(x, y, outerRadius, innerRadius, startAngle, sweepAngle, color, clockwise)`
Draws a filled ring

#### Params
* `x` the horizontal center on the screen
* `y` the vertical center on the screen
* `outerRadius` the ring outer radius
* `innerRadius` the ring inner radius
* `startAngle` the pie start angle, range from -360 to 360.
* `sweepAngle` the pie sweep angle, range from 0 to 360.
* `color` the ARGB color of the pie
* `clockwise` by default is true, clockwise. Set false to counter clockwise.

---

### `d2d.image(image, x, y, [w], [h])`
Draws an image at the specified position, optionally scaled.

Expand Down
194 changes: 194 additions & 0 deletions src/D2DPainter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,54 @@ 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::quad(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float thickness, unsigned int color) {
ComPtr<ID2D1PathGeometry> pathGeometry;
m_d2d1->CreatePathGeometry(&pathGeometry);

ComPtr<ID2D1GeometrySink> sink;
pathGeometry->Open(&sink);

sink->BeginFigure(D2D1::Point2F(x1, y1), D2D1_FIGURE_BEGIN_FILLED);
sink->AddLine(D2D1::Point2F(x2, y2));
sink->AddLine(D2D1::Point2F(x3, y3));
sink->AddLine(D2D1::Point2F(x4, y4));

sink->EndFigure(D2D1_FIGURE_END_CLOSED);
sink->Close();

set_color(color);
m_context->DrawGeometry(pathGeometry.Get(), m_brush.Get(), thickness);
}

void D2DPainter::fill_quad(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, unsigned int color) {
ComPtr<ID2D1PathGeometry> pathGeometry;
m_d2d1->CreatePathGeometry(&pathGeometry);

ComPtr<ID2D1GeometrySink> sink;
pathGeometry->Open(&sink);

sink->BeginFigure(D2D1::Point2F(x1, y1), D2D1_FIGURE_BEGIN_FILLED);
sink->AddLine(D2D1::Point2F(x2, y2));
sink->AddLine(D2D1::Point2F(x3, y3));
sink->AddLine(D2D1::Point2F(x4, y4));

sink->EndFigure(D2D1_FIGURE_END_CLOSED);
sink->Close();

set_color(color);
m_context->FillGeometry(pathGeometry.Get(), 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);
Expand All @@ -90,3 +138,149 @@ void D2DPainter::image(std::shared_ptr<D2DImage>& image, float x, float y) {
void D2DPainter::image(std::shared_ptr<D2DImage>& 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, bool clockwise) {
if (startAngle < 0) {
startAngle += 360.0f;
}
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);
}
auto direction = clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;

ComPtr<ID2D1PathGeometry> pathGeometry;
m_d2d1->CreatePathGeometry(&pathGeometry);

ComPtr<ID2D1GeometrySink> geometrySink;
pathGeometry->Open(&geometrySink);

const float startRadians = startAngle * (3.14159265f / 180.0f);
const float sweepRadians = sweepAngle * (3.14159265f / 180.0f);
const float endRadians = clockwise ? (startRadians + sweepRadians) : (startRadians - sweepRadians);

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, direction,
(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<ID2D1EllipseGeometry> outerCircle;
m_d2d1->CreateEllipseGeometry(D2D1::Ellipse(D2D1::Point2F(centerX, centerY), outerRadius, outerRadius), &outerCircle);
ComPtr<ID2D1EllipseGeometry> innerCircle;
m_d2d1->CreateEllipseGeometry(D2D1::Ellipse(D2D1::Point2F(centerX, centerY), innerRadius, innerRadius), &innerCircle);

ComPtr<ID2D1PathGeometry> pathGeometry;
m_d2d1->CreatePathGeometry(&pathGeometry);
ComPtr<ID2D1GeometrySink> 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, bool clockwise) {
if (startAngle < 0) {
startAngle += 360.0f;
}
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);
}
auto direction = clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;
auto counterDirection = !clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;

set_color(color);

ComPtr<ID2D1PathGeometry> pathGeometry;
m_d2d1->CreatePathGeometry(&pathGeometry);

ComPtr<ID2D1GeometrySink> sink;
pathGeometry->Open(&sink);

const float startRadians = startAngle * (3.14159265f / 180.0f);
const float sweepRadians = sweepAngle * (3.14159265f / 180.0f);
const float endRadians = clockwise ? (startRadians + sweepRadians) : (startRadians - sweepRadians);

// 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, direction,
(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, counterDirection,
(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());
}
Loading

0 comments on commit db6a831

Please sign in to comment.