mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-11 05:07:00 -04:00
Added support for custom colors for graphs;
Moved 'choice colors' from styel to field, split into 'choice colors' and 'choice colors cardlist'; Added support for pie graphs (no gui to use them, though); Fixed bugs caused by selecting a card before the set was changed on all panels. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@336 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
+210
-60
@@ -14,9 +14,12 @@
|
||||
DECLARE_TYPEOF_COLLECTION(GraphAxisP);
|
||||
DECLARE_TYPEOF_COLLECTION(GraphElementP);
|
||||
DECLARE_TYPEOF_COLLECTION(GraphGroup);
|
||||
DECLARE_TYPEOF_COLLECTION(GraphP);
|
||||
DECLARE_TYPEOF_COLLECTION(int);
|
||||
DECLARE_TYPEOF(map<String COMMA UInt>);
|
||||
|
||||
template <typename T> inline T sgn(T v) { return v < 0 ? -1 : 1; }
|
||||
|
||||
// ----------------------------------------------------------------------------- : Events
|
||||
|
||||
DEFINE_EVENT_TYPE(EVENT_GRAPH_SELECT);
|
||||
@@ -60,6 +63,7 @@ GraphData::GraphData(const GraphDataPre& d)
|
||||
} else {
|
||||
a->groups.push_back(GraphGroup(is, it->second));
|
||||
a->max = max(a->max, it->second);
|
||||
a->total += it->second;
|
||||
left--;
|
||||
}
|
||||
if (i > 100) {
|
||||
@@ -75,14 +79,26 @@ GraphData::GraphData(const GraphDataPre& d)
|
||||
FOR_EACH(c, counts) {
|
||||
a->groups.push_back(GraphGroup(c.first, c.second));
|
||||
a->max = max(a->max, c.second);
|
||||
a->total += c.second;
|
||||
}
|
||||
}
|
||||
// find some nice colors for the groups
|
||||
if (a->auto_color) {
|
||||
// colors
|
||||
if (a->auto_color == AUTO_COLOR_NO && a->colors) {
|
||||
// use colors from the table
|
||||
FOR_EACH(g, a->groups) {
|
||||
map<String,Color>::const_iterator it = a->colors->find(g.name);
|
||||
if (it != a->colors->end()) {
|
||||
g.color = it->second;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// find some nice colors for the groups
|
||||
double hue = 0.6; // start hue
|
||||
bool first = true;
|
||||
FOR_EACH(g, a->groups) {
|
||||
double amount = double(g.size) / size; // amount this group takes
|
||||
double amount = a->auto_color == AUTO_COLOR_EVEN
|
||||
? 1. / a->groups.size()
|
||||
: double(g.size) / a->total; // amount this group takes
|
||||
if (!first) hue += amount/2;
|
||||
g.color = hsl2rgb(hue, 1.0, 0.5);
|
||||
hue += amount / 2;
|
||||
@@ -118,8 +134,8 @@ GraphData::GraphData(const GraphDataPre& d)
|
||||
|
||||
// ----------------------------------------------------------------------------- : Graph1D
|
||||
|
||||
void Graph1D::draw(RotatedDC& dc, const vector<int>& current) const {
|
||||
draw(dc, axis < current.size() ? current.at(axis) : -1);
|
||||
void Graph1D::draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const {
|
||||
draw(dc, axis < current.size() ? current.at(axis) : -1, layer);
|
||||
}
|
||||
bool Graph1D::findItem(const RealPoint& pos, const RealRect& rect, vector<int>& out) const {
|
||||
int i = findItem(pos, rect);
|
||||
@@ -134,10 +150,10 @@ bool Graph1D::findItem(const RealPoint& pos, const RealRect& rect, vector<int>&
|
||||
|
||||
// ----------------------------------------------------------------------------- : Bar Graph
|
||||
|
||||
void BarGraph::draw(RotatedDC& dc, int current) const {
|
||||
void BarGraph::draw(RotatedDC& dc, int current, DrawLayer layer) const {
|
||||
if (!data) return;
|
||||
// Rectangle for bars
|
||||
RealRect rect = dc.getInternalRect().move(23, 8, -30, -28);
|
||||
RealRect rect = dc.getInternalRect();
|
||||
// Bar sizes
|
||||
GraphAxis& axis = axis_data();
|
||||
int count = int(axis.groups.size());
|
||||
@@ -145,56 +161,39 @@ void BarGraph::draw(RotatedDC& dc, int current) const {
|
||||
double width = width_space / 5 * 4;
|
||||
double space = width_space / 5;
|
||||
double step_height = rect.height / axis.max; // multiplier for bar height
|
||||
// Highlight current column
|
||||
Color bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
if (current >= 0) {
|
||||
double x = rect.x + width_space * current;
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(lerp(bg, axis.groups[current].color, 0.25));
|
||||
dc.DrawRectangle(RealRect(x + space / 2 - 5, rect.bottom() + 1 + 15,
|
||||
width + 10, -step_height * axis.groups[current].size - 1.999 - 20));
|
||||
dc.SetBrush(lerp(bg, axis.groups[current].color, 0.5));
|
||||
dc.DrawRectangle(RealRect(x + space / 2 - 2, rect.bottom() + 1 + 2,
|
||||
width + 4, -step_height * axis.groups[current].size - 1.999 - 4));
|
||||
}
|
||||
// How many labels and lines to draw?
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
UInt label_step = UInt(max(1.0, (dc.GetCharHeight() + 1) / step_height));
|
||||
// Draw backlines (horizontal) and value labels
|
||||
dc.SetPen(lerp(bg, fg, 0.5));
|
||||
for (UInt i = 0 ; i <= axis.max ; ++i) {
|
||||
if (i % label_step == 0) {
|
||||
int y = int(rect.bottom() - i * step_height);
|
||||
dc.DrawLine(RealPoint(rect.left() - 2, y), RealPoint(rect.right(), y));
|
||||
// draw label, aligned middle right
|
||||
String label; label << i;
|
||||
RealSize text_size = dc.GetTextExtent(label);
|
||||
dc.DrawText(label, align_in_rect(ALIGN_MIDDLE_RIGHT, text_size, RealRect(rect.x - 4, y, 0, 0)));
|
||||
if (layer == LAYER_SELECTION) {
|
||||
// Highlight current column
|
||||
Color bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
if (current >= 0) {
|
||||
double x = rect.x + width_space * current;
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(lerp(bg, axis.groups[current].color, 0.25));
|
||||
dc.DrawRectangle(RealRect(x + space / 2 - 5, rect.y + 1 - 15 * sgn(step_height),
|
||||
width + 10, step_height * axis.groups[current].size + (1.999 + 20) * sgn(step_height)));
|
||||
dc.SetBrush(lerp(bg, axis.groups[current].color, 0.5));
|
||||
dc.DrawRectangle(RealRect(x + space / 2 - 2, rect.y + 1 - 2 * sgn(step_height),
|
||||
width + 4, step_height * axis.groups[current].size + (1.999 + 4) * sgn(step_height)));
|
||||
}
|
||||
} else if (layer == LAYER_VALUES) {
|
||||
// Draw bars
|
||||
Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
dc.SetPen(fg);
|
||||
double x = rect.x;
|
||||
FOR_EACH_CONST(g, axis.groups) {
|
||||
// draw bar
|
||||
dc.SetBrush(g.color);
|
||||
dc.DrawRectangle(RealRect(x + space / 2, rect.y - 1 * sgn(step_height), width, (int)(rect.y + step_height * g.size) - rect.y + 1 * sgn(step_height)));
|
||||
// draw label, aligned bottom center
|
||||
RealSize text_size = dc.GetTextExtent(g.name);
|
||||
dc.SetClippingRegion(RealRect(x + 2, rect.y + 3, width_space - 4, text_size.height));
|
||||
dc.DrawText(g.name, align_in_rect(ALIGN_TOP_CENTER, text_size, RealRect(x, rect.y + 3, width_space, 0)));
|
||||
dc.DestroyClippingRegion();
|
||||
x += width_space;
|
||||
}
|
||||
}
|
||||
// Draw axes
|
||||
dc.SetPen(fg);
|
||||
dc.DrawLine(rect.bottomLeft() - RealSize(2,0), rect.bottomRight());
|
||||
dc.DrawLine(rect.topLeft(), rect.bottomLeft());
|
||||
// Draw bars
|
||||
double x = rect.x;
|
||||
FOR_EACH_CONST(g, axis.groups) {
|
||||
// draw bar
|
||||
dc.SetBrush(g.color);
|
||||
dc.DrawRectangle(RealRect(x + space / 2, rect.bottom() + 1, width, (int)(rect.bottom() - step_height * g.size) - rect.bottom() - 1));
|
||||
// draw label, aligned bottom center
|
||||
RealSize text_size = dc.GetTextExtent(g.name);
|
||||
dc.SetClippingRegion(RealRect(x + 2, rect.bottom() + 3, width_space - 4, text_size.height));
|
||||
dc.DrawText(g.name, align_in_rect(ALIGN_TOP_CENTER, text_size, RealRect(x, rect.bottom() + 3, width_space, 0)));
|
||||
dc.DestroyClippingRegion();
|
||||
x += width_space;
|
||||
}
|
||||
}
|
||||
int BarGraph::findItem(const RealPoint& pos, const RealRect& rect1) const {
|
||||
int BarGraph::findItem(const RealPoint& pos, const RealRect& rect) const {
|
||||
if (!data) return -1;
|
||||
// Rectangle for bars
|
||||
RealRect rect = rect1.move(23, 8, -30, -28);
|
||||
// Bar sizes
|
||||
GraphAxis& axis = axis_data();
|
||||
int count = int(axis.groups.size());
|
||||
@@ -203,10 +202,10 @@ int BarGraph::findItem(const RealPoint& pos, const RealRect& rect1) const {
|
||||
// Find column in which this point could be located
|
||||
int col = int((pos.x - rect.x) / width_space);
|
||||
double in_col = (pos.x - rect.x) - col * width_space;
|
||||
if (in_col < space / 2 || // left
|
||||
in_col > width_space - space / 2 || // right
|
||||
pos.y > rect.bottom() || // below
|
||||
pos.y < rect.top()) { // above
|
||||
if (in_col < space / 2 || // left
|
||||
in_col > width_space - space / 2 || // right
|
||||
pos.y > max(rect.top(), rect.bottom()) || // below
|
||||
pos.y < min(rect.top(), rect.bottom())) { // above
|
||||
return -1;
|
||||
}
|
||||
if (col < 0 || (size_t)col >= axis_data().groups.size()) {
|
||||
@@ -218,18 +217,165 @@ int BarGraph::findItem(const RealPoint& pos, const RealRect& rect1) const {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Pie Graph
|
||||
|
||||
// ----------------------------------------------------------------------------- : Scatter Graph
|
||||
void PieGraph::draw(RotatedDC& dc, int current, DrawLayer layer) const {
|
||||
if (!data) return;
|
||||
// Rectangle for the pie
|
||||
GraphAxis& axis = axis_data();
|
||||
RealRect rect = dc.getInternalRect();
|
||||
double size = min(rect.width, rect.height);
|
||||
RealSize pie_size(size, size);
|
||||
RealSize pie_size_large(size+20, size+20);
|
||||
RealPoint pie_pos = rect.position() + rect.size() / 2;
|
||||
//RealPoint pos = align_in_rect(ALIGN_MIDDLE_CENTER, RealSize(size,size), rect);
|
||||
// draw items
|
||||
if (layer == LAYER_VALUES) {
|
||||
Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
dc.SetPen(fg);
|
||||
// draw pies
|
||||
double angle = 0;
|
||||
int i = 0;
|
||||
FOR_EACH_CONST(g, axis.groups) {
|
||||
// draw pie
|
||||
dc.SetBrush(g.color);
|
||||
if (g.size > 0) {
|
||||
double end_angle = angle + 2 * M_PI * (double)g.size / axis.total;
|
||||
dc.DrawEllipticArc(pie_pos, i == current ? pie_size_large : pie_size, angle, end_angle);
|
||||
angle = end_angle;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
// draw spokes
|
||||
if (axis.groups.size() > 1) {
|
||||
angle = 0;
|
||||
FOR_EACH_CONST(g, axis.groups) {
|
||||
if (g.size > 0) {
|
||||
dc.DrawEllipticSpoke(pie_pos, pie_size, angle);
|
||||
angle += 2 * M_PI * (double)g.size / axis.total;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int PieGraph::findItem(const RealPoint& pos, const RealRect& rect) const {
|
||||
if (!data) return -1;
|
||||
// Rectangle for the pie
|
||||
GraphAxis& axis = axis_data();
|
||||
double size = min(rect.width, rect.height);
|
||||
RealPoint pie_pos = rect.position() + rect.size() / 2;
|
||||
// position in circle
|
||||
Vector2D delta = pos - pie_pos;
|
||||
if (delta.lengthSqr() > size*size) {
|
||||
return -1; // outside circle
|
||||
}
|
||||
double pos_angle = atan2(-delta.y, delta.x); // in range [-pi..pi]
|
||||
if (pos_angle < 0) pos_angle += 2 * M_PI;
|
||||
// find angle
|
||||
double angle = 0;
|
||||
int i = 0;
|
||||
FOR_EACH_CONST(g, axis.groups) {
|
||||
angle += 2 * M_PI * (double)g.size / axis.total;
|
||||
if (angle > pos_angle) return i;
|
||||
++i;
|
||||
}
|
||||
return -1; //should not happen
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Scatter Plot
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Graph Legend
|
||||
|
||||
// ----------------------------------------------------------------------------- : Graph value axis
|
||||
|
||||
void GraphValueAxis::draw(RotatedDC& dc, int current, DrawLayer layer) const {
|
||||
if (layer != LAYER_AXES) return;
|
||||
if (!data) return;
|
||||
// How many labels and lines to draw?
|
||||
RealRect rect = dc.getInternalRect();
|
||||
GraphAxis& axis = axis_data();
|
||||
double step_height = rect.height / axis.max; // height of a single value
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
UInt label_step = UInt(max(1.0, (dc.GetCharHeight() + 1) / step_height)); // values per labeled line
|
||||
// Colors
|
||||
Color bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
// Draw backlines (horizontal) and value labels
|
||||
dc.SetPen(lerp(bg, fg, 0.5));
|
||||
for (UInt i = 0 ; i <= axis.max ; ++i) {
|
||||
if (i % label_step == 0) {
|
||||
int y = rect.top() + i * step_height;
|
||||
dc.DrawLine(RealPoint(rect.left() - 2, y), RealPoint(rect.right(), y));
|
||||
// draw label, aligned middle right
|
||||
String label; label << i;
|
||||
RealSize text_size = dc.GetTextExtent(label);
|
||||
dc.DrawText(label, align_in_rect(ALIGN_MIDDLE_RIGHT, text_size, RealRect(rect.x - 4, y, 0, 0)));
|
||||
}
|
||||
}
|
||||
// Draw axes
|
||||
dc.SetPen(fg);
|
||||
dc.DrawLine(rect.topLeft() - RealSize(2,0), rect.topRight());
|
||||
dc.DrawLine(rect.topLeft(), rect.bottomLeft());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Graph with margins
|
||||
|
||||
void GraphWithMargins::draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const {
|
||||
RealRect inner = dc.getInternalRect().move(margin_left, margin_top, - margin_left - margin_right, - margin_top - margin_bottom);
|
||||
if (upside_down) { inner.y += inner.height; inner.height = -inner.height; }
|
||||
Rotation new_size(0, inner);
|
||||
Rotater rot(dc, new_size);
|
||||
graph->draw(dc, current, layer);
|
||||
}
|
||||
bool GraphWithMargins::findItem(const RealPoint& pos, const RealRect& rect, vector<int>& out) const {
|
||||
RealRect inner = rect.move(margin_left, margin_top, - margin_left - margin_right, - margin_top - margin_bottom);
|
||||
if (upside_down) { inner.y += inner.height; inner.height = -inner.height; }
|
||||
return graph->findItem(pos, inner, out);
|
||||
}
|
||||
void GraphWithMargins::setData(const GraphDataP& d) {
|
||||
Graph::setData(d);
|
||||
graph->setData(d);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Graph Container
|
||||
|
||||
void GraphContainer::draw(RotatedDC& dc, const vector<int>& current, DrawLayer layer) const {
|
||||
FOR_EACH_CONST(g, items) {
|
||||
g->draw(dc, current, layer);
|
||||
}
|
||||
}
|
||||
bool GraphContainer::findItem(const RealPoint& pos, const RealRect& rect, vector<int>& out) const {
|
||||
FOR_EACH_CONST_REVERSE(g, items) {
|
||||
if (g->findItem(pos, rect, out)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void GraphContainer::setData(const GraphDataP& d) {
|
||||
Graph::setData(d);
|
||||
FOR_EACH(g, items) {
|
||||
g->setData(d);
|
||||
}
|
||||
}
|
||||
void GraphContainer::add(const GraphP& graph) {
|
||||
items.push_back(graph);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : GraphControl
|
||||
|
||||
GraphControl::GraphControl(Window* parent, int id)
|
||||
: wxControl(parent, id)
|
||||
{
|
||||
graph = new_shared1<BarGraph>(0);
|
||||
//*
|
||||
shared_ptr<GraphContainer> combined(new GraphContainer());
|
||||
combined->add(new_shared1<GraphValueAxis>(0));
|
||||
combined->add(new_shared1<BarGraph>(0));
|
||||
graph = new_shared6<GraphWithMargins>(combined, 23,8,7,20, true);
|
||||
/*/
|
||||
shared_ptr<GraphContainer> combined(new GraphContainer());
|
||||
combined->add(new_shared1<PieGraph>(0));
|
||||
graph = new_shared6<GraphWithMargins>(combined, 20,20,20,20, false);
|
||||
//*/
|
||||
}
|
||||
|
||||
void GraphControl::setData(const GraphDataPre& data) {
|
||||
@@ -250,7 +396,11 @@ void GraphControl::onPaint(wxPaintEvent&) {
|
||||
rdc.SetPen(*wxTRANSPARENT_PEN);
|
||||
rdc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
rdc.DrawRectangle(rdc.getInternalRect());
|
||||
if (graph) graph->draw(rdc, current_item);
|
||||
if (graph) {
|
||||
for (int layer = LAYER_BOTTOM ; layer < LAYER_COUNT ; ++layer) {
|
||||
graph->draw(rdc, current_item, (DrawLayer)layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphControl::onSize(wxSizeEvent&) {
|
||||
|
||||
Reference in New Issue
Block a user