gfxrenderer_tools.hpp

Back to Renderer

pastel/gfx/

#ifndef PASTELGFX_GFXRENDERERTOOLS_HPP
#define PASTELGFX_GFXRENDERERTOOLS_HPP

#include "pastel/gfx/gfxrenderer_tools.h"

#include "pastel/sys/math_functions.h"

#include "pastel/math/coordinate/coordinates.h"
#include "pastel/math/affine/affine_transformation.h"

namespace Pastel
{

    template <typename Type>
    void drawPoint(
        const GfxRenderer<Type>& renderer,
        const Vector2& point)
    {
        renderer.drawPoint(point);
    }

    template <typename Type>
    void drawTriangle(
        const GfxRenderer<Type>& renderer,
        const Triangle2& triangle)
    {
        if (renderer.filled())
        {
            renderer.drawTriangle(triangle);
        }
        else
        {
            renderer.drawSegment(Segment2(triangle[0], triangle[1]));
            renderer.drawSegment(Segment2(triangle[1], triangle[2]));
            renderer.drawSegment(Segment2(triangle[2], triangle[0]));
        }
    }

    template <typename Type>
    void drawTriangle(
        const GfxRenderer<Type>& renderer,
        const Triangle2& triangle,
        const Tuple<Type, 3>& colorTriangle)
    {
        if (renderer.filled())
        {
            renderer.drawTriangle(triangle, colorTriangle);
        }
        else
        {
            renderer.drawSegment(Segment2(triangle[0], triangle[1]));
            renderer.drawSegment(Segment2(triangle[1], triangle[2]));
            renderer.drawSegment(Segment2(triangle[2], triangle[0]));
        }
    }

    template <typename Type>
    void drawTriangle(
        const GfxRenderer<Type>& renderer,
        const Triangle2& triangle,
        const Triangle2& textureTriangle)
    {
        if (renderer.filled())
        {
            renderer.drawTriangle(triangle, textureTriangle);
        }
        else
        {
            renderer.drawSegment(Segment2(triangle[0], triangle[1]));
            renderer.drawSegment(Segment2(triangle[1], triangle[2]));
            renderer.drawSegment(Segment2(triangle[2], triangle[0]));
        }
    }

    template <typename Type>
    void drawSegment(
        const GfxRenderer<Type>& renderer,
        const Segment2& segment)
    {
        renderer.drawSegment(segment);
    }

    template <typename Type>
    void drawFatSegment(
        const GfxRenderer<Type>& renderer,
        const Segment2& segment,
        real startRadius,
        real endRadius)
    {
        ENSURE_OP(startRadius, >=, 0);
        ENSURE_OP(endRadius, >=, 0);

        Vector2 delta = segment.end() - segment.start();
        real normDelta = norm(delta);

        if (normDelta == 0)
        {
            return;
        }

        Vector2 normal = cross(delta) / normDelta;

        const Vector2 startLeft = segment.start() + normal * startRadius;
        const Vector2 startRight = segment.start() - normal * startRadius;
        const Vector2 endLeft = segment.end() + normal * endRadius;
        const Vector2 endRight = segment.end() - normal * endRadius;

        if (renderer.filled())
        {
            renderer.drawTriangle(Triangle2(startLeft, startRight, endLeft));
            renderer.drawTriangle(Triangle2(startRight, endRight, endLeft));
        }
        else
        {
            renderer.drawSegment(Segment2(startLeft, startRight));
            renderer.drawSegment(Segment2(startRight, endRight));
            renderer.drawSegment(Segment2(endRight, endLeft));
            renderer.drawSegment(Segment2(endLeft, startLeft));
        }
    }

    template <typename Type>
    void drawCircle(
        const GfxRenderer<Type>& renderer,
        const Sphere2& circle,
        integer segments)
    {
        ENSURE_OP(segments, >=, 0);

        if (segments <= 1)
        {
            return;
        }

        const real angleAdd = (2 * constantPi<real>()) / segments;

        if (!renderer.filled())
        {
            real angle = 0;
            real nextAngle = angleAdd;
            for (integer i = 0;i < segments;++i)
            {
                drawSegment(
                    renderer,
                    Segment2(
                    circle.position() +
                    sphericalToCartesian(Vector2(circle.radius(), angle)),
                    circle.position() +
                    sphericalToCartesian(Vector2(circle.radius(), nextAngle))));

                angle = nextAngle;
                nextAngle += angleAdd;
            }
        }
        else
        {
            real angle = 0;
            real nextAngle = angleAdd;
            for (integer i = 0;i < segments;++i)
            {
                drawTriangle(
                    renderer,
                    Triangle2(
                    circle.position(),
                    circle.position() +
                    sphericalToCartesian(Vector2(circle.radius(), angle)),
                    circle.position() +
                    sphericalToCartesian(Vector2(circle.radius(), nextAngle))));

                angle = nextAngle;
                nextAngle += angleAdd;
            }
        }
    }

    template <typename Type>
    void drawBox(
        const GfxRenderer<Type>& renderer,
        const AlignedBox2& alignedBox)
    {
        Box2 box(
            linear(alignedBox.min(), alignedBox.max(), 0.5),

            (alignedBox.max() - alignedBox.min()) * 0.5,
            matrix2x2<real>(Vector2(1, 0), Vector2(0, 1)));

        drawBox(renderer, box);
    }

    template <typename Type>
    void drawBox(
        const GfxRenderer<Type>& renderer,
        const AlignedBox2& alignedBox,
        const Tuple<Vector2, 4>& textureQuad)
    {
        Box2 box(
            linear(alignedBox.min(), alignedBox.max(), 0.5),

            (alignedBox.max() - alignedBox.min()) * 0.5,
            matrix2x2<real>(Vector2(1, 0), Vector2(0, 1)));

        drawBox(renderer, box, textureQuad);
    }

    template <typename Type>
    void drawBox(
        const GfxRenderer<Type>& renderer,
        const Box2& box)
    {
        const Vector2& center = box.position();
        const Vector2 x = box.rotation().cColumn(0) * box.width()[0];
        const Vector2 y = box.rotation().cColumn(1) * box.width()[1];

        Vector2 leftBottom = center - x - y;
        Vector2 rightBottom = center + x - y;
        Vector2 rightTop = center + x + y;
        Vector2 leftTop = center - x + y;

        if (!renderer.filled())
        {
            drawSegment(
                renderer,
                Segment2(leftBottom, rightBottom));
            drawSegment(
                renderer,
                Segment2(rightBottom, rightTop));
            drawSegment(
                renderer,
                Segment2(rightTop, leftTop));
            drawSegment(
                renderer,
                Segment2(leftTop, leftBottom));
        }
        else
        {
            drawTriangle(
                renderer,
                Triangle2(leftBottom, rightBottom, leftTop));
            drawTriangle(
                renderer,
                Triangle2(rightBottom, rightTop, leftTop));
        }
    }

    template <typename Type>
    void drawBox(
        const GfxRenderer<Type>& renderer,
        const Box2& box,
        const Tuple<Vector2, 4>& textureQuad)
    {
        const Vector2& center = box.position();
        const Vector2 x = box.rotation().cColumn(0) * box.width()[0];
        const Vector2 y = box.rotation().cColumn(1) * box.width()[1];

        Vector2 leftBottom = center - x - y;
        Vector2 rightBottom = center + x - y;
        Vector2 rightTop = center + x + y;
        Vector2 leftTop = center - x + y;

        if (!renderer.filled())
        {
            drawSegment(
                renderer,
                Segment2(leftBottom, rightBottom));
            drawSegment(
                renderer,
                Segment2(rightBottom, rightTop));
            drawSegment(
                renderer,
                Segment2(rightTop, leftTop));
            drawSegment(
                renderer,
                Segment2(leftTop, leftBottom));
        }
        else
        {
            drawTriangle(
                renderer,
                Triangle2(leftBottom, rightBottom, leftTop),
                makeTuple(textureQuad[0], textureQuad[1], textureQuad[3]));
            drawTriangle(
                renderer,
                Triangle2(rightBottom, rightTop, leftTop),
                makeTuple(textureQuad[1], textureQuad[2], textureQuad[3]));
        }
    }

    template <typename Type>
    void drawArrow(
        const GfxRenderer<Type>& renderer,
        const Segment2& segment,
        real radius)
    {
        ENSURE_OP(radius, >=, 0);

        Vector2 delta = segment.end() - segment.start();
        real normDelta = norm(delta);

        if (normDelta < 2 * radius)
        {
            return;
        }

        Vector2 tangent = delta / normDelta;
        Vector2 normal = cross(tangent);

        const Vector2 endLeft = segment.end() + normal * radius - tangent * radius * 2;
        const Vector2 endRight = segment.end() - normal * radius - tangent * radius * 2;

        drawFatSegment(renderer, Segment2(segment.start(), segment.end() - tangent * radius * 2),
            radius * 0.15, radius * 0.15);
        drawTriangle(renderer, Triangle2(segment.end(), endLeft, endRight));
    }

    template <typename Type>
    void drawConvexPolygon(
        const GfxRenderer<Type>& renderer,
        const std::vector<Vector2>& convexPolygon)
    {
        integer points = convexPolygon.size();

        if (!renderer.filled())
        {
            for (integer i = 0;i < points - 1;++i)
            {
                drawSegment(renderer, Segment2(convexPolygon[i], convexPolygon[i + 1]));
            }
            drawSegment(renderer, Segment2(convexPolygon[points - 1], convexPolygon[0]));
        }
        else
        {
            for (integer i = 1;i < points - 1;++i)
            {
                drawTriangle(renderer, Triangle2(convexPolygon[0], convexPolygon[i], convexPolygon[i + 1]));
            }
        }
    }

    template <typename Type>
    void concentrate(
        GfxRenderer<Type>& renderer,
        const AlignedBox2& region)
    {
        AlignedBox2 viewWindow = region;

        Vector2 cameraCenter = 
            midpoint(viewWindow.min(), viewWindow.max());

        viewWindow -= cameraCenter;

        viewWindow.min() -= viewWindow.extent() * 0.05;
        viewWindow.max() += viewWindow.extent() * 0.05;
        Vector2 viewExtent = viewWindow.extent();

        real aspectRatio = (real)4 / 3;

        if (viewExtent.x() < aspectRatio * viewExtent.y())
        {
            real xExtentDelta =
                aspectRatio * viewExtent.y() - viewExtent.x();

            viewWindow.min().x() -= xExtentDelta / 2;
            viewWindow.max().x() += xExtentDelta / 2;
        }
        else
        {
            real yExtentDelta =
                viewExtent.x() - aspectRatio * viewExtent.y();

            viewWindow.min().y() -= yExtentDelta / 2;
            viewWindow.max().y() += yExtentDelta / 2;
        }

        renderer.setViewTransformation(
            affineTranslation(cameraCenter));
        renderer.setViewWindow(viewWindow);
    }

}

#endif