diff --git a/src/common/Vector.h b/src/common/Vector.h index 4c00e75c3..93bfd932a 100644 --- a/src/common/Vector.h +++ b/src/common/Vector.h @@ -66,17 +66,26 @@ public: /** * Normalizes the Vector. + * @param length Desired length of the vector. * @return The old length of the Vector. **/ - float normalize(); + float normalize(float length = 1.0); /** - * Gets a normal to the Vector. + * Gets a vector perpendicular to the Vector. + * To get the true (normalized) normal, use v.getNormal(1.0f / v.getLength()) * @return A normal to the Vector. **/ - Vector getNormal() const; + /** + * Gets a vector perpendicular to the Vector. + * To get the true (normalized) normal, use v.getNormal(1.0f / v.getLength()) + * @param Scale factor to apply. + * @return A normal to the Vector. + **/ + Vector getNormal(float scale) const; + /** * Adds a Vector to this Vector. * @param v The Vector we want to add to this Vector. @@ -186,15 +195,20 @@ inline Vector Vector::getNormal() const return Vector(-y, x); } -inline float Vector::normalize() +inline Vector Vector::getNormal(float scale) const +{ + return Vector(-y * scale, x * scale); +} + +inline float Vector::normalize(float length) { - float len = getLength(); + float length_current = getLength(); - if (len > 0) - (*this) /= len; + if (length_current > 0) + (*this) *= length / length_current; - return len; + return length_current; } /** diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index d7bb0de7b..d1bb115b2 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -69,6 +69,16 @@ bool Graphics::getConstant(LineStyle in, const char *&out) return lineStyles.find(in, out); } +bool Graphics::getConstant(const char *in, LineJoin &out) +{ + return lineJoins.find(in, out); +} + +bool Graphics::getConstant(LineJoin in, const char *&out) +{ + return lineJoins.find(in, out); +} + bool Graphics::getConstant(const char *in, PointStyle &out) { return pointStyles.find(in, out); @@ -137,6 +147,15 @@ StringMap::Entry Graphics::lineSty StringMap Graphics::lineStyles(Graphics::lineStyleEntries, sizeof(Graphics::lineStyleEntries)); +StringMap::Entry Graphics::lineJoinEntries[] = +{ + { "none", Graphics::LINE_JOIN_NONE }, + { "miter", Graphics::LINE_JOIN_MITER }, + { "bevel", Graphics::LINE_JOIN_BEVEL } +}; + +StringMap Graphics::lineJoins(Graphics::lineJoinEntries, sizeof(Graphics::lineJoinEntries)); + StringMap::Entry Graphics::pointStyleEntries[] = { { "smooth", Graphics::POINT_SMOOTH }, diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 28567a76b..fde7bb802 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -68,6 +68,14 @@ public: LINE_MAX_ENUM }; + enum LineJoin + { + LINE_JOIN_NONE = 1, + LINE_JOIN_MITER, + LINE_JOIN_BEVEL, + LINE_JOIN_MAX_ENUM + }; + enum PointStyle { POINT_ROUGH = 1, @@ -125,6 +133,9 @@ public: static bool getConstant(const char *in, LineStyle &out); static bool getConstant(LineStyle in, const char *&out); + static bool getConstant(const char *in, LineJoin &out); + static bool getConstant(LineJoin in, const char *&out); + static bool getConstant(const char *in, PointStyle &out); static bool getConstant(PointStyle in, const char *&out); @@ -148,6 +159,9 @@ private: static StringMap::Entry lineStyleEntries[]; static StringMap lineStyles; + static StringMap::Entry lineJoinEntries[]; + static StringMap lineJoins; + static StringMap::Entry pointStyleEntries[]; static StringMap pointStyles; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 9245924b8..0aff9f62d 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -24,6 +24,7 @@ #include "Graphics.h" #include "window/sdl/Window.h" +#include "Polyline.h" #include #include @@ -647,6 +648,11 @@ void Graphics::setLineStyle(Graphics::LineStyle style) lineStyle = style; } +void Graphics::setLineJoin(Graphics::LineJoin join) +{ + lineJoin = join; +} + float Graphics::getLineWidth() const { return lineWidth; @@ -657,6 +663,11 @@ Graphics::LineStyle Graphics::getLineStyle() const return lineStyle; } +Graphics::LineJoin Graphics::getLineJoin() const +{ + return lineJoin; +} + void Graphics::setPointSize(float size) { glPointSize((GLfloat)size); @@ -776,203 +787,26 @@ void Graphics::point(float x, float y) glEnd(); } -// Calculate line boundary points u1 and u2. Sketch: -// u1 -// -------------+---...___ -// | ```'''-- --- -// p- - - - - - q- - . _ _ | w/2 -// | ` ' ' r + -// -------------+---...___ | w/2 -// u2 ```'''-- --- -// -// u1 and u2 depend on four things: -// - the half line width w/2 -// - the previous line vertex p -// - the current line vertex q -// - the next line vertex r -// -// u1/u2 are the intersection points of the parallel lines to p-q and q-r, -// i.e. the point where -// -// (p + w/2 * n1) + mu * (q - p) = (q + w/2 * n2) + lambda * (r - q) (u1) -// (p - w/2 * n1) + mu * (q - p) = (q - w/2 * n2) + lambda * (r - q) (u2) -// -// with n1,n2 being the normals on the segments p-q and q-r: -// -// n1 = perp(q - p) / |q - p| -// n2 = perp(r - q) / |r - q| -// -// The intersection points can be calculated using cramers rule. -static void pushIntersectionPoints(Vector *vertices, Vector *overdraw, - int pos, int count, float hw, float overdraw_factor, - const Vector &p, const Vector &q, const Vector &r) -{ - // calculate line directions - Vector s = (q - p); - Vector t = (r - q); - - // calculate vertex displacement vectors - Vector n1 = s.getNormal(); - Vector n2 = t.getNormal(); - n1.normalize(); - n2.normalize(); - float det_norm = n1 ^ n2; // will be close to zero if the angle between the normals is sharp - n1 *= hw; - n2 *= hw; - - // lines parallel -> assume intersection at displacement points - if (fabs(det_norm) <= .03) - { - vertices[pos] = q - n2; - vertices[pos+1] = q + n2; - } - // real intersection -> calculate boundary intersection points with cramers rule - else - { - float det = s ^ t; - Vector d = n1 - n2; - Vector b = s - d; // s = q - p - Vector c = s + d; - float lambda = (b ^ t) / det; - float mu = (c ^ t) / det; - - // ordering for GL_TRIANGLE_STRIP - vertices[pos] = p + s*mu - n1; // u1 - vertices[pos+1] = p + s*lambda + n1; // u2 - } - - if (overdraw) - { - // displacement of the overdraw vertices - Vector x = (vertices[pos] - q) * overdraw_factor; - - overdraw[pos] = vertices[pos]; - overdraw[pos+1] = vertices[pos] + x; - - overdraw[2*count-pos-2] = vertices[pos+1]; - overdraw[2*count-pos-1] = vertices[pos+1] - x; - } -} - -// precondition: -// glEnableClientState(GL_VERTEX_ARRAY); -static void draw_overdraw(Vector *overdraw, size_t count, float pixel_size, bool looping) -{ - // if not looping, the outer overdraw vertices need to be displaced - // to cover the line endings, i.e.: - // +- - - - //- - + +- - - - - //- - - + - // +-------//-----+ : +-------//-----+ : - // | core // line | --> : | core // line | : - // +-----//-------+ : +-----//-------+ : - // +- - //- - - - + +- - - //- - - - - + - if (!looping) - { - Vector s = overdraw[1] - overdraw[3]; - s.normalize(); - s *= pixel_size; - overdraw[1] += s; - overdraw[2*count-1] += s; - - Vector t = overdraw[count-1] - overdraw[count-3]; - t.normalize(); - t *= pixel_size; - overdraw[count-1] += t; - overdraw[count+1] += t; - - // we need to draw two more triangles to close the - // overdraw at the line start. - overdraw[2*count] = overdraw[0]; - overdraw[2*count+1] = overdraw[1]; - } - - // prepare colors: - // even indices in overdraw* point to inner vertices => alpha = current-alpha, - // odd indices point to outer vertices => alpha = 0. - Color c = gl.getColor(); - - Color *colors = new Color[2*count+2]; - for (size_t i = 0; i < 2*count+2; ++i) - { - colors[i] = c; - // avoids branching. equiv to if (i%2 == 1) colors[i].a = 0; - colors[i].a *= GLubyte(i % 2 == 0); - } - - // draw faded out line halos - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors); - glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)overdraw); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 2*count + 2 * int(!looping)); - glDisableClientState(GL_COLOR_ARRAY); - // "if GL_COLOR_ARRAY is enabled, the value of the current color is - // undefined after glDrawArrays executes" - - gl.setColor(c); - - delete[] colors; -} - void Graphics::polyline(const float *coords, size_t count) { - Vector *vertices = new Vector[count]; // two vertices for every line end-point - Vector *overdraw = NULL; - - Vector p,q,r; - bool looping = (coords[0] == coords[count-2]) && (coords[1] == coords[count-1]); - - float halfwidth = lineWidth/2.f; - float pixel_size = pixel_size_stack.back(); - float overdraw_factor = .0f; - - if (lineStyle == LINE_SMOOTH) + if (lineJoin == LINE_JOIN_NONE) { - overdraw = new Vector[2*count+2]; - overdraw_factor = pixel_size / halfwidth; - halfwidth = std::max(.0f, halfwidth - .25f*pixel_size); + NoneJoinPolyline line; + line.render(coords, count, lineWidth * .5f, pixel_size_stack.back(), lineStyle == LINE_SMOOTH); + line.draw(); } - - // get line vertex boundaries - // if not looping, extend the line at the beginning, else use last point as `p' - r = Vector(coords[0], coords[1]); - if (!looping) - q = r * 2 - Vector(coords[2], coords[3]); - else - q = Vector(coords[count-4], coords[count-3]); - - for (size_t i = 0; i+3 < count; i += 2) + else if (lineJoin == LINE_JOIN_BEVEL) { - p = q; - q = r; - r = Vector(coords[i+2], coords[i+3]); - pushIntersectionPoints(vertices, overdraw, i, count, halfwidth, overdraw_factor, p,q,r); + BevelJoinPolyline line; + line.render(coords, count, lineWidth * .5f, pixel_size_stack.back(), lineStyle == LINE_SMOOTH); + line.draw(); + } + else // LINE_JOIN_MITER + { + MiterJoinPolyline line; + line.render(coords, count, lineWidth * .5f, pixel_size_stack.back(), lineStyle == LINE_SMOOTH); + line.draw(); } - - // if not looping, extend the line at the end, else use first point as `r' - p = q; - q = r; - if (!looping) - r += q - p; - else - r = Vector(coords[2], coords[3]); - pushIntersectionPoints(vertices, overdraw, count-2, count, halfwidth, overdraw_factor, p,q,r); - // end get line vertex boundaries - - // draw the core line - gl.bindTexture(0); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)vertices); - glDrawArrays(GL_TRIANGLE_STRIP, 0, count); - - // draw the line halo (antialiasing) - if (lineStyle == LINE_SMOOTH) - draw_overdraw(overdraw, count, pixel_size, looping); - - glDisableClientState(GL_VERTEX_ARRAY); - - // cleanup - delete[] vertices; - if (lineStyle == LINE_SMOOTH) - delete[] overdraw; } void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h) diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index b050da036..78bd06cac 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -69,6 +69,7 @@ struct DisplayState // Line. Graphics::LineStyle lineStyle; + Graphics::LineJoin lineJoin; // Point. float pointSize; @@ -88,6 +89,7 @@ struct DisplayState backgroundColor.set(0, 0, 0, 255); blendMode = Graphics::BLEND_ALPHA; lineStyle = Graphics::LINE_SMOOTH; + lineJoin = Graphics::LINE_JOIN_MITER; pointSize = 1.0f; pointStyle = Graphics::POINT_SMOOTH; scissor = false; @@ -299,6 +301,12 @@ public: **/ void setLineStyle(LineStyle style); + /** + * Sets the line style. + * @param style LINE_ROUGH or LINE_SMOOTH. + **/ + void setLineJoin(LineJoin style); + /** * Gets the line width. **/ @@ -309,6 +317,11 @@ public: **/ LineStyle getLineStyle() const; + /** + * Gets the line style. + **/ + LineJoin getLineJoin() const; + /** * Sets the size of points. **/ @@ -455,6 +468,7 @@ private: std::vector pixel_size_stack; // stores current size of a pixel (needed for line drawing) LineStyle lineStyle; + LineJoin lineJoin; float lineWidth; GLint matrixLimit; GLint userMatrices; diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 79e448a52..aff443fbd 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -843,6 +843,17 @@ int w_setLineStyle(lua_State *L) return 0; } +int w_setLineJoin(lua_State *L) +{ + Graphics::LineJoin join; + const char *str = luaL_checkstring(L, 1); + if (!Graphics::getConstant(str, join)) + return luaL_error(L, "Invalid line join: %s", str); + + instance->setLineJoin(join); + return 0; +} + int w_getLineWidth(lua_State *L) { lua_pushnumber(L, instance->getLineWidth()); @@ -859,6 +870,16 @@ int w_getLineStyle(lua_State *L) return 1; } +int w_getLineJoin(lua_State *L) +{ + Graphics::LineJoin join = instance->getLineJoin(); + const char *str; + if (!Graphics::getConstant(join, str)) + return luaL_error(L, "Unknown line join"); + lua_pushstring(L, str); + return 1; +} + int w_setPointSize(lua_State *L) { float size = (float)luaL_checknumber(L, 1); @@ -1453,8 +1474,10 @@ static const luaL_Reg functions[] = { "getDefaultMipmapFilter", w_getDefaultMipmapFilter }, { "setLineWidth", w_setLineWidth }, { "setLineStyle", w_setLineStyle }, + { "setLineJoin", w_setLineJoin }, { "getLineWidth", w_getLineWidth }, { "getLineStyle", w_getLineStyle }, + { "getLineJoin", w_getLineJoin }, { "setPointSize", w_setPointSize }, { "setPointStyle", w_setPointStyle }, { "getPointSize", w_getPointSize }, diff --git a/src/modules/graphics/opengl/wrap_Graphics.h b/src/modules/graphics/opengl/wrap_Graphics.h index c11136f43..5bc069bff 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.h +++ b/src/modules/graphics/opengl/wrap_Graphics.h @@ -74,8 +74,10 @@ int w_setDefaultMipmapFilter(lua_State *L); int w_getDefaultMipmapFilter(lua_State *L); int w_setLineWidth(lua_State *L); int w_setLineStyle(lua_State *L); +int w_setLineJoin(lua_State *L); int w_getLineWidth(lua_State *L); int w_getLineStyle(lua_State *L); +int w_getLineJoin(lua_State *L); int w_setPointSize(lua_State *L); int w_setPointStyle(lua_State *L); int w_getPointSize(lua_State *L);