mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-13 05:57:00 -04:00
numeric statistics dimensions; minor tweaks of graph
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@150 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
StatsDimension::StatsDimension()
|
StatsDimension::StatsDimension()
|
||||||
: automatic(false)
|
: automatic(false)
|
||||||
|
, numeric(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
StatsDimension::StatsDimension(const Field& field)
|
StatsDimension::StatsDimension(const Field& field)
|
||||||
@@ -20,6 +21,7 @@ StatsDimension::StatsDimension(const Field& field)
|
|||||||
, name (field.name)
|
, name (field.name)
|
||||||
, description (field.description)
|
, description (field.description)
|
||||||
, icon_filename(field.icon_filename)
|
, icon_filename(field.icon_filename)
|
||||||
|
, numeric(false)
|
||||||
{
|
{
|
||||||
// initialize script, card.{field_name}
|
// initialize script, card.{field_name}
|
||||||
Script& s = script.getScript();
|
Script& s = script.getScript();
|
||||||
@@ -34,6 +36,7 @@ IMPLEMENT_REFLECTION(StatsDimension) {
|
|||||||
REFLECT(description);
|
REFLECT(description);
|
||||||
REFLECT_N("icon", icon_filename);
|
REFLECT_N("icon", icon_filename);
|
||||||
REFLECT(script);
|
REFLECT(script);
|
||||||
|
REFLECT(numeric);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class StatsDimension {
|
|||||||
String description; ///< Description, used in status bar
|
String description; ///< Description, used in status bar
|
||||||
String icon_filename; ///< Icon for lists
|
String icon_filename; ///< Icon for lists
|
||||||
OptionalScript script; ///< Script that determines the value(s)
|
OptionalScript script; ///< Script that determines the value(s)
|
||||||
|
bool numeric; ///< Are the values numeric? If so, they require special sorting
|
||||||
bool automatic; ///< Based on a card field?
|
bool automatic; ///< Based on a card field?
|
||||||
|
|
||||||
DECLARE_REFLECTION();
|
DECLARE_REFLECTION();
|
||||||
|
|||||||
+13
-5
@@ -68,9 +68,6 @@ void linear_blend(Image& img1, const Image& img2, double x1,double y1, double x2
|
|||||||
*/
|
*/
|
||||||
void mask_blend(Image& img1, const Image& img2, const Image& mask);
|
void mask_blend(Image& img1, const Image& img2, const Image& mask);
|
||||||
|
|
||||||
/// Use the red channel of img_alpha as alpha channel for img
|
|
||||||
void set_alpha(Image& img, const Image& img_alpha);
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Effects
|
// ----------------------------------------------------------------------------- : Effects
|
||||||
|
|
||||||
/// Saturate an image, amount should be in range [0...100]
|
/// Saturate an image, amount should be in range [0...100]
|
||||||
@@ -116,16 +113,27 @@ void draw_combine_image(DC& dc, UInt x, UInt y, const Image& img, ImageCombine c
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Masks
|
// ----------------------------------------------------------------------------- : Masks
|
||||||
|
|
||||||
|
/// Use the red channel of img_alpha as alpha channel for img
|
||||||
|
void set_alpha(Image& img, const Image& img_alpha);
|
||||||
|
|
||||||
/// An alpha mask is an alpha channel that can be copied to another image
|
/// An alpha mask is an alpha channel that can be copied to another image
|
||||||
/** It is created by treating black in the source image as transparent and white (red) as opaque
|
/** It is created by treating black in the source image as transparent and white (red) as opaque
|
||||||
*/
|
*/
|
||||||
class AlphaMask {
|
class AlphaMask {
|
||||||
public:
|
public:
|
||||||
AlphaMask();
|
AlphaMask(const Image& mask);
|
||||||
~AlphaMask();
|
~AlphaMask();
|
||||||
|
|
||||||
// TODO
|
/// Apply the alpha mask to an image
|
||||||
|
void setAlpha(Image& i) const;
|
||||||
|
/// Apply the alpha mask to a bitmap
|
||||||
|
void setAlpha(Bitmap& b) const;
|
||||||
|
|
||||||
|
/// Is the given location fully transparent?
|
||||||
|
bool isTransparent(int x, int y) const;
|
||||||
|
|
||||||
|
/// Size of the mask
|
||||||
|
wxSize size;
|
||||||
private:
|
private:
|
||||||
Byte* alpha;
|
Byte* alpha;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -126,6 +126,8 @@ void DataEditor::createTabIndex() {
|
|||||||
}
|
}
|
||||||
void DataEditor::onInit() {
|
void DataEditor::onInit() {
|
||||||
createTabIndex();
|
createTabIndex();
|
||||||
|
current_viewer = nullptr;
|
||||||
|
current_editor = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Clipboard & Formatting
|
// ----------------------------------------------------------------------------- : Clipboard & Formatting
|
||||||
|
|||||||
@@ -42,9 +42,36 @@ GraphData::GraphData(const GraphDataPre& d)
|
|||||||
counts[e->values[i]] += 1;
|
counts[e->values[i]] += 1;
|
||||||
}
|
}
|
||||||
// TODO: allow some ordering in the groups, and allow colors to be passed
|
// TODO: allow some ordering in the groups, and allow colors to be passed
|
||||||
FOR_EACH(c, counts) {
|
if (a->numeric) {
|
||||||
a->groups.push_back(GraphGroup(c.first, c.second));
|
// TODO: start at something other than 0?
|
||||||
a->max = max(a->max, c.second);
|
// TODO: support fractions?
|
||||||
|
size_t left = counts.size();
|
||||||
|
int i = 0;
|
||||||
|
while (left) {
|
||||||
|
String is = String() << i++;
|
||||||
|
map<String,UInt>::const_iterator it = counts.find(is);
|
||||||
|
if (it == counts.end()) {
|
||||||
|
// not found, add a 0 bar
|
||||||
|
a->groups.push_back(GraphGroup(is, 0));
|
||||||
|
} else {
|
||||||
|
a->groups.push_back(GraphGroup(is, it->second));
|
||||||
|
a->max = max(a->max, it->second);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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
|
// find some nice colors for the groups
|
||||||
if (a->auto_color) {
|
if (a->auto_color) {
|
||||||
@@ -106,7 +133,7 @@ bool Graph1D::findItem(const RealPoint& pos, const RealRect& rect, vector<int>&
|
|||||||
void BarGraph::draw(RotatedDC& dc, int current) const {
|
void BarGraph::draw(RotatedDC& dc, int current) const {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
// Rectangle for bars
|
// Rectangle for bars
|
||||||
RealRect rect = dc.getInternalRect().move(15, 5, -20, -20);
|
RealRect rect = dc.getInternalRect().move(23, 8, -30, -28);
|
||||||
// Bar sizes
|
// Bar sizes
|
||||||
GraphAxis& axis = axis_data();
|
GraphAxis& axis = axis_data();
|
||||||
int count = int(axis.groups.size());
|
int count = int(axis.groups.size());
|
||||||
@@ -151,10 +178,10 @@ void BarGraph::draw(RotatedDC& dc, int current) const {
|
|||||||
FOR_EACH_CONST(g, axis.groups) {
|
FOR_EACH_CONST(g, axis.groups) {
|
||||||
// draw bar
|
// draw bar
|
||||||
dc.SetBrush(g.color);
|
dc.SetBrush(g.color);
|
||||||
dc.DrawRectangle(RealRect(x + space / 2, rect.bottom() + 1, width, -step_height * g.size - 1.999));
|
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
|
// draw label, aligned bottom center
|
||||||
RealSize text_size = dc.GetTextExtent(g.name);
|
RealSize text_size = dc.GetTextExtent(g.name);
|
||||||
dc.SetClippingRegion(RealRect(x + 2, rect.bottom(), width_space - 4, text_size.height));
|
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.DrawText(g.name, align_in_rect(ALIGN_TOP_CENTER, text_size, RealRect(x, rect.bottom() + 3, width_space, 0)));
|
||||||
dc.DestroyClippingRegion();
|
dc.DestroyClippingRegion();
|
||||||
x += width_space;
|
x += width_space;
|
||||||
@@ -163,7 +190,7 @@ void BarGraph::draw(RotatedDC& dc, int current) const {
|
|||||||
int BarGraph::findItem(const RealPoint& pos, const RealRect& rect1) const {
|
int BarGraph::findItem(const RealPoint& pos, const RealRect& rect1) const {
|
||||||
if (!data) return -1;
|
if (!data) return -1;
|
||||||
// Rectangle for bars
|
// Rectangle for bars
|
||||||
RealRect rect = rect1.move(15, 5, -20, -20);
|
RealRect rect = rect1.move(23, 8, -30, -28);
|
||||||
// Bar sizes
|
// Bar sizes
|
||||||
GraphAxis& axis = axis_data();
|
GraphAxis& axis = axis_data();
|
||||||
int count = int(axis.groups.size());
|
int count = int(axis.groups.size());
|
||||||
|
|||||||
@@ -36,15 +36,17 @@ class GraphGroup {
|
|||||||
/** The sum of groups.sum = sum of all elements in the data */
|
/** The sum of groups.sum = sum of all elements in the data */
|
||||||
class GraphAxis {
|
class GraphAxis {
|
||||||
public:
|
public:
|
||||||
GraphAxis(const String& name, bool auto_color = true)
|
GraphAxis(const String& name, bool auto_color = true, bool numeric = false)
|
||||||
: name(name)
|
: name(name)
|
||||||
, auto_color(auto_color)
|
, auto_color(auto_color)
|
||||||
, max(0)
|
, max(0)
|
||||||
|
, numeric(numeric)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
String name; ///< Name/label of this axis
|
String name; ///< Name/label of this axis
|
||||||
bool auto_color; ///< Automatically assign colors to the groups on this axis
|
bool auto_color; ///< Automatically assign colors to the groups on this axis
|
||||||
vector<GraphGroup> groups; ///< Groups along this axis
|
vector<GraphGroup> groups; ///< Groups along this axis
|
||||||
|
bool numeric; ///< Numeric axis?
|
||||||
UInt max; ///< Maximum size of the groups
|
UInt max; ///< Maximum size of the groups
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -54,9 +54,10 @@ void PackageList::showData(const String& pattern) {
|
|||||||
PackageP package = ::packages.openAny(f);
|
PackageP package = ::packages.openAny(f);
|
||||||
// open image
|
// open image
|
||||||
InputStreamP stream = package->openIconFile();
|
InputStreamP stream = package->openIconFile();
|
||||||
|
Image img;
|
||||||
Bitmap bmp;
|
Bitmap bmp;
|
||||||
if (stream) {
|
if (stream && img.LoadFile(*stream)) {
|
||||||
bmp = Bitmap(Image(*stream));
|
bmp = Bitmap(img);
|
||||||
}
|
}
|
||||||
// add to list
|
// add to list
|
||||||
packages.push_back(PackageData(package, bmp));
|
packages.push_back(PackageData(package, bmp));
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ void StatsPanel::onCommand(int id) {
|
|||||||
StatsCategory& cat = categories->getSelection();
|
StatsCategory& cat = categories->getSelection();
|
||||||
GraphDataPre d;
|
GraphDataPre d;
|
||||||
FOR_EACH(dim, cat.dimensions) {
|
FOR_EACH(dim, cat.dimensions) {
|
||||||
d.axes.push_back(new_shared1<GraphAxis>(dim->name));
|
d.axes.push_back(new_shared3<GraphAxis>(dim->name, true, dim->numeric));
|
||||||
}
|
}
|
||||||
FOR_EACH(card, set->cards) {
|
FOR_EACH(card, set->cards) {
|
||||||
Context& ctx = set->getContext(card);
|
Context& ctx = set->getContext(card);
|
||||||
|
|||||||
@@ -1978,6 +1978,9 @@
|
|||||||
<File
|
<File
|
||||||
RelativePath=".\gfx\image_effects.cpp">
|
RelativePath=".\gfx\image_effects.cpp">
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\gfx\mask_image.cpp">
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\gfx\polynomial.cpp">
|
RelativePath=".\gfx\polynomial.cpp">
|
||||||
</File>
|
</File>
|
||||||
|
|||||||
+24
-18
@@ -9,6 +9,7 @@
|
|||||||
#include <render/value/image.hpp>
|
#include <render/value/image.hpp>
|
||||||
#include <render/card/viewer.hpp>
|
#include <render/card/viewer.hpp>
|
||||||
#include <data/set.hpp>
|
#include <data/set.hpp>
|
||||||
|
#include <data/stylesheet.hpp>
|
||||||
#include <gui/util.hpp>
|
#include <gui/util.hpp>
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : ImageValueViewer
|
// ----------------------------------------------------------------------------- : ImageValueViewer
|
||||||
@@ -23,9 +24,9 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
|||||||
if (image.LoadFile(*image_file)) {
|
if (image.LoadFile(*image_file)) {
|
||||||
image.Rescale(dc.trS(style().width), dc.trS(style().height));
|
image.Rescale(dc.trS(style().width), dc.trS(style().height));
|
||||||
// apply mask to image
|
// apply mask to image
|
||||||
/* loadMask(dc);
|
loadMask(dc);
|
||||||
if (alpha_mask) alpha_mask->setAlpha(image);
|
if (alpha_mask) alpha_mask->setAlpha(image);
|
||||||
*/ bitmap = Bitmap(image);
|
bitmap = Bitmap(image);
|
||||||
}
|
}
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
handle_error(e, false, false); // don't handle now, we are in onPaint
|
handle_error(e, false, false); // don't handle now, we are in onPaint
|
||||||
@@ -34,15 +35,9 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
|||||||
// if there is no image, generate a placeholder, only if there is enough room for it
|
// if there is no image, generate a placeholder, only if there is enough room for it
|
||||||
if (!bitmap.Ok() && style().width > 40) {
|
if (!bitmap.Ok() && style().width > 40) {
|
||||||
bitmap = imagePlaceholder(dc, dc.trS(style().width), dc.trS(style().height), viewer.drawEditing());
|
bitmap = imagePlaceholder(dc, dc.trS(style().width), dc.trS(style().height), viewer.drawEditing());
|
||||||
/* loadMask(dc);
|
loadMask(dc);
|
||||||
if (alpha_mask) alpha_mask->setAlpha(bitmap);
|
if (alpha_mask) alpha_mask->setAlpha(bitmap);
|
||||||
/* if (alphaMask) {
|
}
|
||||||
// convert to image and apply alpha
|
|
||||||
Image image = bmp.ConvertToImage();
|
|
||||||
alpha_mask->setAlpha(image);
|
|
||||||
bitmap = image;
|
|
||||||
}
|
|
||||||
*/ }
|
|
||||||
// draw image, if any
|
// draw image, if any
|
||||||
if (bitmap.Ok()) {
|
if (bitmap.Ok()) {
|
||||||
dc.DrawBitmap(bitmap, style().getPos());
|
dc.DrawBitmap(bitmap, style().getPos());
|
||||||
@@ -55,15 +50,13 @@ bool ImageValueViewer::containsPoint(const RealPoint& p) const {
|
|||||||
if (x < 0 || y < 0 || x >= (int)style().width || y >= (int)style().height) {
|
if (x < 0 || y < 0 || x >= (int)style().width || y >= (int)style().height) {
|
||||||
return false; // outside rectangle
|
return false; // outside rectangle
|
||||||
}
|
}
|
||||||
/* // check against mask
|
// check against mask
|
||||||
if (!style->maskFilename.value.empty()) {
|
if (!style().mask_filename().empty()) {
|
||||||
RotatedObject rot(viewer.getRotation());
|
loadMask(viewer.getRotation());
|
||||||
loadMask(rot);
|
return !alpha_mask || !alpha_mask->isTransparent(x, y);
|
||||||
return !alphaMask->isTransparent(x, y);
|
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}*/
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageValueViewer::onValueChange() {
|
void ImageValueViewer::onValueChange() {
|
||||||
@@ -72,7 +65,20 @@ void ImageValueViewer::onValueChange() {
|
|||||||
|
|
||||||
void ImageValueViewer::onStyleChange() {
|
void ImageValueViewer::onStyleChange() {
|
||||||
bitmap = Bitmap();
|
bitmap = Bitmap();
|
||||||
// alpha_mask = AlphaMaskP();
|
alpha_mask = AlphaMaskP();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageValueViewer::loadMask(const Rotation& rot) const {
|
||||||
|
if (style().mask_filename().empty()) return; // no mask
|
||||||
|
if (alpha_mask && alpha_mask->size == wxSize(rot.trS(style().width), rot.trS(style().height))) return; // mask loaded and right size
|
||||||
|
// (re) load the mask
|
||||||
|
Image image;
|
||||||
|
InputStreamP image_file = viewer.stylesheet->openIn(style().mask_filename);
|
||||||
|
if (image.LoadFile(*image_file)) {
|
||||||
|
Image resampled(rot.trS(style().width), rot.trS(style().height));
|
||||||
|
resample(image, resampled);
|
||||||
|
alpha_mask = new_shared1<AlphaMask>(resampled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitmap ImageValueViewer::imagePlaceholder(const Rotation& rot, UInt w, UInt h, bool editing) {
|
Bitmap ImageValueViewer::imagePlaceholder(const Rotation& rot, UInt w, UInt h, bool editing) {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
#include <render/value/viewer.hpp>
|
#include <render/value/viewer.hpp>
|
||||||
#include <data/field/image.hpp>
|
#include <data/field/image.hpp>
|
||||||
|
|
||||||
|
DECLARE_POINTER_TYPE(AlphaMask);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : ImageValueViewer
|
// ----------------------------------------------------------------------------- : ImageValueViewer
|
||||||
|
|
||||||
/// Viewer that displays an image value
|
/// Viewer that displays an image value
|
||||||
@@ -29,9 +31,9 @@ class ImageValueViewer : public ValueViewer {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Bitmap bitmap;
|
Bitmap bitmap;
|
||||||
// mutable AlphaMaskP alpha_mask;
|
mutable AlphaMaskP alpha_mask;
|
||||||
|
|
||||||
// void loadMask(const RotatedObject& rot) const;
|
void loadMask(const Rotation& rot) const;
|
||||||
|
|
||||||
/// Generate a placeholder image
|
/// Generate a placeholder image
|
||||||
static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, bool editing);
|
static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, bool editing);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class Rotation {
|
|||||||
RealRect getExternalRect() const;
|
RealRect getExternalRect() const;
|
||||||
|
|
||||||
/// Translate a size or length
|
/// Translate a size or length
|
||||||
inline double trS(double s) const { return s * zoom; }
|
inline double trS(double s) const { return s * zoom; }
|
||||||
|
|
||||||
/// Translate a single point
|
/// Translate a single point
|
||||||
RealPoint tr(const RealPoint& p) const;
|
RealPoint tr(const RealPoint& p) const;
|
||||||
|
|||||||
Reference in New Issue
Block a user