Move circle drawing code to draw.cpp.

This commit is contained in:
Tommy 2022-05-29 13:04:56 +12:00
parent 96bc7d6046
commit 881abd1123
3 changed files with 140 additions and 125 deletions

View File

@ -129,6 +129,96 @@ void draw::point(int x, int y)
SDL_RenderDrawPoint(renderer(), x, y);
}
void draw::circle(int cx, int cy, int r, const color_t& c, uint8_t octants)
{
draw::set_color(c);
draw::circle(cx, cy, r, octants);
}
void draw::circle(int cx, int cy, int r, uint8_t octants)
{
// Algorithm based on
// http://de.wikipedia.org/wiki/Rasterung_von_Kreisen#Methode_von_Horn
// version of 2011.02.07.
int d = -r;
int x = r;
int y = 0;
std::vector<SDL_Point> points;
while(!(y > x)) {
if(octants & 0x04) points.push_back({cx + x, cy + y});
if(octants & 0x02) points.push_back({cx + x, cy - y});
if(octants & 0x20) points.push_back({cx - x, cy + y});
if(octants & 0x40) points.push_back({cx - x, cy - y});
if(octants & 0x08) points.push_back({cx + y, cy + x});
if(octants & 0x01) points.push_back({cx + y, cy - x});
if(octants & 0x10) points.push_back({cx - y, cy + x});
if(octants & 0x80) points.push_back({cx - y, cy - x});
d += 2 * y + 1;
++y;
if(d > 0) {
d += -2 * x + 2;
--x;
}
}
draw::points(points);
}
void draw::disc(int cx, int cy, int r, const color_t& c, uint8_t octants)
{
draw::set_color(c);
draw::disc(cx, cy, r, octants);
}
void draw::disc(int cx, int cy, int r, uint8_t octants)
{
int d = -r;
int x = r;
int y = 0;
while(!(y > x)) {
// I use the formula of Bresenham's line algorithm
// to determine the boundaries of a segment.
// The slope of the line is always 1 or -1 in this case.
if(octants & 0x04)
// x2 - 1 = y2 - (cy + 1) + cx
draw::line(cx + x, cy + y + 1, cx + y + 1, cy + y + 1);
if(octants & 0x02)
// x2 - 1 = cy - y2 + cx
draw::line(cx + x, cy - y, cx + y + 1, cy - y);
if(octants & 0x20)
// x2 + 1 = (cy + 1) - y2 + (cx - 1)
draw::line(cx - x - 1, cy + y + 1, cx - y - 2, cy + y + 1);
if(octants & 0x40)
// x2 + 1 = y2 - cy + (cx - 1)
draw::line(cx - x - 1, cy - y, cx - y - 2, cy - y);
if(octants & 0x08)
// y2 = x2 - cx + (cy + 1)
draw::line(cx + y, cy + x + 1, cx + y, cy + y + 1);
if(octants & 0x01)
// y2 = cx - x2 + cy
draw::line(cx + y, cy - x, cx + y, cy - y);
if(octants & 0x10)
// y2 = (cx - 1) - x2 + (cy + 1)
draw::line(cx - y - 1, cy + x + 1, cx - y - 1, cy + y + 1);
if(octants & 0x80)
// y2 = x2 - (cx - 1) + cy
draw::line(cx - y - 1, cy - x, cx - y - 1, cy - y);
d += 2 * y + 1;
++y;
if(d > 0) {
d += -2 * x + 2;
--x;
}
}
}
/*******************/
/* texture drawing */

View File

@ -138,6 +138,46 @@ void points(const std::vector<SDL_Point>& points);
/** Draw a single point. */
void point(int x, int y);
// TODO: enum for common octant choices - nice but not necessary
/**
* Draw a circle of the given colour.
*
* Only the outline of the circle is drawn. To draw a filled circle,
* use draw::disc().
*
* The octants bitfield can be used to draw only certain octants
* of the circle, resulting in one or more arcs.
*
* If no colour is specified, the current drawing colour will be used.
*
* @param x The x coordinate of the center of the circle.
* @param y The y coordinate of the center of the circle.
* @param r The radius of the circle.
* @param c The colour of the circle.
* @param octants A bitfield indicating which octants to draw,
* starting at twelve o'clock and moving clockwise.
*/
void circle(int x, int y, int r, const color_t& c, uint8_t octants = 0xff);
void circle(int x, int y, int r, uint8_t octants = 0xff);
/**
* Draw a solid disc of the given colour.
*
* The octants bitfield can be used to draw only certain octants
* of the disc, resulting in one or more filled wedges.
*
* If no colour is specified, the current drawing colour will be used.
*
* @param x The x coordinate of the center of the circle.
* @param y The y coordinate of the center of the circle.
* @param r The radius of the circle.
* @param c The colour of the circle.
* @param octants A bitfield indicating which octants to draw,
* starting at twelve o'clock and moving clockwise.
*/
void disc(int x, int y, int r, const color_t& c, uint8_t octants = 0xff);
void disc(int x, int y, int r, uint8_t octants = 0xff);
/*******************/
/* texture drawing */

View File

@ -39,121 +39,6 @@
namespace gui2
{
namespace
{
/***** ***** ***** ***** ***** DRAWING PRIMITIVES ***** ***** ***** ***** *****/
/**
* Draws a circle on a surface.
*
* @pre The @p surface is locked.
*
* @param canvas The canvas to draw upon, the caller should lock the
* surface before calling.
* @param color The border color of the circle to draw.
* @param x_center The x coordinate of the center of the circle to draw.
* @param y_center The y coordinate of the center of the circle to draw.
* @param radius The radius of the circle to draw.
* @tparam octants A bitfield indicating which octants to draw, starting at twelve o'clock and moving clockwise.
*/
template<unsigned int octants = 0xff>
static void draw_circle(
color_t color,
const int x_center,
const int y_center,
const int radius)
{
DBG_GUI_D << "Shape: draw circle at " << x_center << ',' << y_center
<< " with radius " << radius << ".\n";
// Algorithm based on
// http://de.wikipedia.org/wiki/Rasterung_von_Kreisen#Methode_von_Horn
// version of 2011.02.07.
int d = -static_cast<int>(radius);
int x = radius;
int y = 0;
std::vector<SDL_Point> points;
while(!(y > x)) {
if(octants & 0x04) points.push_back({x_center + x, y_center + y});
if(octants & 0x02) points.push_back({x_center + x, y_center - y});
if(octants & 0x20) points.push_back({x_center - x, y_center + y});
if(octants & 0x40) points.push_back({x_center - x, y_center - y});
if(octants & 0x08) points.push_back({x_center + y, y_center + x});
if(octants & 0x01) points.push_back({x_center + y, y_center - x});
if(octants & 0x10) points.push_back({x_center - y, y_center + x});
if(octants & 0x80) points.push_back({x_center - y, y_center - x});
d += 2 * y + 1;
++y;
if(d > 0) {
d += -2 * x + 2;
--x;
}
}
draw::set_color(color);
draw::points(points);
}
// TODO: highdpi - move to general drawing function library
/**
* Draws a filled circle on a surface.
*
* @pre The @p surface is locked.
*
* @param canvas The canvas to draw upon, the caller should lock the
* surface before calling.
* @param color The fill color of the circle to draw.
* @param x_center The x coordinate of the center of the circle to draw.
* @param y_center The y coordinate of the center of the circle to draw.
* @param radius The radius of the circle to draw.
* @tparam octants A bitfield indicating which octants to draw, starting at twelve o'clock and moving clockwise.
*/
template<unsigned int octants = 0xff>
static void fill_circle(
color_t color,
const int x_center,
const int y_center,
const int radius)
{
DBG_GUI_D << "Shape: draw filled circle at " << x_center << ',' << y_center
<< " with radius " << radius << ".\n";
draw::set_color(color);
int d = -static_cast<int>(radius);
int x = radius;
int y = 0;
while(!(y > x)) {
// I use the formula of Bresenham's line algorithm to determine the boundaries of a segment.
// The slope of the line is always 1 or -1 in this case.
if(octants & 0x04) draw::line(x_center + x, y_center + y + 1, x_center + y + 1, y_center + y + 1); // x2 - 1 = y2 - (y_center + 1) + x_center
if(octants & 0x02) draw::line(x_center + x, y_center - y, x_center + y + 1, y_center - y); // x2 - 1 = y_center - y2 + x_center
if(octants & 0x20) draw::line(x_center - x - 1, y_center + y + 1, x_center - y - 2, y_center + y + 1); // x2 + 1 = (y_center + 1) - y2 + (x_center - 1)
if(octants & 0x40) draw::line(x_center - x - 1, y_center - y, x_center - y - 2, y_center - y); // x2 + 1 = y2 - y_center + (x_center - 1)
if(octants & 0x08) draw::line(x_center + y, y_center + x + 1, x_center + y, y_center + y + 1); // y2 = x2 - x_center + (y_center + 1)
if(octants & 0x01) draw::line(x_center + y, y_center - x, x_center + y, y_center - y); // y2 = x_center - x2 + y_center
if(octants & 0x10) draw::line(x_center - y - 1, y_center + x + 1, x_center - y - 1, y_center + y + 1); // y2 = (x_center - 1) - x2 + (y_center + 1)
if(octants & 0x80) draw::line(x_center - y - 1, y_center - x, x_center - y - 1, y_center - y); // y2 = x2 - (x_center - 1) + y_center
d += 2 * y + 1;
++y;
if(d > 0) {
d += -2 * x + 2;
--x;
}
}
}
} // namespace
/***** ***** ***** ***** ***** LINE ***** ***** ***** ***** *****/
line_shape::line_shape(const config& cfg)
@ -353,10 +238,10 @@ void round_rectangle_shape::draw(
draw::fill({x + border_thickness_, y + r + 1, w - border_thickness_ * 2, h - r * 2});
draw::fill({x + r, y - r + h + 1, w - r * 2, r - border_thickness_});
fill_circle<0xc0>(fill_color, x + r, y + r, r);
fill_circle<0x03>(fill_color, x + w - r, y + r, r);
fill_circle<0x30>(fill_color, x + r, y + h - r, r);
fill_circle<0x0c>(fill_color, x + w - r, y + h - r, r);
draw::disc(x + r, y + r, r, 0xc0);
draw::disc(x + w - r, y + r, r, 0x03);
draw::disc(x + r, y + h - r, r, 0x30);
draw::disc(x + w - r, y + h - r, r, 0x0c);
}
const color_t border_color = border_color_(variables);
@ -370,10 +255,10 @@ void round_rectangle_shape::draw(
draw::line(x + i, y + r, x + i, y + h - r);
draw::line(x + w - i, y + r, x + w - i, y + h - r);
draw_circle<0xc0>(border_color, x + r, y + r, r - i);
draw_circle<0x03>(border_color, x + w - r, y + r, r - i);
draw_circle<0x30>(border_color, x + r, y + h - r, r - i);
draw_circle<0x0c>(border_color, x + w - r, y + h - r, r - i);
draw::circle(x + r, y + r, r - i, 0xc0);
draw::circle(x + w - r, y + r, r - i, 0x03);
draw::circle(x + r, y + h - r, r - i, 0x30);
draw::circle(x + w - r, y + h - r, r - i, 0x0c);
}
}
@ -415,12 +300,12 @@ void circle_shape::draw(
const color_t fill_color = fill_color_(variables);
if(!fill_color.null() && radius) {
fill_circle(fill_color, x, y, radius);
draw::disc(x, y, radius, fill_color);
}
const color_t border_color = border_color_(variables);
for(unsigned int i = 0; i < border_thickness_; i++) {
draw_circle(border_color, x, y, radius - i);
draw::circle(x, y, radius - i, border_color);
}
}