10 #include "jade.newick.hpp"
11 #include "jade.simplex.hpp"
12 #include "jade.vec2.hpp"
19 template <
typename TValue>
38 : _root (node.get_id())
52 typedef typename simplex_type::container_type container_type;
53 typedef typename simplex_type::execute_args execute_args_type;
54 typedef typename simplex_type::options options_type;
60 const auto import_params = [
this](
61 const container_type & params) ->
void
63 auto dst = _table.begin();
64 for (
auto src = params.begin(); src != params.end(); ++src)
65 (dst++)->second.radians = *src;
66 _update_table(*_table.find(_root)->second.node);
74 const auto objfunc = [
this, import_params](
77 import_params(params);
79 for (
const auto & a0 : _table)
80 for (
const auto & b0 : _table)
81 if (a0.first != b0.first)
83 const auto a = a0.second.position;
84 const auto b = b0.second.position;
87 return std::numeric_limits<value_type>::max();
98 static const auto tau =
value_type(2.0 * std::acos(-1.0));
102 const auto n = _table.size();
108 options_type opts (n);
109 opts.unit = to_radians(1);
111 for (
const auto & p : _table)
112 opts.vertex.push_back(p.second.radians);
113 simplex_type simplex (objfunc, opts);
120 execute_args_type exe_args_1;
121 exe_args_1.max_iterations = 100;
122 exe_args_1.max_seconds = 0.5;
123 simplex.execute(objfunc, exe_args_1);
125 execute_args_type exe_args_2;
126 exe_args_2.min_length = to_radians(0.01) *
value_type(n);
127 exe_args_2.max_seconds = 0.5;
128 simplex.execute(objfunc, exe_args_2);
133 import_params(simplex.get_vertex());
143 std::ostringstream out;
155 const auto & root = *_table.find(_root)->second.node;
156 metrics_data metrics;
157 _create_metrics(metrics);
159 _write_svg_header(out, metrics);
160 _write_edges(out, metrics, root);
161 _write_nodes(out, metrics, root);
162 _write_svg_footer(out);
169 char const *
const path)
172 assert(path !=
nullptr);
173 std::ofstream out (path);
175 throw error() <<
"error opening '" << path <<
"' for writing";
183 const std::string & path)
226 static constexpr
double opacity = 0.75;
227 static constexpr
char const * circle_fill_color =
"black";
228 static constexpr
char const * circle_stroke_color =
"white";
229 static constexpr
char const * text_color =
"white";
230 static constexpr
char const * text_outline_color =
"black";
231 static constexpr
char const * line_color =
"black";
232 static constexpr
char const * font_family =
"Times,serif";
235 typedef std::map<int, node_ex> table_type;
238 void _create_metrics(metrics_data & metrics)
const
240 typedef std::numeric_limits<value_type> numeric_limits_type;
242 memset(&metrics, 0,
sizeof(metrics));
244 auto iter = _table.begin();
245 if (iter == _table.end())
250 const auto is_measured = [](
const node_ex & n) ->
bool
252 return n.node->has_length() && n.node->has_name();
255 auto shortest_edge = numeric_limits_type::quiet_NaN();
256 if (is_measured(iter->second))
257 shortest_edge = iter->second.length;
259 auto min = iter->second.position;
260 auto max = iter->second.position;
262 if (++iter == _table.end())
269 while (iter != _table.end())
271 if (is_measured(iter->second))
273 const auto shortest_edge_i =
277 if (std::isnan(shortest_edge)
278 || shortest_edge_i < shortest_edge)
279 shortest_edge = shortest_edge_i;
287 if (std::isnan(shortest_edge))
315 metrics.large_radius = metrics.scale *
321 const auto diameter = metrics.large_radius + metrics.large_radius;
322 min = metrics.scale * min - diameter;
323 max = metrics.scale * max + diameter;
325 metrics.font_size = metrics.large_radius *
value_type(0.90);
326 metrics.small_radius = metrics.large_radius *
value_type(0.30);
327 metrics.stroke_width = metrics.large_radius *
value_type(0.15);
328 metrics.text_offset = metrics.large_radius *
value_type(0.30);
329 metrics.view_box_left = min.x;
330 metrics.view_box_top = min.y;
331 metrics.view_box_width = (max - min).x;
332 metrics.view_box_height = (max - min).y;
337 void _find_edge_coords(
338 const metrics_data & metrics,
345 const auto & n1ex = _table.find(n1.get_id())->second;
346 const auto & n2ex = _table.find(n2.get_id())->second;
348 p1 = metrics.scale * n1ex.position;
349 p2 = metrics.scale * n2ex.position;
353 p1 += n * (n1ex.node->get_name().empty()
354 ? metrics.small_radius
355 : metrics.large_radius);
357 p2 -= n * (n2ex.node->get_name().empty()
358 ? metrics.small_radius
359 : metrics.large_radius);
365 static const auto tau =
value_type(2.0 * std::acos(-1.0));
366 _init_table(node, 0, tau);
377 ex.radians = radians;
378 ex.length = std::max(
value_type(0), node.get_length());
380 const auto id = node.get_id();
383 const auto & children = node.get_children();
384 const auto n = children.size();
387 for (
size_t i = 0; i < n; i++)
390 const auto percent = (half + ival) / nval;
391 const auto radians2 = (range * percent) - (half * range);
392 const auto range2 = range / nval;
393 _init_table(*children[i], radians2, range2);
398 void _update_table(
const node_type & node)
409 const auto id = node.get_id();
410 auto & ex = _table[id];
412 const auto rad = radians0 + ex.radians;
414 ex.position = p0 + ex.length *
vec2_type(
418 for (
const auto ptr : node.get_children())
419 _update_table(*ptr, rad, ex.position);
425 const metrics_data & metrics,
429 const auto stroke_width = metrics.stroke_width;
431 for (
const auto child : node.get_children())
434 _find_edge_coords(metrics, node, *child, p1, p2);
435 _write_svg_line(out, p1.x, p1.y, p2.x, p2.y, stroke_width);
438 for (
const auto child : node.get_children())
439 _write_edges(out, metrics, *child);
445 const metrics_data & metrics,
449 const auto stroke_width =
value_type(0.5) * metrics.stroke_width;
450 const auto parent_id = node.get_id();
451 const auto & parent_ex = _table.find(parent_id)->second;
452 const auto position = metrics.scale * parent_ex.position;
454 const auto radius = node.get_name().empty() ?
455 metrics.small_radius :
456 metrics.large_radius;
460 _write_svg_circle(out, position.x, position.y,
461 radius, stroke_width);
463 if (!node.get_name().empty())
465 const auto x = position.x;
466 const auto y = position.y + metrics.text_offset;
467 const auto font_size = metrics.font_size;
468 const auto text = node.get_name().c_str();
469 _write_svg_text(out, x, y, font_size, text,
true);
470 _write_svg_text(out, x, y, font_size, text,
false);
475 for (
const auto child : node.get_children())
476 _write_nodes(out, metrics, *child);
480 static void _write_svg_circle(
488 <<
"cx=\"" << cx <<
"px\" "
489 <<
"cy=\"" << cy <<
"px\" "
490 <<
"r=\"" << r <<
"px\" "
492 <<
"fill:" << theme::circle_fill_color <<
";"
493 <<
"fill-opacity:" << theme::opacity <<
";"
494 <<
"stroke:" << theme::circle_stroke_color <<
";"
495 <<
"stroke-width:" << stroke_width <<
"px\">"
500 static void _write_svg_footer(
507 static void _write_svg_header(
509 const metrics_data & metrics)
512 <<
"xmlns=\"http://www.w3.org/2000/svg\" "
513 <<
"version=\"1.1\" "
515 << metrics.view_box_left <<
" "
516 << metrics.view_box_top <<
" "
517 << metrics.view_box_width <<
" "
518 << metrics.view_box_height <<
"\">\n";
522 static void _write_svg_line(
531 <<
"x1=\"" << x1 <<
"px\" "
532 <<
"y1=\"" << y1 <<
"px\" "
533 <<
"x2=\"" << x2 <<
"px\" "
534 <<
"y2=\"" << y2 <<
"px\" "
536 <<
"stroke-opacity:" << theme::opacity <<
";"
537 <<
"stroke-linecap:butt;"
538 <<
"stroke:" << theme::line_color <<
";"
539 <<
"stroke-width:" << stroke_width <<
"px\">"
544 static void _write_svg_text(
549 char const *
const text,
553 <<
"x=\"" << x <<
"px\" "
554 <<
"y=\"" << y <<
"px\" "
558 out <<
"fill:" << theme::text_outline_color <<
";"
559 <<
"fill-opacity:" << theme::opacity <<
";"
560 <<
"stroke-opacity:" << theme::opacity <<
";"
561 <<
"stroke:" << theme::text_outline_color <<
";"
562 <<
"stroke-width:" << font_size /
value_type(7) <<
"px;";
564 out <<
"fill:" << theme::text_color <<
";";
566 out <<
"text-anchor:middle;"
567 <<
"font-size:" << font_size <<
"px;"
568 <<
"font-family:" << theme::font_family <<
"\">";
570 for (
char const * ptr = text; *ptr !=
'\0'; ptr++)
571 if (std::isprint(*ptr))
582 #endif // JADE_SVG_HPP__