dotty: colored acceptance sets

This implement several new options for --dot in order to
allow emptiness sets to be output as colored ⓿ or ❶...
Also add a SPOT_DOTDEFAULT environment variable.

* NEWS, src/bin/man/spot-x.x, src/bin/common_aoutput.cc,
src/bin/dstar2tgba.cc: Document the new options.
* doc/org/.dir-locals.el, doc/org/init.el.in: Setup
SPOT_DOTEXTRA and SPOT_DOTDEFAULT for all documents.
* doc/org/autfilt.org, doc/org/dstar2tgba.org, doc/org/ltl2tgba.org,
doc/org/ltldo.org, doc/org/oaut.org, doc/org/randaut.org,
doc/org/satmin.org: Adjust to this new setup.
* src/misc/escape.cc, src/misc/escape.hh (escape_html): New function.
* src/tgba/acc.cc, src/tgba/acc.hh (to_text, to_html): New method.
* src/tgbaalgos/dotty.cc: Implement the new options.
* src/tgbatest/readsave.test, wrap/python/tests/automata.ipynb: More
tests.
* wrap/python/spot.py: Make sure the default argument for
dotty_reachable is None, so that SPOT_DOTDEFAULT is honored.
This commit is contained in:
Alexandre Duret-Lutz 2015-03-17 09:08:20 +01:00
parent 7caf2b83d6
commit 838bfb2ae3
21 changed files with 1500 additions and 1193 deletions

View file

@ -31,11 +31,16 @@
#include "tgba/formula2bdd.hh"
#include "tgbaalgos/sccinfo.hh"
#include <cstdlib>
#include <cstring>
#include <ctype.h>
namespace spot
{
namespace
{
constexpr int MAX_BULLET = 20;
class dotty_output
{
std::ostream& os_;
@ -46,74 +51,229 @@ namespace spot
bool opt_show_acc_ = false;
bool mark_states_ = false;
bool opt_scc_ = false;
bool opt_html_labels_ = false;
const_tgba_digraph_ptr aut_;
std::vector<std::string>* sn_;
std::string* name_ = nullptr;
acc_cond::mark_t inf_sets_ = 0U;
acc_cond::mark_t fin_sets_ = 0U;
bool opt_rainbow = false;
bool opt_bullet = false;
bool opt_all_bullets = false;
std::string opt_font_;
const char* const palette9[9] =
{
"#5DA5DA", /* blue */
"#F17CB0", /* pink */
"#FAA43A", /* orange */
"#B276B2", /* purple */
"#60BD68", /* green */
"#F15854", /* red */
"#B2912F", /* brown */
"#4D4D4D", /* gray */
"#DECF3F", /* yellow */
};
const char*const* palette = palette9;
int palette_mod = 9;
public:
void
parse_opts(const char* options)
{
const char* orig = options;
while (char c = *options++)
switch (c)
{
case '.':
{
static const char* def = getenv("SPOT_DOTDEFAULT");
// Prevent infinite recursions...
if (orig == def)
throw std::runtime_error
(std::string("SPOT_DOTDEFAULT should not contain '.'"));
if (def)
parse_opts(def);
break;
}
case 'a':
opt_show_acc_ = true;
break;
case 'b':
opt_bullet = true;
break;
case 'c':
opt_circles_ = true;
break;
case 'h':
opt_horizontal_ = true;
break;
case 'f':
if (*options != '(')
throw std::runtime_error
(std::string("invalid font specification for dotty()"));
{
auto* end = strchr(++options, ')');
if (!end)
throw std::runtime_error
(std::string("invalid font specification for dotty()"));
opt_font_ = std::string(options, end - options);
options = end + 1;
}
break;
case 'n':
opt_name_ = true;
break;
case 'N':
opt_name_ = false;
break;
case 'r':
opt_html_labels_ = true;
opt_rainbow = true;
break;
case 'R':
opt_html_labels_ = true;
opt_rainbow = false;
break;
case 's':
opt_scc_ = true;
break;
case 'v':
opt_horizontal_ = false;
break;
case 't':
opt_force_acc_trans_ = true;
break;
default:
throw std::runtime_error
(std::string("unknown option for dotty(): ") + c);
}
}
dotty_output(std::ostream& os, const char* options)
: os_(os)
{
if (options)
while (char c = *options++)
switch (c)
{
case 'a':
opt_show_acc_ = true;
break;
case 'c':
opt_circles_ = true;
break;
case 'h':
opt_horizontal_ = true;
break;
case 'n':
opt_name_ = true;
break;
case 'N':
opt_name_ = false;
break;
case 's':
opt_scc_ = true;
break;
case 'v':
opt_horizontal_ = false;
break;
case 't':
opt_force_acc_trans_ = true;
break;
default:
throw std::runtime_error
(std::string("unknown option for dotty(): ") + c);
}
parse_opts(options ? options : ".");
}
void
output_set(std::ostream& os, int v) const
{
if (opt_bullet && (v >= 0) & (v <= MAX_BULLET))
{
static const char* const tab[MAX_BULLET + 1] = {
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"", "", "", "",
"",
};
os << tab[v];
}
else
{
os << v;
}
}
const char*
html_set_color(int v) const
{
if (opt_rainbow)
return palette[v % palette_mod];
// Color according to Fin/Inf
if (inf_sets_.has(v))
{
if (fin_sets_.has(v))
return palette[2];
else
return palette[0];
}
else
{
return palette[1];
}
}
void
output_html_set_aux(std::ostream& os, int v) const
{
os << "<font color=\"" << html_set_color(v) << "\">";
output_set(os, v);
os << "</font>";
}
void
output_html_set(int v) const
{
output_html_set_aux(os_, v);
}
void
start()
{
if (opt_html_labels_)
std::tie(inf_sets_, fin_sets_) =
aut_->get_acceptance().used_inf_fin_sets();
if (opt_bullet && aut_->acc().num_sets() <= MAX_BULLET)
opt_all_bullets = true;
os_ << "digraph G {\n";
if (opt_horizontal_)
os_ << " rankdir=LR\n";
if (name_ || opt_show_acc_)
{
os_ << " label=\"";
if (name_)
if (!opt_html_labels_)
{
escape_str(os_, *name_);
os_ << " label=\"";
if (name_)
{
escape_str(os_, *name_);
if (opt_show_acc_)
os_ << "\\n";
}
if (opt_show_acc_)
os_ << "\\n";
aut_->get_acceptance().to_text
(os_, [this](std::ostream& os, int v)
{
this->output_set(os, v);
});
os_ << "\"\n";
}
if (opt_show_acc_)
os_ << aut_->get_acceptance();
os_ << "\"\n labelloc=\"t\"\n";
else
{
os_ << " label=<";
if (name_)
{
escape_html(os_, *name_);
if (opt_show_acc_)
os_ << "<br/>";
}
if (opt_show_acc_)
aut_->get_acceptance().to_html
(os_, [this](std::ostream& os, int v)
{
this->output_html_set_aux(os, v);
});
os_ << ">\n";
}
os_ << " labelloc=\"t\"\n";
}
if (opt_circles_)
os_ << " node [shape=\"circle\"]\n";
if (!opt_font_.empty())
os_ << " fontname=\"" << opt_font_
<< "\"\n node [fontname=\"" << opt_font_
<< "\"]\n edge [fontname=\"" << opt_font_
<< "\"]\n";
// Any extra text passed in the SPOT_DOTEXTRA environment
// variable should be output at the end of the "header", so
// that our setup can be overridden.
static const char* extra = getenv("SPOT_DOTEXTRA");
if (extra)
if (extra && *extra)
os_ << " " << extra << '\n';
os_ << " I [label=\"\", style=invis, ";
os_ << (opt_horizontal_ ? "width" : "height");
@ -144,15 +304,53 @@ namespace spot
process_link(const tgba_digraph::trans_storage_t& t)
{
std::string label = bdd_format_formula(aut_->get_dict(), t.cond);
label = escape_str(label);
if (!mark_states_)
if (auto a = t.acc)
{
label += "\\n";
label += aut_->acc().format(a);
}
os_ << " " << t.src << " -> " << t.dst
<< " [label=\"" + label + "\"]\n";
os_ << " " << t.src << " -> " << t.dst;
if (!opt_html_labels_)
{
os_ << " [label=\"";
escape_str(os_, label);
if (!mark_states_)
if (auto a = t.acc)
{
os_ << "\\n";
if (!opt_all_bullets)
os_ << '{';
const char* space = "";
for (auto v: a.sets())
{
if (!opt_all_bullets)
os_ << space;
output_set(os_, v);
space = ",";
}
if (!opt_all_bullets)
os_ << '}';
}
os_ << "\"]\n";
}
else
{
os_ << " [label=<";
escape_html(os_, label);
if (!mark_states_)
if (auto a = t.acc)
{
os_ << "<br/>";
if (!opt_all_bullets)
os_ << '{';
const char* space = "";
for (auto v: a.sets())
{
if (!opt_all_bullets)
os_ << space;
output_html_set(v);
space = ",";
}
if (!opt_all_bullets)
os_ << '}';
}
os_ << ">]\n";
}
}
void print(const const_tgba_digraph_ptr& aut)