8 const float dx =
x - other
.x;
9 const float dy =
y - other
.y;
10 return std::sqrt(dx * dx + dy * dy);
18 out <<
"Line(name=" << name <<
", closed=" << (closed ?
"true" :
"false")
20 for (size_t i = 0; i < points.size(); i++) {
21 out <<
"(" << points[i].x <<
", " << points[i].y <<
")";
22 if (i < points.size() - 1) {
30 const size_t n = points.size();
34 constexpr point origin { 0.0f, 0.0f };
35 constexpr point east { 100.0f, 0.0f };
39 float best_distance = points[0].distance_to(origin);
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;
51 = points.begin() +
static_cast<std::ptrdiff_t>(best_idx);
52 std::ranges::rotate(points, it);
56 const size_t front =
closed ? 1 : 0;
59 const float first = points[front].distance_to(closed ? east : origin);
60 const float last = points.back().distance_to(closed ? east : origin);
63 points.begin() +
static_cast<std::ptrdiff_t>(front),
71 if (closed != other.closed || points.size() != other.points.size()) {
74 for (size_t i = 0; i < points.size(); i++) {
75 if (!points[i].compare(other.points[i])) {
83 std::vector<point> points, std::string name,
bool closed
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();
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;
99 while (start < input.size()) {
100 size_t end = input.find(
';', start);
101 if (end == std::string_view::npos) {
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)
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)
119 float x = parse_float(x_str);
120 float y = parse_float(y_str);
121 points.emplace_back(x, y);
125 if (points.empty()) {
126 throw std::runtime_error(
127 "No valid points found in input: " + points_str
134 std::string normalized;
135 normalized.reserve(str.size());
136 for (
const char ch : str) {
137 if (std::isspace(ch) || ch ==
'(' || ch ==
')') {
140 normalized.push_back(ch);
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)
std::string normalize_str(std::string_view str)
Remove whitespace and parentheses from a string.
float parse_float(std::string_view num_str)
Parse a float from a string view.
std::shared_ptr< line const > line_ptr
Shared, immutable line pointer.
Polyline / polygon described in percentage coordinates.
bool closed
Whether the chain is closed.
void dump(std::ostream &out) const
Print a human-readable representation of the line.
void normalize()
Canonicalize point order.
bool operator==(const line &other) const
Equality check using canonical point comparison.
Point in percentage-based image coordinates.
float distance_to(const point &other) const
Compute Euclidean distance to another point.
float x
Horizontal coordinate (percentage of width).
float y
Vertical coordinate (percentage of height).
static constexpr float epsilon
Tolerance used for fuzzy point comparisons.
bool compare(const point &other) const
Compare two points with tolerance epsilon.