diff --git a/src/gfx/color.cpp b/src/gfx/color.cpp new file mode 100644 index 00000000..1f56393f --- /dev/null +++ b/src/gfx/color.cpp @@ -0,0 +1,48 @@ +//+----------------------------------------------------------------------------+ +//| Description: Magic Set Editor - Program to make Magic (tm) cards | +//| Copyright: (C) 2001 - 2006 Twan van Laarhoven | +//| License: GNU General Public License 2 or later (see file COPYING) | +//+----------------------------------------------------------------------------+ + +// ----------------------------------------------------------------------------- : Includes + +#include + +// ----------------------------------------------------------------------------- : Color utility functions + +Color lerp(const Color& a, const Color& b, double t) { + return Color(a.Red() + (b.Red() - a.Red() ) * t, + a.Green() + (b.Green() - a.Green()) * t, + a.Blue() + (b.Blue() - a.Blue() ) * t); +} + + +int hsl2rgbp(double t1, double t2, double t3) { + // adjust t3 to [0...1) + if (t3 < 0.0) t3 += 1; + else if (t3 > 1.0) t3 -= 1; + // determine color + if (6.0 * t3 < 1) return (int)(255 * (t1 + (t2-t1) * 6.0 * t3) ); + if (2.0 * t3 < 1) return (int)(255 * (t2) ); + if (3.0 * t3 < 2) return (int)(255 * (t1 + (t2-t1) * 6.0 * (2.0/3.0 - t3)) ); + else return (int)(255 * (t1) ); +} +Color hsl2rgb(double h, double s, double l) { + double t2 = l < 0.5 ? l * (1.0 + s) : + l * (1.0 - s) + s; + double t1 = 2.0 * l - t2; + return Color( + hsl2rgbp(t1, t2, h + 1.0/3.0), + hsl2rgbp(t1, t2, h) , + hsl2rgbp(t1, t2, h - 1.0/3.0) + ); +} + + +Color darken(const Color& c) { + return Color( + c.Red() * 8 / 10, + c.Green() * 8 / 10, + c.Blue() * 8 / 10 + ); +} diff --git a/src/gfx/gfx.hpp b/src/gfx/gfx.hpp index 9094470b..6e69e9bf 100644 --- a/src/gfx/gfx.hpp +++ b/src/gfx/gfx.hpp @@ -144,7 +144,7 @@ class ContourMask { UInt *lefts, *rights; }; -// ----------------------------------------------------------------------------- : Utility +// ----------------------------------------------------------------------------- : Color utility functions inline int bot(int x) { return max(0, x); } ///< bottom range check for color values inline int top(int x) { return min(255, x); } ///< top range check for color values @@ -153,5 +153,11 @@ inline int col(int x) { return top(bot(x)); } ///< top and bottom range check fo /// Linear interpolation between colors Color lerp(const Color& a, const Color& b, double t); +/// convert HSL to RGB, h,s,l must be in range [0...1) +Color hsl2rgb(double h, double s, double l); + +/// A darker version of a color +Color darken(const Color& c); + // ----------------------------------------------------------------------------- : EOF #endif diff --git a/src/gui/control/gallery_list.cpp b/src/gui/control/gallery_list.cpp index f4ae4ded..5d36ebd9 100644 --- a/src/gui/control/gallery_list.cpp +++ b/src/gui/control/gallery_list.cpp @@ -7,6 +7,7 @@ // ----------------------------------------------------------------------------- : Includes #include +#include // ----------------------------------------------------------------------------- : Events @@ -111,14 +112,6 @@ void GalleryList::onKeyDown(wxKeyEvent& ev) { } } -// Linear interpolation between colors -// MOVE ME, declared in gfx.hpp -Color lerp(const Color& a, const Color& b, double t) { - return Color(a.Red() + (b.Red() - a.Red() ) * t, - a.Green() + (b.Green() - a.Green()) * t, - a.Blue() + (b.Blue() - a.Blue() ) * t); -} - wxSize GalleryList::DoGetBestSize() const { wxSize ws = GetSize(), cs = GetClientSize(); const int w = item_size.GetWidth() + 2 * MARGIN; diff --git a/src/gui/control/graph.cpp b/src/gui/control/graph.cpp index 2ae83017..486b62e7 100644 --- a/src/gui/control/graph.cpp +++ b/src/gui/control/graph.cpp @@ -7,19 +7,136 @@ // ----------------------------------------------------------------------------- : Includes #include +#include #include -// ----------------------------------------------------------------------------- : Graph +DECLARE_TYPEOF_COLLECTION(GraphAxisP); +DECLARE_TYPEOF_COLLECTION(GraphElementP); +DECLARE_TYPEOF_COLLECTION(GraphGroup); +typedef map map_String_UInt; +DECLARE_TYPEOF(map_String_UInt); +// ----------------------------------------------------------------------------- : GraphData + +GraphElement::GraphElement(const String& v1) { + values.push_back(v1); +} +GraphElement::GraphElement(const String& v1, const String& v2) { + values.push_back(v1); + values.push_back(v2); +} + + +GraphData::GraphData(const GraphDataPre& d) + : axes(d.axes) +{ + // total size + size = (UInt)d.elements.size(); + // find groups on each axis + size_t value_count = 1; + size_t i = 0; + FOR_EACH(a, axes) { + map counts; // note: default constructor for UInt() does initialize to 0 + FOR_EACH_CONST(e, d.elements) { + counts[e->values[i]] += 1; + } + // TODO: allow some ordering in the groups, and allow colors to be passed + FOR_EACH(c, counts) { + a->groups.push_back(GraphGroup(c.first, c.second)); + a->max = max(a->max, c.second); + } + // find some nice colors for the groups + if (a->auto_color) { + double hue = 0.6; // start hue + bool first = true; + FOR_EACH(g, a->groups) { + double amount = double(g.size) / size; // amount this group takes + if (!first) hue += amount/2; + g.color = hsl2rgb(hue, 1.0, 0.5); + hue += amount / 2; + first = false; + } + } + value_count *= a->groups.size(); + ++i; + } + // count elements in each position + values.clear(); + values.resize(value_count, 0); + FOR_EACH_CONST(e, d.elements) { + // find index j in elements + size_t i = 0, j = 0; + FOR_EACH(a, axes) { + String v = e->values[i]; + size_t k = 0, l = 0; + FOR_EACH(g, a->groups) { + if (v == g.name) { + k = l; + break; + } + l += 1; + } + j = j * a->groups.size() + k; + ++i; + } + values[j] += 1; + } +} + + +// ----------------------------------------------------------------------------- : Graph1D + +bool Graph1D::findItem(const RealPoint& pos, vector& out) const { + int i = findItem(pos); + if (i == -1) return false; + else { + out.clear(); + out.insert(out.begin(), data->axes.size(), -1); + out.at(axis) = i; + return true; + } +} + +// ----------------------------------------------------------------------------- : Bar Graph + +void BarGraph::draw(RotatedDC& dc) const { + // TODO +} +int BarGraph::findItem(const RealPoint& pos) const { + return -1; // TODO +} + +// ----------------------------------------------------------------------------- : Pie Graph + +// ----------------------------------------------------------------------------- : Graph Legend // ----------------------------------------------------------------------------- : GraphControl GraphControl::GraphControl(Window* parent, int id) : wxControl(parent, id) -{} +{ + graph = new_shared1(0); +} + +void GraphControl::setData(const GraphDataPre& data) { + setData(new_shared1(data)); +} +void GraphControl::setData(const GraphDataP& data) { + if (graph) { + graph->setData(data); + current_item.clear(); // TODO : preserver selection + Refresh(false); + } +} void GraphControl::onPaint(wxPaintEvent&) { wxBufferedPaintDC dc(this); + wxSize cs = GetClientSize(); + RotatedDC rdc(dc, 0, RealRect(RealPoint(0,0),cs), 1, false); + rdc.SetPen(*wxTRANSPARENT_PEN); + rdc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + rdc.DrawRectangle(rdc.getInternalRect()); + if (graph) graph->draw(rdc); } void GraphControl::onSize(wxSizeEvent&) { diff --git a/src/gui/control/graph.hpp b/src/gui/control/graph.hpp index d2e7dbfe..0bd9a5cf 100644 --- a/src/gui/control/graph.hpp +++ b/src/gui/control/graph.hpp @@ -23,25 +23,39 @@ DECLARE_POINTER_TYPE(Graph); /** A group is rendered as a single bar or pie slice */ class GraphGroup { public: + GraphGroup(const String& name, UInt size, const Color& color = *wxBLACK) + : name(name), color(color), size(size) + {} + String name; ///< Name of this position Color color; ///< Associated color - int size; ///< Number of elements in this group + UInt size; ///< Number of elements in this group }; /// An axis in a graph, consists of a list of groups /** The sum of groups.sum = sum of all elements in the data */ class GraphAxis { public: + GraphAxis(const String& name, bool auto_color = true) + : name(name) + , auto_color(auto_color) + , max(0) + {} + String name; ///< Name/label of this axis bool auto_color; ///< Automatically assign colors to the groups on this axis vector groups; ///< Groups along this axis - int max; ///< Maximum size of the groups + UInt max; ///< Maximum size of the groups }; /// A single data point of a graph class GraphElement { public: - vector axis_groups; ///< Group name for each axis + GraphElement() {} + GraphElement(const String& v1); + GraphElement(const String& v1, const String& v2); + + vector values; ///< Group name for each axis }; /// Data to be displayed in a graph, not processed yet @@ -54,11 +68,11 @@ class GraphDataPre { /// Data to be displayed in a graph class GraphData { public: - GraphData(GraphDataPre); + GraphData(const GraphDataPre&); vector axes; ///< The axes in the data - vector values; ///< Multi dimensional (dim = axes.size()) array of values - int size; ///< Total number of elements + vector values; ///< Multi dimensional (dim = axes.size()) array of values + UInt size; ///< Total number of elements }; @@ -69,7 +83,7 @@ class GraphData { class Graph { public: /// Draw this graph, filling the internalRect() of the dc. - virtual void draw(RotatedDC& dc) = 0; + virtual void draw(RotatedDC& dc) const = 0; /// Find the item at the given position, position is normalized to [0..1) virtual bool findItem(const RealPoint& pos, vector& out) const { return false; } /// Change the data @@ -84,7 +98,7 @@ class Graph { class Graph1D : public Graph { public: inline Graph1D(size_t axis) : axis(axis) {} - virtual bool findItem(const RealPoint& pos, vector& out) const { return false; } + virtual bool findItem(const RealPoint& pos, vector& out) const; protected: size_t axis; /// Find an item, return the position along the axis, or -1 if not found diff --git a/src/mse.vcproj b/src/mse.vcproj index f0b686e0..4a68a79e 100644 --- a/src/mse.vcproj +++ b/src/mse.vcproj @@ -1240,6 +1240,33 @@ + + + + + + + + + + + + + +