diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 6c23bd3c4..4614694ca 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -72,6 +72,16 @@ bool Graphics::getConstant(DrawMode in, const char *&out) return drawModes.find(in, out); } +bool Graphics::getConstant(const char *in, ArcMode &out) +{ + return arcModes.find(in, out); +} + +bool Graphics::getConstant(ArcMode in, const char *&out) +{ + return arcModes.find(in, out); +} + bool Graphics::getConstant(const char *in, BlendMode &out) { return blendModes.find(in, out); @@ -180,6 +190,15 @@ StringMap::Entry Graphics::drawMode StringMap Graphics::drawModes(Graphics::drawModeEntries, sizeof(Graphics::drawModeEntries)); +StringMap::Entry Graphics::arcModeEntries[] = +{ + { "open", ARC_OPEN }, + { "closed", ARC_CLOSED }, + { "pie", ARC_PIE }, +}; + +StringMap Graphics::arcModes(Graphics::arcModeEntries, sizeof(Graphics::arcModeEntries)); + StringMap::Entry Graphics::blendModeEntries[] = { { "alpha", BLEND_ALPHA }, diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 583822c5f..a2cdf7e7d 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -70,6 +70,14 @@ public: DRAW_MAX_ENUM }; + enum ArcMode + { + ARC_OPEN, + ARC_CLOSED, + ARC_PIE, + ARC_MAX_ENUM + }; + enum BlendMode { BLEND_ALPHA, @@ -259,6 +267,9 @@ public: static bool getConstant(const char *in, DrawMode &out); static bool getConstant(DrawMode in, const char *&out); + static bool getConstant(const char *in, ArcMode &out); + static bool getConstant(ArcMode in, const char *&out); + static bool getConstant(const char *in, BlendMode &out); static bool getConstant(BlendMode in, const char *&out); @@ -294,6 +305,9 @@ private: static StringMap::Entry drawModeEntries[]; static StringMap drawModes; + static StringMap::Entry arcModeEntries[]; + static StringMap arcModes; + static StringMap::Entry blendModeEntries[]; static StringMap blendModes; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 785371426..ca075c25c 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -1369,7 +1369,7 @@ void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b, int po delete[] coords; } -void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1, float angle2, int points) +void Graphics::arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, float angle2, int points) { // Nothing to display with no points or equal angles. (Or is there with line mode?) if (points <= 0 || angle1 == angle2) @@ -1378,7 +1378,7 @@ void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1, // Oh, you want to draw a circle? if (fabs(angle1 - angle2) >= 2.0f * (float) LOVE_M_PI) { - circle(mode, x, y, radius, points); + circle(drawmode, x, y, radius, points); return; } @@ -1387,20 +1387,62 @@ void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1, if (angle_shift == 0.0) return; - float phi = angle1; - int num_coords = (points + 3) * 2; - float *coords = new float[num_coords]; - coords[0] = coords[num_coords - 2] = x; - coords[1] = coords[num_coords - 1] = y; + // Prevent the connecting line from being drawn if a closed line arc has a + // small angle. Avoids some visual issues when connected lines are at sharp + // angles, due to the miter line join drawing code. + if (drawmode == DRAW_LINE && arcmode == ARC_CLOSED && fabsf(angle1 - angle2) < LOVE_TORAD(4)) + arcmode = ARC_OPEN; - for (int i = 0; i <= points; ++i, phi += angle_shift) + // Quick fix for the last part of a filled open arc not being drawn (because + // polygon(DRAW_FILL, ...) doesn't work without a closed loop of vertices.) + if (drawmode == DRAW_FILL && arcmode == ARC_OPEN) + arcmode = ARC_CLOSED; + + float phi = angle1; + + float *coords = nullptr; + int num_coords = 0; + + const auto createPoints = [&](float *coordinates) { - coords[2 * (i+1)] = x + radius * cosf(phi); - coords[2 * (i+1) + 1] = y + radius * sinf(phi); + for (int i = 0; i <= points; ++i, phi += angle_shift) + { + coordinates[2 * i + 0] = x + radius * cosf(phi); + coordinates[2 * i + 1] = y + radius * sinf(phi); + } + }; + + if (arcmode == ARC_PIE) + { + num_coords = (points + 3) * 2; + coords = new float[num_coords]; + + coords[0] = coords[num_coords - 2] = x; + coords[1] = coords[num_coords - 1] = y; + + createPoints(coords + 2); + } + else if (arcmode == ARC_OPEN) + { + num_coords = (points + 1) * 2; + coords = new float[num_coords]; + + createPoints(coords); + } + else // ARC_CLOSED + { + num_coords = (points + 2) * 2; + coords = new float[num_coords]; + + createPoints(coords); + + // Connect the ends of the arc. + coords[num_coords - 2] = coords[0]; + coords[num_coords - 1] = coords[1]; } // NOTE: We rely on polygon() using GL_TRIANGLE_FAN, when fill mode is used. - polygon(mode, coords, num_coords); + polygon(drawmode, coords, num_coords); delete[] coords; } diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index e5b49c3d2..d55afa747 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -420,7 +420,8 @@ public: /** * Draws an arc using the specified arguments. - * @param mode The mode of drawing (line/filled). + * @param drawmode The mode of drawing (line/filled). + * @param arcmode The type of arc. * @param x X-coordinate. * @param y Y-coordinate. * @param radius Radius of the arc. @@ -428,7 +429,7 @@ public: * @param angle2 The angle at which the arc terminates. * @param points Number of points to use to draw the arc. **/ - void arc(DrawMode mode, float x, float y, float radius, float angle1, float angle2, int points = 10); + void arc(DrawMode drawmode, ArcMode arcmode, float x, float y, float radius, float angle1, float angle2, int points = 10); /** * Draws a polygon with an arbitrary number of vertices. diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 4cf49852e..244680a9e 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -1711,7 +1711,7 @@ int w_rectangle(lua_State *L) Graphics::DrawMode mode; const char *str = luaL_checkstring(L, 1); if (!Graphics::getConstant(str, mode)) - return luaL_error(L, "Incorrect draw mode %s", str); + return luaL_error(L, "Invalid draw mode: %s", str); float x = (float)luaL_checknumber(L, 2); float y = (float)luaL_checknumber(L, 3); @@ -1742,7 +1742,7 @@ int w_circle(lua_State *L) Graphics::DrawMode mode; const char *str = luaL_checkstring(L, 1); if (!Graphics::getConstant(str, mode)) - return luaL_error(L, "Incorrect draw mode %s", str); + return luaL_error(L, "Invalid draw mode: %s", str); float x = (float)luaL_checknumber(L, 2); float y = (float)luaL_checknumber(L, 3); @@ -1762,7 +1762,7 @@ int w_ellipse(lua_State *L) Graphics::DrawMode mode; const char *str = luaL_checkstring(L, 1); if (!Graphics::getConstant(str, mode)) - return luaL_error(L, "Incorrect draw mode %s", str); + return luaL_error(L, "Invalid draw mode: %s", str); float x = (float)luaL_checknumber(L, 2); float y = (float)luaL_checknumber(L, 3); @@ -1781,23 +1781,41 @@ int w_ellipse(lua_State *L) int w_arc(lua_State *L) { - Graphics::DrawMode mode; - const char *str = luaL_checkstring(L, 1); - if (!Graphics::getConstant(str, mode)) - return luaL_error(L, "Incorrect draw mode %s", str); + Graphics::DrawMode drawmode; + const char *drawstr = luaL_checkstring(L, 1); + if (!Graphics::getConstant(drawstr, drawmode)) + return luaL_error(L, "Invalid draw mode: %s", drawstr); - float x = (float)luaL_checknumber(L, 2); - float y = (float)luaL_checknumber(L, 3); - float radius = (float)luaL_checknumber(L, 4); - float angle1 = (float)luaL_checknumber(L, 5); - float angle2 = (float)luaL_checknumber(L, 6); - int points; - if (lua_isnoneornil(L, 7)) - points = radius > 10 ? (int)(radius) : 10; - else - points = (int) luaL_checknumber(L, 7); + int startidx = 2; - instance()->arc(mode, x, y, radius, angle1, angle2, points); + Graphics::ArcMode arcmode = Graphics::ARC_PIE; + + if (lua_type(L, 2) == LUA_TSTRING) + { + const char *arcstr = luaL_checkstring(L, 2); + if (!Graphics::getConstant(arcstr, arcmode)) + return luaL_error(L, "Invalid arc mode: %s", arcstr); + + startidx = 3; + } + + float x = (float) luaL_checknumber(L, startidx + 0); + float y = (float) luaL_checknumber(L, startidx + 1); + float radius = (float) luaL_checknumber(L, startidx + 2); + float angle1 = (float) luaL_checknumber(L, startidx + 3); + float angle2 = (float) luaL_checknumber(L, startidx + 4); + + int points = (int) radius; + float angle = fabs(angle1 - angle2); + + // The amount of points is based on the fraction of the circle created by the arc. + if (angle < 2.0f * (float) LOVE_M_PI) + points *= angle / (2.0f * (float) LOVE_M_PI); + + points = std::max(points, 10); + points = (int) luaL_optnumber(L, startidx + 5, points); + + instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points); return 0; }