aboutsummaryrefslogtreecommitdiff
path: root/src/o3d/polygon.hpp
blob: 5ede9103f849444d126ceb7ee560a8d9697a5aa2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#ifndef O3D_POLYGON_HPP
#define O3D_POLYGON_HPP

#include <cstddef>
#include <array>
#include <type_traits>
#include <tuple>
#include "math/vector.hpp"
#include "math/utils.hpp"
#include "o3d/deriv_vertex.hpp"
#include "o3d/tri_deriv.hpp"

namespace engine::o3d::polygon {

template<typename T>
concept PolygonVectorTypeConcept = engine::math::VectorTypeConcept<T> && (std::is_same_v<T, engine::math::Vector3> || std::is_same_v<T, engine::math::Vector4>);

template<PolygonVectorTypeConcept VectorType, std::size_t max_points_count> struct Polygon;

template<engine::math::VectorTypeConcept VectorType>
constexpr Polygon<VectorType, 3> from_triangle_derived(const engine::o3d::TriangleDerived<VectorType>& triangle_derived);

template<PolygonVectorTypeConcept VectorType, std::size_t max_points_count, engine::math::vector_coords::VectorCoord coord_id, bool keep_geq>
constexpr Polygon<VectorType, max_points_count + 1> clip_aligned(float boundary, const Polygon<VectorType, max_points_count>& polygon);

template<PolygonVectorTypeConcept VectorType, std::size_t max_points_count>
struct Polygon {
    std::size_t points_count;
    std::array<engine::o3d::DerivedVertex<VectorType>, max_points_count> points;

    constexpr Polygon() : points_count { 0 } {}
    constexpr Polygon(std::size_t points_count, std::array<engine::o3d::DerivedVertex<VectorType>, max_points_count> points)
        : points_count { points_count }, points { points } {}

    constexpr Polygon<VectorType, max_points_count + 2> clip_z(float z1, float z2) const & {
        return
            clip_aligned<VectorType, max_points_count + 1, engine::math::vector_coords::VectorCoord::z, false>(z2,
            clip_aligned<VectorType, max_points_count + 0, engine::math::vector_coords::VectorCoord::z, true >(z1,
            *this));
    }

    constexpr Polygon<VectorType, max_points_count + 4> clip_xy(float x1, float y1, float x2, float y2) const & {
        return
            clip_aligned<VectorType, max_points_count + 3, engine::math::vector_coords::VectorCoord::y, false>(y2,
            clip_aligned<VectorType, max_points_count + 2, engine::math::vector_coords::VectorCoord::x, false>(x2,
            clip_aligned<VectorType, max_points_count + 1, engine::math::vector_coords::VectorCoord::y, true >(y1,
            clip_aligned<VectorType, max_points_count + 0, engine::math::vector_coords::VectorCoord::x, true >(x1,
            *this))));
    }

    constexpr Polygon<VectorType, max_points_count> map_xy(
            const engine::math::Vector2& from1, const engine::math::Vector2& from2,
            const engine::math::Vector2& to1,   const engine::math::Vector2& to2) const & {
        Polygon<VectorType, max_points_count> polygon;
        polygon.points_count = points_count;
        for (std::size_t i = 0; i < points_count; i++) {
            auto& p = polygon.points[i];
            if constexpr (std::is_same_v<VectorType, engine::math::Vector3>) {
                p.vertex = {
                    points[i].vertex.xy().map(from1, from2, to1, to2),
                    points[i].vertex.z,
                };
            } else {
                p.vertex = {
                    points[i].vertex.xy().map(from1, from2, to1, to2),
                    points[i].vertex.z,
                    points[i].vertex.w,
                };
            }
            p.b0 = points[i].b0;
            p.b1 = points[i].b1;
        }
        return polygon;
    }

    constexpr std::tuple<std::size_t, std::array<engine::o3d::TriangleDerived<engine::math::Vector3>, max_points_count - 2>> to_triangles() const &
    requires (max_points_count >= 3)
    {
        std::array<engine::o3d::TriangleDerived<VectorType>, max_points_count - 2> triangles;
        if (points_count < 3) return { 0, triangles };
        for (std::size_t i = 0; i < points_count - 2; i++)
            triangles[i] = { points[0], points[i + 1], points[i + 2] };
        return { points_count - 2, triangles };
    }

    constexpr std::tuple<std::size_t, std::array<engine::o3d::TriangleDerived<engine::math::Vector3>, 0>> to_triangles() const & {
        return { 0, {} };
    }
};

template<engine::math::VectorTypeConcept VectorType>
constexpr Polygon<VectorType, 3> from_triangle_derived(const engine::o3d::TriangleDerived<VectorType>& triangle_derived) {
    return { 3, { triangle_derived.derived_vertex1, triangle_derived.derived_vertex2, triangle_derived.derived_vertex3 } };
}

template<PolygonVectorTypeConcept VectorType, std::size_t max_points_count, engine::math::vector_coords::VectorCoord coord_id, bool keep_geq>
constexpr Polygon<VectorType, max_points_count + 1> clip_aligned(float boundary, const Polygon<VectorType, max_points_count>& polygon)
{
    using transposition = engine::math::vector_coords::transpose<engine::math::vector_coords::VectorCoord::x, coord_id>;
    constexpr auto y_coord_id = transposition::template id<engine::math::vector_coords::VectorCoord::y>();
    constexpr auto z_coord_id = transposition::template id<engine::math::vector_coords::VectorCoord::z>();
    constexpr auto is_in = [](const engine::o3d::DerivedVertex<VectorType>& p, float boundary) constexpr {
        if constexpr (keep_geq) return p.vertex.template v<coord_id>() >= boundary;
        else                    return p.vertex.template v<coord_id>() <= boundary;
    };
    Polygon<VectorType, max_points_count + 1> new_polygon;
    for (size_t i = 0; i < polygon.points_count; i++) {
        const auto& prev = polygon.points[(i + polygon.points_count - 1) % polygon.points_count];
        const auto& cur  = polygon.points[i];
        if (is_in(prev, boundary) != is_in(cur, boundary)) {
            float fac = (boundary - prev.vertex.template v<coord_id>()) / (cur.vertex.template v<coord_id>() - prev.vertex.template v<coord_id>());
            auto& new_point = new_polygon.points[new_polygon.points_count++];
            new_point.vertex.template v<coord_id>()   = boundary;
            new_point.vertex.template v<y_coord_id>() = engine::math::utils::lerp(prev.vertex.template v<y_coord_id>(), cur.vertex.template v<y_coord_id>(), fac);
            if constexpr (std::is_same_v<VectorType, engine::math::Vector3>) {
                // called new_w because it represents w, but because we only need x, y and w, we use
                // Vector3, and therefore the vector coordinate's name is z
                float new_w = 1.f / ((1.f - fac) / prev.vertex.z + fac / cur.vertex.z);
                new_point.vertex.template v<z_coord_id>() = new_w;
                new_point.b0 = new_w * engine::math::utils::lerp(prev.b0 / prev.vertex.z, cur.b0 / cur.vertex.z, fac);
                new_point.b1 = new_w * engine::math::utils::lerp(prev.b1 / prev.vertex.z, cur.b1 / cur.vertex.z, fac);
            } else { // Vector4
                constexpr auto w_coord_id = transposition::template id<engine::math::vector_coords::VectorCoord::w>();
                new_point.vertex.template v<z_coord_id>() = engine::math::utils::lerp(prev.vertex.template v<z_coord_id>(), cur.vertex.template v<z_coord_id>(), fac);
                new_point.vertex.template v<w_coord_id>() = engine::math::utils::lerp(prev.vertex.template v<w_coord_id>(), cur.vertex.template v<w_coord_id>(), fac);
                new_point.b0 = engine::math::utils::lerp(prev.b0, cur.b0, fac);
                new_point.b1 = engine::math::utils::lerp(prev.b1, cur.b1, fac);
            }
        }
        if (is_in(cur, boundary))
            new_polygon.points[new_polygon.points_count++] = cur;
    }
    return new_polygon;
}

template<std::size_t max_points_count>
constexpr Polygon<engine::math::Vector3, max_points_count> div_by_w(const Polygon<engine::math::Vector4, max_points_count>& polygon) {
    Polygon<engine::math::Vector3, max_points_count> new_polygon;
    new_polygon.points_count = polygon.points_count;
    for (std::size_t i = 0; i < polygon.points_count; i++)
        new_polygon.points[i] = polygon.points[i].div_by_w();
    return new_polygon;
}

template<std::size_t max_points_count>
constexpr float signed_area_xy(const Polygon<engine::math::Vector3, max_points_count>& polygon) {
    float res = 0.f;
    if (polygon.points_count > 0) {
        for (std::size_t i = 0; i < polygon.points_count - 1; i++)
            res += polygon.points[i].vertex.xy().det(polygon.points[i + 1].vertex.xy());
        res += polygon.points[polygon.points_count - 1].vertex.xy().det(polygon.points[0].vertex.xy());
    }
    return .5f * res;
}

}

#endif // O3D_POLYGON_HPP