Browse Source

Added a new variant of love.graphics.arc: arc(drawmode, arcmode, x, y, radius, angle1, angle2 [, points]).

arcmode is "open", "closed", or "pie" (with pie being the mode used for the original variant of the function).
Alex Szpakowski 9 years ago
parent
commit
a42341aa73

+ 19 - 0
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<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawMode
 
 StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM> Graphics::drawModes(Graphics::drawModeEntries, sizeof(Graphics::drawModeEntries));
 
+StringMap<Graphics::ArcMode, Graphics::ARC_MAX_ENUM>::Entry Graphics::arcModeEntries[] =
+{
+	{ "open",   ARC_OPEN   },
+	{ "closed", ARC_CLOSED },
+	{ "pie",    ARC_PIE    },
+};
+
+StringMap<Graphics::ArcMode, Graphics::ARC_MAX_ENUM> Graphics::arcModes(Graphics::arcModeEntries, sizeof(Graphics::arcModeEntries));
+
 StringMap<Graphics::BlendMode, Graphics::BLEND_MAX_ENUM>::Entry Graphics::blendModeEntries[] =
 {
 	{ "alpha",    BLEND_ALPHA    },

+ 14 - 0
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<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
 	static StringMap<DrawMode, DRAW_MAX_ENUM> drawModes;
 
+	static StringMap<ArcMode, ARC_MAX_ENUM>::Entry arcModeEntries[];
+	static StringMap<ArcMode, ARC_MAX_ENUM> arcModes;
+
 	static StringMap<BlendMode, BLEND_MAX_ENUM>::Entry blendModeEntries[];
 	static StringMap<BlendMode, BLEND_MAX_ENUM> blendModes;
 

+ 52 - 10
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;
 
+	// 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;
+
+	// 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;
-	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;
 
-	for (int i = 0; i <= points; ++i, phi += angle_shift)
+	float *coords = nullptr;
+	int num_coords = 0;
+
+	const auto createPoints = [&](float *coordinates)
+	{
+		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
 	{
-		coords[2 * (i+1)]     = x + radius * cosf(phi);
-		coords[2 * (i+1) + 1] = y + radius * sinf(phi);
+		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;
 }

+ 3 - 2
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.

+ 36 - 18
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;
+
+	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(mode, x, y, radius, angle1, angle2, points);
+	instance()->arc(drawmode, arcmode, x, y, radius, angle1, angle2, points);
 	return 0;
 }