From 43d92a5ce7b4497dfe7b678955204ea3abdf810c Mon Sep 17 00:00:00 2001 From: twanvl Date: Sat, 7 Jul 2007 01:39:08 +0000 Subject: [PATCH] Keyword usage can now be used for statistics git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@517 0fc631ac-6414-0410-93d0-97cfa31319b6 --- src/data/statistics.cpp | 3 +++ src/data/statistics.hpp | 1 + src/gui/control/graph.cpp | 40 +++++++++++++++++++++++++--------- src/gui/control/graph.hpp | 2 ++ src/gui/set/stats_panel.cpp | 16 +++++++++++++- src/script/functions/basic.cpp | 25 +++++++++++++++++++++ 6 files changed, 76 insertions(+), 11 deletions(-) diff --git a/src/data/statistics.cpp b/src/data/statistics.cpp index 3d6ef1f7..7067ab38 100644 --- a/src/data/statistics.cpp +++ b/src/data/statistics.cpp @@ -23,6 +23,7 @@ StatsDimension::StatsDimension() , position_hint(0) , numeric (false) , show_empty (false) + , split_list (false) {} StatsDimension::StatsDimension(const Field& field) @@ -33,6 +34,7 @@ StatsDimension::StatsDimension(const Field& field) , icon_filename(field.icon_filename) , numeric (false) , show_empty (false) + , split_list (false) { // choice field? const ChoiceField* choice_field = dynamic_cast(&field); @@ -72,6 +74,7 @@ IMPLEMENT_REFLECTION_NO_GET_MEMBER(StatsDimension) { REFLECT(script); REFLECT(numeric); REFLECT(show_empty); + REFLECT(split_list); REFLECT(colors); REFLECT(groups); } diff --git a/src/data/statistics.hpp b/src/data/statistics.hpp index 1322db03..a10b9939 100644 --- a/src/data/statistics.hpp +++ b/src/data/statistics.hpp @@ -35,6 +35,7 @@ class StatsDimension : public IntrusivePtrBase { OptionalScript script; ///< Script that determines the value(s) bool numeric; ///< Are the values numeric? If so, they require special sorting bool show_empty; ///< Should "" be shown? + bool split_list; ///< Split values into multiple ones separated by commas map colors; ///< Colors for the categories vector groups; ///< Order of the items diff --git a/src/gui/control/graph.cpp b/src/gui/control/graph.cpp index dc6ec290..7664f4bd 100644 --- a/src/gui/control/graph.cpp +++ b/src/gui/control/graph.cpp @@ -37,6 +37,24 @@ GraphElement::GraphElement(const String& v1, const String& v2) { values.push_back(v2); } +void GraphDataPre::splitList(size_t axis) { + size_t count = elements.size(); // only the elements that were already there + for (size_t i = 0 ; i < count ; ++i) { + GraphElement& e = *elements[i]; + String& v = e.values[axis]; + size_t comma = v.find_first_of(_(',')); + while (comma != String::npos) { + // split + GraphElementP e2(new GraphElement(e)); + e2->values[axis] = v.substr(0,comma); + elements.push_back(e2); + if (is_substr(v, comma, _(", "))) ++comma; // skip space after it + v = v.substr(comma + 1); + comma = v.find_first_of(_(',')); + } + } +} + GraphData::GraphData(const GraphDataPre& d) : axes(d.axes) @@ -50,13 +68,12 @@ GraphData::GraphData(const GraphDataPre& d) FOR_EACH_CONST(e, d.elements) { counts[e->values[i]] += 1; } - // TODO: allow some ordering in the groups if (a->numeric) { // TODO: start at something other than 0? // TODO: support fractions? size_t left = counts.size(); int i = 0; - while (left) { + while (!counts.empty() && i < 100) { String is = String() << i++; map::const_iterator it = counts.find(is); if (it == counts.end()) { @@ -66,16 +83,19 @@ GraphData::GraphData(const GraphDataPre& d) a->groups.push_back(GraphGroup(is, it->second)); a->max = max(a->max, it->second); a->total += it->second; + counts.erase(is); left--; } - if (i > 100) { - // prevent infinite loops if there are non-numeric entries - // drop empty tail - while (a->groups.size() > 1 && a->groups.back().size == 0) { - a->groups.pop_back(); - } - break; - } + } + // Also keep non-numeric entries + FOR_EACH(c, counts) { + a->groups.push_back(GraphGroup(c.first, c.second)); + a->max = max(a->max, c.second); + a->total += c.second; + } + // drop empty tail + while (a->groups.size() > 1 && a->groups.back().size == 0) { + a->groups.pop_back(); } } else if (a->order) { // specific group order diff --git a/src/gui/control/graph.hpp b/src/gui/control/graph.hpp index 4590acfb..709c131d 100644 --- a/src/gui/control/graph.hpp +++ b/src/gui/control/graph.hpp @@ -87,6 +87,8 @@ class GraphDataPre { public: vector axes; vector elements; + /// Split compound elements, "a,b,c" -> "a" and "b" and "c" + void splitList(size_t axis); }; /// Data to be displayed in a graph diff --git a/src/gui/set/stats_panel.cpp b/src/gui/set/stats_panel.cpp index c3156a26..6a3bb460 100644 --- a/src/gui/set/stats_panel.cpp +++ b/src/gui/set/stats_panel.cpp @@ -147,6 +147,8 @@ void StatsPanel::onCommand(int id) { // ----------------------------------------------------------------------------- : Filtering card list +bool chosen(const String& choice, const String& input); + class StatsFilter : public CardListFilter { public: StatsFilter(Set& set) @@ -155,7 +157,13 @@ class StatsFilter : public CardListFilter { virtual bool keep(const CardP& card) { Context& ctx = set.getContext(card); FOR_EACH(v, values) { - if (v.first->script.invoke(ctx)->toString() != v.second) return false; + StatsDimension& dim = *v.first; + String value = dim.script.invoke(ctx)->toString(); + if (dim.split_list) { + if (!chosen(v.second, value)) return false; + } else { + if (value != v.second) return false; + } } return true; } @@ -208,6 +216,12 @@ void StatsPanel::showCategory() { d.elements.push_back(e); } } + // split lists + size_t dim_id = 0; + FOR_EACH(dim, cat.dimensions) { + if (dim->split_list) d.splitList(dim_id); + ++dim_id; + } graph->setLayout(cat.type); graph->setData(d); filterCards(); diff --git a/src/script/functions/basic.cpp b/src/script/functions/basic.cpp index 7e21ae41..809d7bb5 100644 --- a/src/script/functions/basic.cpp +++ b/src/script/functions/basic.cpp @@ -297,6 +297,30 @@ SCRIPT_RULE_2_DEPENDENCIES(expand_keywords) { SCRIPT_RETURN(_("")); } +SCRIPT_FUNCTION(keyword_usage) { + SCRIPT_PARAM(CardP, card); + SCRIPT_OPTIONAL_PARAM_(bool, unique); + // make a list "kw1, kw2, kw3" of keywords used on card + String ret; + for (KeywordUsageStatistics::const_iterator it = card->keyword_usage.begin() ; it != card->keyword_usage.end() ; ++it) { + bool keep = true; + if (unique) { + // prevent duplicates + for (KeywordUsageStatistics::const_iterator it2 = card->keyword_usage.begin() ; it != it2 ; ++it2) { + if (it->second == it2->second) { + keep = false; + break; + } + } + } + if (keep) { + if (!ret.empty()) ret += _(", "); + ret += it->second->keyword; + } + } + SCRIPT_RETURN(ret); +} + // ----------------------------------------------------------------------------- : Rules : regex replace class ScriptReplaceRule : public ScriptValue { @@ -677,6 +701,7 @@ void init_script_basic_functions(Context& ctx) { // keyword ctx.setVariable(_("expand keywords"), script_expand_keywords); ctx.setVariable(_("expand keywords rule"), script_expand_keywords_rule); + ctx.setVariable(_("keyword usage"), script_keyword_usage); // advanced string rules ctx.setVariable(_("replace"), script_replace); ctx.setVariable(_("filter"), script_filter);