YODAU 1.0
YEAR OF THE DEPEND ADULT UNDERGARMENT
Loading...
Searching...
No Matches
geometry.cpp
Go to the documentation of this file.
1#include "geometry.hpp"
2
3#include <algorithm>
4#include <charconv>
5#include <cmath>
6
7float yodau::backend::point::distance_to(const point& other) const {
8 const float dx = x - other.x;
9 const float dy = y - other.y;
10 return std::sqrt(dx * dx + dy * dy);
11}
12
13bool yodau::backend::point::compare(const point& other) const {
14 return std::fabs(x - other.x) < epsilon && std::fabs(y - other.y) < epsilon;
15}
16
17void yodau::backend::line::dump(std::ostream& out) const {
18 out << "Line(name=" << name << ", closed=" << (closed ? "true" : "false")
19 << ", points=[";
20 for (size_t i = 0; i < points.size(); i++) {
21 out << "(" << points[i].x << ", " << points[i].y << ")";
22 if (i < points.size() - 1) {
23 out << "; ";
24 }
25 }
26 out << "])";
27}
28
29void yodau::backend::line::normalize() {
30 const size_t n = points.size();
31 if (n < 2) {
32 return;
33 }
34 constexpr point origin { 0.0f, 0.0f };
35 constexpr point east { 100.0f, 0.0f };
36
37 if (closed) {
38 size_t best_idx = 0;
39 float best_distance = points[0].distance_to(origin);
40
41 for (size_t i = 1; i < n; i++) {
42 float distance = points[i].distance_to(origin);
43 if (distance < best_distance) {
44 best_distance = distance;
45 best_idx = i;
46 }
47 }
48
49 if (best_idx != 0) {
50 const auto it
51 = points.begin() + static_cast<std::ptrdiff_t>(best_idx);
52 std::ranges::rotate(points, it);
53 }
54 }
55
56 const size_t front = closed ? 1 : 0;
57
58 if (n >= 2 + front) {
59 const float first = points[front].distance_to(closed ? east : origin);
60 const float last = points.back().distance_to(closed ? east : origin);
61 if (last < first) {
62 std::reverse(
63 points.begin() + static_cast<std::ptrdiff_t>(front),
64 points.end()
65 );
66 }
67 }
68}
69
70bool yodau::backend::line::operator==(const line& other) const {
71 if (closed != other.closed || points.size() != other.points.size()) {
72 return false;
73 }
74 for (size_t i = 0; i < points.size(); i++) {
75 if (!points[i].compare(other.points[i])) {
76 return false;
77 }
78 }
79 return true;
80}
81
82yodau::backend::line_ptr yodau::backend::make_line(
83 std::vector<point> points, std::string name, bool closed
84) {
85 auto line_ptr = std::make_shared<line>();
86 line_ptr->points = std::move(points);
87 line_ptr->name = std::move(name);
88 line_ptr->closed = closed;
89 line_ptr->normalize();
90 return line_ptr;
91}
92
93std::vector<yodau::backend::point>
94yodau::backend::parse_points(const std::string& points_str) {
95 std::string normalized = normalize_str(points_str);
96 std::string_view input { normalized };
97 std::vector<point> points;
98 size_t start = 0;
99 while (start < input.size()) {
100 size_t end = input.find(';', start);
101 if (end == std::string_view::npos) {
102 end = input.size();
103 }
104 std::string_view segment { input.substr(start, end - start) };
105 if (!segment.empty()) {
106 const size_t comma_pos = segment.find(',');
107 if (comma_pos == std::string_view::npos) {
108 throw std::runtime_error(
109 "Missing comma separator: " + std::string(segment)
110 );
111 }
112 std::string_view x_str = segment.substr(0, comma_pos);
113 std::string_view y_str = segment.substr(comma_pos + 1);
114 if (x_str.empty() || y_str.empty()) {
115 throw std::runtime_error(
116 "Empty coordinate in point: " + std::string(segment)
117 );
118 }
119 float x = parse_float(x_str);
120 float y = parse_float(y_str);
121 points.emplace_back(x, y);
122 }
123 start = end + 1;
124 }
125 if (points.empty()) {
126 throw std::runtime_error(
127 "No valid points found in input: " + points_str
128 );
129 }
130 return points;
131}
132
133std::string yodau::backend::normalize_str(const std::string_view str) {
134 std::string normalized;
135 normalized.reserve(str.size());
136 for (const char ch : str) {
137 if (std::isspace(ch) || ch == '(' || ch == ')') {
138 continue;
139 }
140 normalized.push_back(ch);
141 }
142 return normalized;
143}
144
145float yodau::backend::parse_float(const std::string_view num_str) {
146 float value {};
147 const char* first = num_str.data();
148 const char* last = num_str.data() + num_str.size();
149 auto [ptr, ec] = std::from_chars(first, last, value);
150 if (ec != std::errc() || ptr != last) {
151 throw std::runtime_error(
152 "Invalid float value: " + std::string(num_str)
153 );
154 }
155 return value;
156}
std::string normalize_str(std::string_view str)
Remove whitespace and parentheses from a string.
Definition geometry.cpp:133
float parse_float(std::string_view num_str)
Parse a float from a string view.
Definition geometry.cpp:145
std::shared_ptr< line const > line_ptr
Shared, immutable line pointer.
Definition geometry.hpp:146
Polyline / polygon described in percentage coordinates.
Definition geometry.hpp:81
bool closed
Whether the chain is closed.
Definition geometry.hpp:97
void dump(std::ostream &out) const
Print a human-readable representation of the line.
Definition geometry.cpp:17
void normalize()
Canonicalize point order.
Definition geometry.cpp:29
bool operator==(const line &other) const
Equality check using canonical point comparison.
Definition geometry.cpp:70
Point in percentage-based image coordinates.
Definition geometry.hpp:19
float distance_to(const point &other) const
Compute Euclidean distance to another point.
Definition geometry.cpp:7
float x
Horizontal coordinate (percentage of width).
Definition geometry.hpp:23
float y
Vertical coordinate (percentage of height).
Definition geometry.hpp:28
static constexpr float epsilon
Tolerance used for fuzzy point comparisons.
Definition geometry.hpp:33
bool compare(const point &other) const
Compare two points with tolerance epsilon.
Definition geometry.cpp:13