diff options
Diffstat (limited to 'vcl/opengl/RenderList.cxx')
-rw-r--r-- | vcl/opengl/RenderList.cxx | 314 |
1 files changed, 240 insertions, 74 deletions
diff --git a/vcl/opengl/RenderList.cxx b/vcl/opengl/RenderList.cxx index aee6a0411d77..5c59002f95ff 100644 --- a/vcl/opengl/RenderList.cxx +++ b/vcl/opengl/RenderList.cxx @@ -10,6 +10,7 @@ #include "opengl/RenderList.hxx" #include "opengl/VertexUtils.hxx" +#include "opengl/LineRenderUtils.hxx" #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/polygon/b2dpolygontriangulator.hxx> @@ -19,22 +20,177 @@ namespace { -inline void lclAddLineSegmentVertices(RenderParameters& rRenderParameter, GLfloat fX1, GLfloat fY1, GLfloat fX2, GLfloat fY2, - SalColor nColor, double fTransparency) +/** Append vertices for the polyline + * + * OpenGL polyline drawing algorithm inspired by: + * - http://mattdesl.svbtle.com/drawing-lines-is-hard + * - https://www.mapbox.com/blog/drawing-antialiased-lines/ + * - https://cesiumjs.org/2013/04/22/Robust-Polyline-Rendering-with-WebGL/ + * - http://artgrammer.blogspot.si/2011/05/drawing-nearly-perfect-2d-line-segments.html + * - http://artgrammer.blogspot.si/2011/07/drawing-polylines-by-tessellation.html + * + */ +void appendPolyLine(vcl::LineBuilder& rBuilder, const basegfx::B2DPolygon& rPolygon, + basegfx::B2DLineJoin eLineJoin, css::drawing::LineCap eLineCap, + double fMiterMinimumAngle) { - glm::vec2 aPoint1(fX1, fY1); - glm::vec2 aPoint2(fX2, fY2); + sal_uInt32 nPoints = rPolygon.count(); + bool bClosed = rPolygon.isClosed(); + + if (nPoints == 2 || eLineJoin == basegfx::B2DLineJoin::NONE) + { + // If line joint is NONE or a simple line with 2 points, draw the polyline + // each line segment separatly. + + for (sal_uInt32 i = 0; i < (bClosed ? nPoints : nPoints - 1); ++i) + { + sal_uInt32 index1 = (i + 0) % nPoints; // loop indices - important when polyline is closed + sal_uInt32 index2 = (i + 1) % nPoints; + + glm::vec2 aPoint1(rPolygon.getB2DPoint(index1).getX(), rPolygon.getB2DPoint(index1).getY()); + glm::vec2 aPoint2(rPolygon.getB2DPoint(index2).getX(), rPolygon.getB2DPoint(index2).getY()); + + rBuilder.appendLine(aPoint1, aPoint2); + } + } + else if (nPoints > 2) + { + int i = 0; + int lastPoint = int(nPoints); - glm::vec2 aLineVector = vcl::vertex::normalize(aPoint2 - aPoint1); - glm::vec2 aNormal = glm::vec2(-aLineVector.y, aLineVector.x); + glm::vec2 p0(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY()); + glm::vec2 p1(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY()); + glm::vec2 p2(rPolygon.getB2DPoint(1).getX(), rPolygon.getB2DPoint(1).getY()); - vcl::vertex::addLinePointFirst(rRenderParameter.maVertices, rRenderParameter.maExtrusionVectors, - aPoint1, aNormal, 1.0f); - vcl::vertex::addLinePointNext (rRenderParameter.maVertices, rRenderParameter.maExtrusionVectors, - aPoint1, aNormal, 1.0f, - aPoint2, aNormal, 1.0f); + glm::vec2 nextLineVector; + glm::vec2 previousLineVector; + glm::vec2 normal; // perpendicular to the line vector - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nColor, fTransparency); + nextLineVector = vcl::vertex::normalize(p2 - p1); + + if (!bClosed) + { + normal = glm::vec2(-nextLineVector.y, nextLineVector.x); // make perpendicular + rBuilder.appendAndConnectLinePoint(p1, normal, 1.0f); + + i++; // first point done already + lastPoint--; // last point will be calculated separatly from the loop + + p0 = p1; + previousLineVector = nextLineVector; + } + else + { + lastPoint++; // we need to connect last point to first point so one more line segment to calculate + previousLineVector = vcl::vertex::normalize(p1 - p0); + } + + for (; i < lastPoint; ++i) + { + int index1 = (i + 0) % nPoints; // loop indices - important when polyline is closed + int index2 = (i + 1) % nPoints; + + p1 = glm::vec2(rPolygon.getB2DPoint(index1).getX(), rPolygon.getB2DPoint(index1).getY()); + p2 = glm::vec2(rPolygon.getB2DPoint(index2).getX(), rPolygon.getB2DPoint(index2).getY()); + + if (p1 == p2) // skip equal points, normals could div-by-0 + continue; + + nextLineVector = vcl::vertex::normalize(p2 - p1); + + if (eLineJoin == basegfx::B2DLineJoin::Miter) + { + if (vcl::vertex::lineVectorAngle(previousLineVector, nextLineVector) < fMiterMinimumAngle) + rBuilder.appendBevelJoint(p1, previousLineVector, nextLineVector); + else + rBuilder.appendMiterJoint(p1, previousLineVector, nextLineVector); + } + else if (eLineJoin == basegfx::B2DLineJoin::Bevel) + { + rBuilder.appendBevelJoint(p1, previousLineVector, nextLineVector); + } + else if (eLineJoin == basegfx::B2DLineJoin::Round) + { + rBuilder.appendRoundJoint(p1, previousLineVector, nextLineVector); + } + p0 = p1; + previousLineVector = nextLineVector; + } + + if (!bClosed) + { + // Create vertices for the last point. There is no line join so just + // use the last line segment normal as the extrusion vector. + p1 = glm::vec2(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY()); + normal = glm::vec2(-previousLineVector.y, previousLineVector.x); + rBuilder.appendAndConnectLinePoint(p1, normal, 1.0f); + } + } + + if (!bClosed && nPoints >= 2 && (eLineCap == css::drawing::LineCap_ROUND || eLineCap == css::drawing::LineCap_SQUARE)) + { + glm::vec2 aBeginCapPoint1(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY()); + glm::vec2 aBeginCapPoint2(rPolygon.getB2DPoint(1).getX(), rPolygon.getB2DPoint(1).getY()); + + glm::vec2 aEndCapPoint1(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY()); + glm::vec2 aEndCapPoint2(rPolygon.getB2DPoint(nPoints - 2).getX(), rPolygon.getB2DPoint(nPoints - 2).getY()); + + if (eLineCap == css::drawing::LineCap_ROUND) + { + rBuilder.appendRoundLineCapVertices(aBeginCapPoint1, aBeginCapPoint2); + rBuilder.appendRoundLineCapVertices(aEndCapPoint1, aEndCapPoint2); + } + else if (eLineCap == css::drawing::LineCap_SQUARE) + { + rBuilder.appendSquareLineCapVertices(aBeginCapPoint1, aBeginCapPoint2); + rBuilder.appendSquareLineCapVertices(aEndCapPoint1, aEndCapPoint2); + } + } +} + +inline void appendTrapezoid(std::vector<Vertex>& rVertices, std::vector<GLuint>& rIndices, + GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, + GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4, + SalColor nColor, GLfloat fTransparency) +{ + GLubyte nR, nG, nB, nA; + vcl::vertex::createColor(nColor, fTransparency, nR, nG, nB, nA); + + GLuint zero = rVertices.size(); + + rVertices.insert(rVertices.end(), { + Vertex{glm::vec2{x1, y1}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}}, + Vertex{glm::vec2{x2, y2}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}}, + Vertex{glm::vec2{x3, y3}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}}, + Vertex{glm::vec2{x4, y4}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}}, + }); + + rIndices.insert(rIndices.end(), { + zero + 0, zero + 1, zero + 2, + zero + 2, zero + 1, zero + 3 + }); +} + +void appendRectangle(std::vector<Vertex>& rVertices, std::vector<GLuint>& rIndices, + GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, + SalColor nColor, GLfloat fTransparency) +{ + GLubyte nR, nG, nB, nA; + vcl::vertex::createColor(nColor, fTransparency, nR, nG, nB, nA); + + GLuint zero = rVertices.size(); + + rVertices.insert(rVertices.end(), { + Vertex{glm::vec2{x1, y1}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}}, + Vertex{glm::vec2{x2, y1}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}}, + Vertex{glm::vec2{x1, y2}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}}, + Vertex{glm::vec2{x2, y2}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}}, + }); + + rIndices.insert(rIndices.end(), { + zero + 0, zero + 1, zero + 2, + zero + 2, zero + 1, zero + 3 + }); } } // end anonymous namespace @@ -47,9 +203,8 @@ void RenderList::addDrawPixel(long nX, long nY, SalColor nColor) checkOverlapping(basegfx::B2DRange(nX, nY, nX, nY)); RenderParameters& rRenderParameter = maRenderEntries.back().maTriangleParameters; - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, nX - 0.5f, nY - 0.5f, nX + 0.5f, nY + 0.5f); - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nColor, 0.0f); - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + nX - 0.5f, nY - 0.5f, nX + 0.5f, nY + 0.5f, nColor, 0.0f); } void RenderList::addDrawRectangle(long nX, long nY, long nWidth, long nHeight, double fTransparency, @@ -72,46 +227,32 @@ void RenderList::addDrawRectangle(long nX, long nY, long nWidth, long nHeight, d // Draw rectangle stroke with line color if (nLineColor != SALCOLOR_NONE) { - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, fX1 - 0.5f, fY1 - 0.5f, fX1 + 0.5f, fY2 + 0.5f); - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, fX1 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY1 + 0.5f); - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, fX2 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY2 + 0.5f); - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, fX1 - 0.5f, fY2 - 0.5f, fX2 + 0.5f, fY2 + 0.5f); - - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nLineColor, fTransparency); - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nLineColor, fTransparency); - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nLineColor, fTransparency); - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nLineColor, fTransparency); - - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + fX1 - 0.5f, fY1 - 0.5f, fX1 + 0.5f, fY2 + 0.5f, nLineColor, fTransparency); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + fX1 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY1 + 0.5f, nLineColor, fTransparency); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + fX2 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY2 + 0.5f, nLineColor, fTransparency); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + fX1 - 0.5f, fY2 - 0.5f, fX2 + 0.5f, fY2 + 0.5f, nLineColor, fTransparency); } if (nFillColor != SALCOLOR_NONE) { if (nLineColor == SALCOLOR_NONE) { - // Draw rectangle stroke with fill color - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, fX1 - 0.5f, fY1 - 0.5f, fX1 + 0.5f, fY2 + 0.5f); - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, fX1 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY1 + 0.5f); - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, fX2 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY2 + 0.5f); - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, fX1 - 0.5f, fY2 - 0.5f, fX2 + 0.5f, fY2 + 0.5f); - - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nFillColor, fTransparency); - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nFillColor, fTransparency); - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nFillColor, fTransparency); - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nFillColor, fTransparency); - - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + fX1 - 0.5f, fY1 - 0.5f, fX1 + 0.5f, fY2 + 0.5f, nFillColor, fTransparency); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + fX1 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY1 + 0.5f, nFillColor, fTransparency); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + fX2 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY2 + 0.5f, nFillColor, fTransparency); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + fX1 - 0.5f, fY2 - 0.5f, fX2 + 0.5f, fY2 + 0.5f, nFillColor, fTransparency); } // Draw rectangle fill with fill color - vcl::vertex::addRectangle<GL_TRIANGLES>(rRenderParameter.maVertices, fX1 + 0.5f, fY1 + 0.5f, fX2 - 0.5f, fY2 - 0.5f); - vcl::vertex::addQuadColors<GL_TRIANGLES>(rRenderParameter.maColors, nFillColor, fTransparency); - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rRenderParameter.maExtrusionVectors); + appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices, + fX1 + 0.5f, fY1 + 0.5f, fX2 - 0.5f, fY2 - 0.5f, nFillColor, fTransparency); } } @@ -122,9 +263,13 @@ void RenderList::addDrawLine(long nX1, long nY1, long nX2, long nY2, SalColor nL checkOverlapping(basegfx::B2DRange(nX1, nY1, nX2, nY2)); - RenderParameters& rRenderParameter = bUseAA ? maRenderEntries.back().maLineAAParameters : - maRenderEntries.back().maLineParameters; - lclAddLineSegmentVertices(rRenderParameter, nX1, nY1, nX2, nY2, nLineColor, 0.0f); + RenderParameters& rRenderParameter = maRenderEntries.back().maLineParameters; + + glm::vec2 aPoint1(nX1, nY1); + glm::vec2 aPoint2(nX2, nY2); + + vcl::LineBuilder aBuilder(rRenderParameter.maVertices, rRenderParameter.maIndices, nLineColor, 0.0f, 1.0f, bUseAA); + aBuilder.appendLine(aPoint1, aPoint2); } void RenderList::addDrawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency, @@ -139,16 +284,13 @@ void RenderList::addDrawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon, checkOverlapping(rPolyPolygon.getB2DRange()); - RenderParameters& rLineParameter = maRenderEntries.back().maLineParameters; - RenderParameters& rLineAAParameter = maRenderEntries.back().maLineAAParameters; + RenderParameters& rLineRenderParameter = maRenderEntries.back().maLineParameters; + RenderParameters& rTriangleRenderParameter = maRenderEntries.back().maTriangleParameters; if (nFillColor != SALCOLOR_NONE) { - RenderParameters& rTriangleParameter = maRenderEntries.back().maTriangleParameters; - - const basegfx::B2DPolyPolygon& aSimplePolyPolygon = ::basegfx::tools::solveCrossovers(rPolyPolygon); basegfx::B2DTrapezoidVector aTrapezoidVector; - basegfx::tools::trapezoidSubdivide(aTrapezoidVector, aSimplePolyPolygon); + basegfx::tools::trapezoidSubdivide(aTrapezoidVector, rPolyPolygon); if (!aTrapezoidVector.empty()) { @@ -162,24 +304,18 @@ void RenderList::addDrawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon, GLfloat bottomX2 = rTrapezoid.getBottomXRight(); GLfloat bottomY = rTrapezoid.getBottomY(); - vcl::vertex::addTrapezoid<GL_TRIANGLES>(rTriangleParameter.maVertices, - topX1, topY, - topX2, topY, - bottomX1, bottomY, - bottomX2, bottomY); - vcl::vertex::addQuadColors<GL_TRIANGLES>(rTriangleParameter.maColors, nFillColor, fTransparency); - vcl::vertex::addQuadEmptyExtrusionVectors<GL_TRIANGLES>(rTriangleParameter.maExtrusionVectors); - + appendTrapezoid(rTriangleRenderParameter.maVertices, rTriangleRenderParameter.maIndices, + topX1, topY, topX2, topY, + bottomX1, bottomY, bottomX2, bottomY, + nFillColor, fTransparency); if (bUseAA) { - lclAddLineSegmentVertices(rLineAAParameter, topX1, topY, topX2, topY, - nFillColor, fTransparency); - lclAddLineSegmentVertices(rLineAAParameter, topX2, topY, bottomX2, bottomY, - nFillColor, fTransparency); - lclAddLineSegmentVertices(rLineAAParameter, bottomX2, bottomY, bottomX1, bottomY, - nFillColor, fTransparency); - lclAddLineSegmentVertices(rLineAAParameter, bottomX1, bottomY, topX1, topY, - nFillColor, fTransparency); + vcl::LineBuilder aBuilder(rLineRenderParameter.maVertices, rLineRenderParameter.maIndices, + nFillColor, fTransparency, 1.0f, true); + aBuilder.appendLine(glm::vec2(topX1, topY), glm::vec2(topX2, topY)); + aBuilder.appendLine(glm::vec2(topX2, topY), glm::vec2(bottomX2, bottomY)); + aBuilder.appendLine(glm::vec2(bottomX2, bottomY), glm::vec2(bottomX1, bottomY)); + aBuilder.appendLine(glm::vec2(bottomX1, bottomY), glm::vec2(topX1, topY)); } } } @@ -187,7 +323,8 @@ void RenderList::addDrawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon, if (nLineColor != SALCOLOR_NONE && nLineColor != nFillColor) { - RenderParameters& rParameter = bUseAA ? rLineAAParameter : rLineParameter; + vcl::LineBuilder aBuilder(rLineRenderParameter.maVertices, rLineRenderParameter.maIndices, + nLineColor, fTransparency, 1.0f, bUseAA); for (const basegfx::B2DPolygon& rPolygon : rPolyPolygon) { @@ -210,7 +347,7 @@ void RenderList::addDrawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon, x2 = aPolygon.getB2DPoint(index2).getX(); y2 = aPolygon.getB2DPoint(index2).getY(); - lclAddLineSegmentVertices(rParameter, x1, y1, x2, y2, nLineColor, fTransparency); + aBuilder.appendLine(glm::vec2(x1, y1), glm::vec2(x2, y2)); } } } @@ -241,4 +378,33 @@ bool RenderList::addDrawTextureWithMaskColor(OpenGLTexture& rTexture, SalColor n return true; } +void RenderList::addDrawPolyLine(const basegfx::B2DPolygon& rPolygon, double fTransparency, + const basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin eLineJoin, + css::drawing::LineCap eLineCap, double fMiterMinimumAngle, + SalColor nLineColor, bool bUseAA) +{ + if (rPolygon.count() <= 1) + return; + if (nLineColor == SALCOLOR_NONE) + return; + if (fTransparency == 1.0) + return; + + const bool bIsHairline = (rLineWidth.getX() == rLineWidth.getY()) && (rLineWidth.getX() <= 1.2); + const float fLineWidth = bIsHairline ? 1.0f : rLineWidth.getX(); + + basegfx::B2DPolygon aPolygon(rPolygon); + if (rPolygon.areControlPointsUsed()) + aPolygon = rPolygon.getDefaultAdaptiveSubdivision(); + + checkOverlapping(aPolygon.getB2DRange()); + + RenderParameters& rParameter = maRenderEntries.back().maLineParameters; + + vcl::LineBuilder aBuilder(rParameter.maVertices, rParameter.maIndices, + nLineColor, fTransparency, fLineWidth, bUseAA); + + appendPolyLine(aBuilder, aPolygon, eLineJoin, eLineCap, fMiterMinimumAngle); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |