header_utils
Loading...
Searching...
No Matches
segment.h
1
4
5#pragma once
6
7#include "geometry_common.h"
8
10{
13 template <std::floating_point T>
14 struct tsegment
15 {
16 using tvec = glm::tvec2<T>;
17 using value_type = T;
18
19 tvec start{};
20 tvec end{};
21
22 constexpr tsegment() noexcept = default;
23 constexpr tsegment(tvec const& start, tvec const& end) noexcept : start(start), end(end) {}
24 constexpr tsegment(std::pair<tvec, tvec> const& p) noexcept : start(p.first), end(p.second) {}
25
26 constexpr tvec vec() const noexcept { return end - start; }
27 tvec dir() const noexcept { return glm::normalize(vec()); }
28 auto length() const noexcept { return glm::distance(start, end); }
29 constexpr auto center() const noexcept { return (start + end) / T(2); }
30
31 static constexpr tsegment from_offset(tvec const& start, tvec const& offset) noexcept { return { start, start + offset }; }
32 static constexpr tsegment from_dir(tvec const& start, tvec const& dir, T len) noexcept { return { start, start + dir * len }; }
33 static constexpr tsegment from_pair(std::pair<tvec, tvec> const& p) noexcept { return { p.first, p.second }; }
34
35 basic_line_t<T> line() const noexcept { return line_crossing_points(start, end); }
36
37 tsegment& set_position(tvec const& pos) noexcept { const auto d = vec(); start = pos; end = pos + d; return *this; }
38 tsegment& operator+=(tvec const& offs) noexcept { start += offs; end += offs; return *this; }
39 tsegment& operator-=(tvec const& offs) noexcept { start -= offs; end -= offs; return *this; }
40 tsegment& translate(tvec const& offs) noexcept { return this->operator+=(offs); }
41
42 tsegment& set_length(T len) noexcept { end = start + dir() * len; return *this; }
43 tsegment& set_length_around_center(T len) noexcept
44 {
45 const auto c = center();
46 const auto d = dir();
47 const auto hlen = len / T(2);
48 start = c - d * hlen;
49 end = c + d * hlen;
50 return *this;
51 }
52
53 tsegment& grow(T len) noexcept { const auto d = dir(); start -= d * len; end += d * len; return *this; }
54 tsegment& shrink(T len) noexcept { const auto d = dir(); start += d * len; end -= d * len; return *this; }
55
56 constexpr std::optional<tvec> intersection(tsegment const& other) const noexcept
57 {
59 const auto r = vec();
60 const auto s = other.vec();
61 const auto rxs = glm::cross(r, s);
62 const auto qp = other.start - start;
63 const auto qpxr = glm::cross(qp, r);
64 if (glm::abs(rxs) < std::numeric_limits<T>::epsilon() && glm::abs(qpxr) < std::numeric_limits<T>::epsilon())
65 {
67 const auto t0 = glm::dot(qp, r) / glm::dot(r, r);
68 const auto t1 = t0 + glm::dot(s, r) / glm::dot(r, r);
69 if ((t0 >= 0 && t0 <= 1) || (t1 >= 0 && t1 <= 1))
70 return std::nullopt;
71 return std::nullopt;
72 }
73 if (glm::abs(rxs) < std::numeric_limits<T>::epsilon() && glm::abs(qpxr) > std::numeric_limits<T>::epsilon())
74 return std::nullopt;
75 const auto t = glm::cross(qp, s) / rxs;
76 const auto u = glm::cross(qp, r) / rxs;
77 if (rxs != 0 && t >= 0 && t <= 1 && u >= 0 && u <= 1)
78 return start + t * r;
79 return std::nullopt;
80 }
81
83
84 T edge_length() const { return length(); }
85 tvec edge_point_alpha(T t) const { return glm::mix(start, end, t); }
86 tvec edge_point(T t) const { return glm::mix(start, end, t / edge_length()); }
87 trec2<T> bounding_box() const { return trec2<T>::from_points({ &start, &start + 2 }); }
88 tvec closest_point_to(tvec pt) const
89 {
90 const auto dir = this->vec();
91 const auto d1 = glm::dot(pt - start, dir);
92 if (d1 <= 0)
93 return start;
94 const auto d2 = glm::dot(dir, dir);
95 if (d1 > d2)
96 return end;
97 return start + dir * (d1 / d2);
98 }
99
100 interval<T> projected_on_axis(tvec const& axis) const
101 {
102 const auto p0 = glm::dot(axis, start);
103 const auto p1 = glm::dot(axis, end);
104 return { std::min(p0, p1), std::max(p0, p1) };
105 }
106
108
109 template <typename FUNC>
110 void for_each_edge(FUNC&& func) const
111 {
112 func(start, end);
113 }
114
115 template <typename FUNC>
116 void for_each_vertex(FUNC&& func) const
117 {
118 func(start);
119 func(end);
120 }
121
122 size_t vertex_count() const { return 2; }
123
124 size_t edge_count() const { return 1; }
125
126 auto edge(size_t index) const -> std::optional<std::pair<glm::tvec2<T>, glm::tvec2<T>>>
127 {
128 if (index == 0)
129 return std::make_pair(start, end);
130 return std::nullopt;
131 }
132
133 auto vertex(size_t index) const -> std::optional<tvec>
134 {
135 if (index == 0)
136 return start;
137 if (index == 1)
138 return end;
139 return std::nullopt;
140 }
141 };
142
143 using segment = tsegment<float>;
144
145 static_assert(polygon_shape<float, segment>);
146}
A shape with vertices and edges.
basic_line_t< T > line_crossing_points(glm::tvec2< T > const &p1, glm::tvec2< T > const &p2)
TODO: Move these to the body as static methods?
TODO: This all needs to be tested.
Represents a 2D segment (technically a polygon_shape).
Definition segment.h:15
T edge_length() const
Shape interface.
Definition segment.h:84
void for_each_edge(FUNC &&func) const
Polygon shape interface.
Definition segment.h:110