31#ifndef ALEPH_TEST_GEOMETRY_VISUAL_GOLDEN_H
32#define ALEPH_TEST_GEOMETRY_VISUAL_GOLDEN_H
63 for (
const char ch :
raw)
64 if (std::isalnum(
static_cast<unsigned char>(
ch))
or ch ==
'-' or ch ==
'_')
69 return out.empty() ? std::string(
"case") :
out;
75 out.reserve(s.size());
76 for (
const char ch : s)
80 case '&':
out +=
"&";
break;
81 case '<':
out +=
"<";
break;
82 case '>':
out +=
">";
break;
83 case '"':
out +=
""";
break;
84 case '\'':
out +=
"'";
break;
85 default:
out.push_back(
ch);
break;
95 return std::filesystem::path(
env_dir);
97 return std::filesystem::path(
"test_artifacts/golden_svg");
103 if (poly.size() == 0)
109 scene.highlighted_points.append(it.get_current_vertex());
111 scene.points.append(it.get_current_vertex());
117 for (
size_t i = 0; i <
scene.points.size(); ++i)
119 for (
size_t i = 0; i <
scene.highlighted_points.size(); ++i)
122 for (
size_t i = 0; i <
scene.segments.size(); ++i)
128 for (
size_t i = 0; i <
scene.polygons.size(); ++i)
130 if (
scene.polygons(i).size() == 0)
139 const std::string &
note =
"")
143 std::filesystem::create_directories(
out_dir,
ec);
145 const std::filesystem::path
out_file =
161 for (
size_t i = 0; i <
all_points.size(); ++i)
163 const double x =
all_points(i).get_x().get_d();
173 xmin = std::min(xmin, x);
174 xmax = std::max(xmax, x);
175 ymin = std::min(ymin,
y);
176 ymax = std::max(ymax,
y);
186 double dx = xmax - xmin;
187 double dy = ymax - ymin;
201 double pad = std::max(dx, dy) * 0.08;
211 constexpr double width = 960.0;
212 constexpr double height = 720.0;
213 constexpr double margin = 32.0;
220 const auto map_point = [=](const ::Point & p)
222 const double x =
xoff + (p.get_x().get_d() - xmin) *
scale;
223 const double y = height - (
yoff + (p.get_y().get_d() - ymin) *
scale);
224 return std::pair<double, double>{x,
y};
227 out <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
228 out <<
"<svg xmlns=\"http://www.w3.org/2000/svg\" "
229 <<
"width=\"" <<
static_cast<int>(width) <<
"\" "
230 <<
"height=\"" <<
static_cast<int>(height) <<
"\" "
231 <<
"viewBox=\"0 0 " <<
static_cast<int>(width) <<
" "
232 <<
static_cast<int>(height) <<
"\">\n";
233 out <<
" <rect x=\"0\" y=\"0\" width=\"" <<
static_cast<int>(width)
234 <<
"\" height=\"" <<
static_cast<int>(height)
235 <<
"\" fill=\"#ffffff\"/>\n";
236 out <<
" <rect x=\"1\" y=\"1\" width=\"" <<
static_cast<int>(width - 2)
237 <<
"\" height=\"" <<
static_cast<int>(height - 2)
238 <<
"\" fill=\"none\" stroke=\"#e5e7eb\" stroke-width=\"2\"/>\n";
241 out <<
" <text x=\"16\" y=\"22\" font-size=\"14\" "
242 <<
"font-family=\"monospace\" fill=\"#111827\">"
245 static constexpr std::array<const char *, 8>
stroke_colors = {
246 "#2563eb",
"#16a34a",
"#ea580c",
"#7c3aed",
247 "#0f766e",
"#b91c1c",
"#1d4ed8",
"#0369a1"
249 static constexpr std::array<const char *, 8>
fill_colors = {
250 "#bfdbfe",
"#bbf7d0",
"#fed7aa",
"#ddd6fe",
251 "#99f6e4",
"#fecaca",
"#c7d2fe",
"#bae6fd"
254 out << std::fixed << std::setprecision(3);
256 for (
size_t pi = 0; pi <
scene.polygons.size(); ++pi)
258 const ::Polygon & poly =
scene.polygons(pi);
259 if (poly.size() == 0)
265 const auto [x,
y] = map_point(it.get_current_vertex());
277 if (poly.is_closed()
and count >= 3)
279 <<
"\" fill=\"" <<
fill
280 <<
"\" fill-opacity=\"0.22\" stroke=\"" <<
stroke
281 <<
"\" stroke-width=\"2\"/>\n";
284 <<
"\" fill=\"none\" stroke=\"" <<
stroke
285 <<
"\" stroke-width=\"2\"/>\n";
289 const auto [vx, vy] = map_point(it.get_current_vertex());
290 out <<
" <circle cx=\"" << vx <<
"\" cy=\"" << vy
291 <<
"\" r=\"3.0\" fill=\"" <<
stroke
292 <<
"\" stroke=\"#ffffff\" stroke-width=\"1\"/>\n";
296 for (
size_t si = 0;
si <
scene.segments.size(); ++
si)
298 const ::Segment & seg =
scene.segments(
si);
299 const auto [x1,
y1] = map_point(seg.get_src_point());
300 const auto [x2, y2] = map_point(seg.get_tgt_point());
301 out <<
" <line x1=\"" << x1 <<
"\" y1=\"" <<
y1
302 <<
"\" x2=\"" << x2 <<
"\" y2=\"" << y2
303 <<
"\" stroke=\"#4b5563\" stroke-width=\"2.0\"/>\n";
306 for (
size_t i = 0; i <
scene.points.size(); ++i)
308 const auto [x,
y] = map_point(
scene.points(i));
309 out <<
" <circle cx=\"" << x <<
"\" cy=\"" <<
y
310 <<
"\" r=\"3.2\" fill=\"#111827\" stroke=\"#ffffff\" "
311 <<
"stroke-width=\"0.9\"/>\n";
314 for (
size_t i = 0; i <
scene.highlighted_points.size(); ++i)
316 const auto [x,
y] = map_point(
scene.highlighted_points(i));
317 out <<
" <circle cx=\"" << x <<
"\" cy=\"" <<
y
318 <<
"\" r=\"4.3\" fill=\"#dc2626\" stroke=\"#ffffff\" "
319 <<
"stroke-width=\"1.2\"/>\n";
322 out <<
" <text x=\"16\" y=\"" <<
static_cast<int>(height - 14)
323 <<
"\" font-size=\"12\" font-family=\"monospace\" fill=\"#4b5563\">"
Simple dynamic array with automatic resizing and functional operations.
void reserve(size_t cap)
Reserves cap cells into the array.
bool has_curr() const noexcept
Return true if the iterator has current item.
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_y1_function > > y1(const __gmp_expr< T, U > &expr)
void add_polygon_vertices(SvgScene &scene, const ::Polygon &poly, const bool as_highlight=false)
std::filesystem::path emit_case_svg(const std::string &case_id, const SvgScene &scene, const std::string ¬e="")
std::string sanitize_filename(const std::string &raw)
std::string xml_escape(const std::string &s)
std::filesystem::path golden_output_dir()
void collect_scene_points(const SvgScene &scene, ::Array<::Point > &all_points)
and
Check uniqueness with explicit hash + equality functors.
void fill(Itor beg, const Itor &end, const T &value)
Fill a range with a value.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
2D point and geometric utilities.
2D polygon representation and geometric operations.
Iterator over the vertices of a polygon.
::Array<::Polygon > polygons
::Array<::Point > highlighted_points
::Array<::Segment > segments
Dynamic array container with automatic resizing.