diff --git a/data/en.mse-locale/locale b/data/en.mse-locale/locale index 9e08f3be..8ee8f8b6 100644 --- a/data/en.mse-locale/locale +++ b/data/en.mse-locale/locale @@ -112,10 +112,13 @@ help: redo: Redoes the last action cut: Move the selected text to the clipboard cut card: Move the selected card to the clipboard + cut keyword: Move the selected keyword to the clipboard copy: Place the selected text on the clipboard copy card: Place the selected card on the clipboard + copy keyword: Place the selected keyword on the clipboard paste: Inserts the text from the clipboard paste card: Inserts the card from the clipboard + paste keyword: Inserts the keyword from the clipboard preferences: Change the configuration of Magic Set Editor cards: @@ -273,6 +276,7 @@ tooltip: new set: New set open set: Open set save set: Save set + export: Export set cut: Cut copy: Copy diff --git a/data/magic-blends.mse-include/card-colors b/data/magic-blends.mse-include/card-colors index b9f3cea8..e55efd1e 100644 --- a/data/magic-blends.mse-include/card-colors +++ b/data/magic-blends.mse-include/card-colors @@ -176,6 +176,7 @@ choice colors: artifact : rgb(188,192,195) multicolor : rgb(255,188,14) land : rgb(109,62,39) + hybrid : rgb(243,26,136) # purple # Sub menus, same colors multicolor 2 color white / blue : rgb(255,188,14) multicolor 2 color blue / black : rgb(255,188,14) diff --git a/data/magic.mse-game/game b/data/magic.mse-game/game index af45c611..2d182e33 100644 --- a/data/magic.mse-game/game +++ b/data/magic.mse-game/game @@ -971,31 +971,31 @@ card field: ############################################################## Statistics categories -statistics dimension: - name: card color2 - script: primary_card_color(card.card_color) - icon: stats/card_color.png - colors: - white : rgb(255,237,202) - blue : rgb(42,141,255) - black : rgb(33,33,33) - red : rgb(255,52,0) - green : rgb(138,230,0) - colorless : rgb(122,85,85) - artifact : rgb(188,192,195) - multicolor : rgb(255,188,14) - land : rgb(109,62,39) - hybrid : rgb(243,26,136) - group: white - group: blue - group: black - group: red - group: green - group: colorless - group: artifact - group: multicolor - group: land - group: hybrid +#statistics dimension: +# name: card color2 +# script: primary_card_color(card.card_color) +# icon: stats/card_color.png +# colors: +# white : rgb(255,237,202) +# blue : rgb(42,141,255) +# black : rgb(33,33,33) +# red : rgb(255,52,0) +# green : rgb(138,230,0) +# colorless : rgb(122,85,85) +# artifact : rgb(188,192,195) +# multicolor : rgb(255,188,14) +# land : rgb(109,62,39) +# hybrid : rgb(243,26,136) +# group: white +# group: blue +# group: black +# group: red +# group: green +# group: colorless +# group: artifact +# group: multicolor +# group: land +# group: hybrid statistics dimension: name: converted mana cost @@ -1010,11 +1010,16 @@ statistics dimension: icon: stats/colored_casting_cost.png #statistics dimension: -# name: power2 -# script: card.power +# name: p/t +# script: card.pt # numeric: true # icon: stats/power.png +#statistics dimension: +# name: word count +# type: word count +# display: list + #statistics dimension: # name: toughness2 # script: card.toughness @@ -1024,14 +1029,20 @@ statistics dimension: statistics category: name: color / rarity type: stack - dimension: card color2 + dimension: card color dimension: rarity -#statistics category: -# name: power / toughness -# type: scatter -# dimension: power2 -# dimension: toughness2 +statistics category: + name: power / toughness + type: scatter + dimension: power + dimension: toughness + +statistics category: + name: color / cost + type: scatter + dimension: card color + dimension: converted mana cost #statistics field: # name: creature type diff --git a/src/gui/control/graph.cpp b/src/gui/control/graph.cpp index c8811f4a..45d498d7 100644 --- a/src/gui/control/graph.cpp +++ b/src/gui/control/graph.cpp @@ -18,6 +18,7 @@ DECLARE_TYPEOF_COLLECTION(GraphP); DECLARE_TYPEOF_COLLECTION(int); DECLARE_TYPEOF_COLLECTION(vector); DECLARE_TYPEOF_COLLECTION(String); +DECLARE_TYPEOF_COLLECTION(UInt); DECLARE_TYPEOF(map); template inline T sgn(T v) { return v < 0 ? -1 : 1; } @@ -200,7 +201,7 @@ int find_bar_graph_column(double width, double x, int count) { double width_space = width / count; // including spacing double space = width_space / 5; // Find column in which the point could be located - int col = int(x / width_space); + int col = floor(x / width_space); if (col < 0 || col >= count) return -1; // not a column double in_col = x - col * width_space; if (in_col < space / 2) return -1; // left @@ -254,14 +255,14 @@ void BarGraph2D::draw(RotatedDC& dc, const vector& current, DrawLayer layer GraphAxis& axis1 = axis1_data(); // the major axis GraphAxis& axis2 = axis2_data(); // the stacked axis int count = int(axis1.groups.size()); - // Bar sizes + // Draw if (layer == LAYER_SELECTION) { // Highlight current column Color bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); int cur1 = this->axis1 < current.size() ? current[this->axis1] : -1; int cur2 = this->axis2 < current.size() ? current[this->axis2] : -1; if (cur1 >= 0) { - // draw that bar + // draw selected bar int start = 0; int j = 0; FOR_EACH_CONST(g2, axis2.groups) { @@ -278,8 +279,8 @@ void BarGraph2D::draw(RotatedDC& dc, const vector& current, DrawLayer layer } } else if (cur2 >= 0) { // entire row + // TODO } - // TODO } else if (layer == LAYER_VALUES) { // Draw bars dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); @@ -396,6 +397,72 @@ int PieGraph::findItem(const RealPoint& pos, const RealRect& rect) const { // ----------------------------------------------------------------------------- : Scatter Plot +void ScatterGraph::draw(RotatedDC& dc, const vector& current, DrawLayer layer) const { + if (!data || data->axes.size() <= max(axis1,axis2)) return; + // Rectangle for bars + RealRect rect = dc.getInternalRect(); + GraphAxis& axis1 = axis1_data(); // the major axis + GraphAxis& axis2 = axis2_data(); // the stacked axis + RealSize size(rect.width / axis1.groups.size(), rect.height / axis2.groups.size()); // size for a single cell + double step = min(size.width, size.height) / sqrt((double)max_value) / 2.01; + // Draw + dc.SetPen(*wxTRANSPARENT_PEN); + if (layer == LAYER_SELECTION) { + Color bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + int cur1 = this->axis1 < current.size() ? current[this->axis1] : -1; + int cur2 = this->axis2 < current.size() ? current[this->axis2] : -1; + if (cur1 >= 0 && cur2 >= 0) { + UInt value = values[cur1 * axis2.groups.size() + cur2]; + if (value) { + dc.SetBrush(lerp(bg,lerp(axis1.groups[cur1].color, axis2.groups[cur2].color, 0.5),0.5)); + dc.DrawCircle(RealPoint(rect.left() + cur1 * size.width, rect.bottom() - (cur2+1) * size.height) + size/2, sqrt((double)value) * step + 5); + } + } else if (cur1 >= 0) { + dc.SetBrush(lerp(bg,axis1.groups[cur1].color,0.3)); + dc.DrawRectangle(RealRect(rect.x + cur1 * size.width, rect.y, size.width, rect.height)); + } else if (cur2 >= 0) { + dc.SetBrush(lerp(bg,axis2.groups[cur2].color,0.3)); + dc.DrawRectangle(RealRect(rect.x, rect.bottom() - (cur2+1) * size.height, rect.width, size.height)); + } + } else { + size_t i = 0; + double x = rect.left(); + FOR_EACH_CONST(g1, axis1.groups) { + double y = rect.bottom() - size.height; + FOR_EACH_CONST(g2, axis2.groups) { + UInt value = values[i++]; + dc.SetBrush(lerp(g1.color, g2.color, 0.5)); + dc.DrawCircle(RealPoint(x,y) + size/2, sqrt((double)value) * step); + y -= size.height; + } + x += size.width; + } + } +} +bool ScatterGraph::findItem(const RealPoint& pos, const RealRect& rect, vector& out) const { + if (!data || data->axes.size() <= max(axis1,axis2)) return false; + // clicked item + GraphAxis& axis1 = axis1_data(); + GraphAxis& axis2 = axis2_data(); + int col = floor((pos.x - rect.x) / rect.width * axis1.groups.size()); + int row = floor((rect.bottom() - pos.y) / rect.height * axis2.groups.size()); + if (col < 0 || col >= (int)axis1.groups.size()) return false; + if (row < 0 || row >= (int)axis2.groups.size()) return false; + // done + out.clear(); + out.insert(out.begin(), data->axes.size(), -1); + out.at(this->axis1) = col; + out.at(this->axis2) = row; + return true; +} +void ScatterGraph::setData(const GraphDataP& d) { + Graph2D::setData(d); + // find maximum + max_value = 0; + FOR_EACH(v, values) { + max_value = max(max_value, v); + } +} // ----------------------------------------------------------------------------- : Graph Legend @@ -407,6 +474,7 @@ void GraphLabelAxis::draw(RotatedDC& dc, int current, DrawLayer layer) const { GraphAxis& axis = axis_data(); int count = int(axis.groups.size()); // Draw + dc.SetFont(*wxNORMAL_FONT); if (layer == LAYER_SELECTION) { // highlight selection } else if (layer != LAYER_AXES) { @@ -427,15 +495,18 @@ void GraphLabelAxis::draw(RotatedDC& dc, int current, DrawLayer layer) const { Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); if (draw_lines) { dc.SetPen(lerp(bg, fg, 0.5)); - for (int i = 1 ; i <= count ; ++i) { - dc.DrawLine(RealPoint(rect.x + i*width, rect.top()), RealPoint(rect.x + i*width, rect.bottom())); + for (int i = 0 ; i < count ; ++i) { + if (draw_lines == DRAW_LINES_BETWEEN) { + dc.DrawLine(RealPoint(rect.x + (i+1.0)*width, rect.top()), RealPoint(rect.x + (i+1.0)*width, rect.bottom())); + } else { + dc.DrawLine(RealPoint(rect.x + (i+0.5)*width, rect.top()), RealPoint(rect.x + (i+0.5)*width, rect.bottom() + 2)); + } } } // always draw axis line dc.SetPen(fg); dc.DrawLine(rect.topLeft(), rect.bottomLeft()); } else { - // TODO double height = rect.height / count; // width of an item // Draw labels double y = rect.bottom(); @@ -443,7 +514,7 @@ void GraphLabelAxis::draw(RotatedDC& dc, int current, DrawLayer layer) const { // draw label, aligned bottom center RealSize text_size = dc.GetTextExtent(g.name); //dc.SetClippingRegion(RealRect(x + 2, rect.bottom() + 3, width - 4, text_size.height)); - dc.DrawText(g.name, align_in_rect(ALIGN_MIDDLE_RIGHT, text_size, RealRect(-3, y, 0, -height))); + dc.DrawText(g.name, align_in_rect(ALIGN_MIDDLE_RIGHT, text_size, RealRect(-4, y, 0, -height))); //dc.DestroyClippingRegion(); y -= height; } @@ -452,8 +523,12 @@ void GraphLabelAxis::draw(RotatedDC& dc, int current, DrawLayer layer) const { Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); if (draw_lines) { dc.SetPen(lerp(bg, fg, 0.5)); - for (int i = 1 ; i <= count ; ++i) { - dc.DrawLine(RealPoint(rect.left(), rect.bottom() - i*height), RealPoint(rect.right(), rect.bottom() - i*height)); + for (int i = 0 ; i < count ; ++i) { + if (draw_lines == DRAW_LINES_BETWEEN) { + dc.DrawLine(RealPoint(rect.left(), rect.bottom() - (i+1.0)*height), RealPoint(rect.right(), rect.bottom() - (i+1.0)*height)); + } else { + dc.DrawLine(RealPoint(rect.left() - 2, rect.bottom() - (i+0.5)*height), RealPoint(rect.right(), rect.bottom() - (i+0.5)*height)); + } } } // always draw axis line @@ -466,10 +541,10 @@ int GraphLabelAxis::findItem(const RealPoint& pos, const RealRect& rect) const { GraphAxis& axis = axis_data(); int col; if (direction == HORIZONTAL) { - col = (pos.x - rect.x) / rect.width * axis.groups.size(); + col = floor((pos.x - rect.x) / rect.width * axis.groups.size()); if (pos.y < rect.bottom()) return -1; } else { - col = (pos.y - rect.y) / rect.height * axis.groups.size(); + col = floor((rect.bottom() - pos.y) / rect.height * axis.groups.size()); if (pos.x > rect.left()) return -1; } if (col < 0 || col >= (int)axis.groups.size()) return -1; @@ -582,12 +657,11 @@ void GraphControl::setLayout(GraphType type) { graph = new_intrusive5(combined, 23,8,7,20); break; } case GRAPH_TYPE_SCATTER: { - // TODO intrusive_ptr combined(new GraphContainer()); - combined->add(new_intrusive4(0, HORIZONTAL, false, true)); - combined->add(new_intrusive4(1, VERTICAL, false, true)); - //combined->add(new_intrusive2(0,1)); - graph = new_intrusive5(combined, 23,8,7,20); + combined->add(new_intrusive4(0, HORIZONTAL, false, DRAW_LINES_MID)); + combined->add(new_intrusive4(1, VERTICAL, false, DRAW_LINES_MID)); + combined->add(new_intrusive2(0,1)); + graph = new_intrusive5(combined, 80,8,7,20); break; } default: graph = GraphP(); diff --git a/src/gui/control/graph.hpp b/src/gui/control/graph.hpp index af076afe..13488c2c 100644 --- a/src/gui/control/graph.hpp +++ b/src/gui/control/graph.hpp @@ -181,6 +181,17 @@ class PieGraph : public Graph1D { virtual int findItem(const RealPoint& pos, const RealRect& rect) const; }; +/// A scatter plot +class ScatterGraph : public Graph2D { + public: + inline ScatterGraph(size_t axis1, size_t axis2) : Graph2D(axis1, axis2) {} + virtual void draw(RotatedDC& dc, const vector& current, DrawLayer layer) const; + virtual bool findItem(const RealPoint& pos, const RealRect& rect, vector& out) const; + virtual void setData(const GraphDataP& d); + private: + UInt max_value; ///< highest value +}; + /// The legend, used for pie graphs class GraphLegend : public Graph1D { public: @@ -192,11 +203,17 @@ class GraphLegend : public Graph1D { //class GraphTable { //}; +enum DrawLines +{ DRAW_LINES_NO +, DRAW_LINES_BETWEEN +, DRAW_LINES_MID +}; + /// Draws a horizontal/vertical axis for group labels class GraphLabelAxis : public Graph1D { public: - inline GraphLabelAxis(size_t axis, Direction direction, bool rotate = false, bool draw_lines = false) - : Graph1D(axis), direction(direction), rotate(rotate), draw_lines(draw_lines) + inline GraphLabelAxis(size_t axis, Direction direction, bool rotate = false, DrawLines draw_lines = DRAW_LINES_NO, bool label = false) + : Graph1D(axis), direction(direction), rotate(rotate), draw_lines(draw_lines), label(label) {} virtual void draw(RotatedDC& dc, int current, DrawLayer layer) const; virtual int findItem(const RealPoint& pos, const RealRect& rect) const; @@ -204,7 +221,8 @@ class GraphLabelAxis : public Graph1D { Direction direction; int levels; bool rotate; - bool draw_lines; + DrawLines draw_lines; + bool label; }; /// Draws an a vertical axis for counts diff --git a/src/util/rotation.cpp b/src/util/rotation.cpp index 2a810705..75d5fcd8 100644 --- a/src/util/rotation.cpp +++ b/src/util/rotation.cpp @@ -172,6 +172,11 @@ void RotatedDC::DrawRoundedRectangle(const RealRect& r, double radius) { dc.DrawRoundedRectangle(r_ext.x, r_ext.y, r_ext.width, r_ext.height, trS(radius)); } +void RotatedDC::DrawCircle(const RealPoint& center, double radius) { + wxPoint p = tr(center); + dc.DrawCircle(p.x + 1, p.y + 1, trS(radius)); +} + /// Convert radians to degrees double rad_to_deg(double rad) { return rad * (180.0 / M_PI); } /// Convert degrees to radians diff --git a/src/util/rotation.hpp b/src/util/rotation.hpp index 8eebabf3..bc989553 100644 --- a/src/util/rotation.hpp +++ b/src/util/rotation.hpp @@ -146,6 +146,7 @@ class RotatedDC : public Rotation { void DrawLine (const RealPoint& p1, const RealPoint& p2); void DrawRectangle(const RealRect& r); void DrawRoundedRectangle(const RealRect& r, double radius); + void DrawCircle(const RealPoint& center, double radius); /// Draw an arc of an ellipse, angles are in radians void DrawEllipticArc(const RealPoint& center, const RealSize& size, double start, double end); /// Draw spokes of an ellipse