Files
MagicSetEditor2/src/data/symbol.hpp
T

327 lines
12 KiB
C++

//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) Twan van Laarhoven and the other MSE developers |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#pragma once
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/reflect.hpp>
#include <util/action_stack.hpp>
#include <util/vector2d.hpp>
#include <util/real_point.hpp>
DECLARE_POINTER_TYPE(ControlPoint);
DECLARE_POINTER_TYPE(SymbolPart);
DECLARE_POINTER_TYPE(SymbolShape);
DECLARE_POINTER_TYPE(SymbolSymmetry);
DECLARE_POINTER_TYPE(SymbolGroup);
DECLARE_POINTER_TYPE(Symbol);
// ----------------------------------------------------------------------------- : ControlPoint
/// Mode of locking for control points in a bezier curve
/** Specificly: the relation between delta_before and delta_after
*/
enum LockMode
{ LOCK_FREE ///< no locking
, LOCK_DIR ///< delta_before = x * delta_after
, LOCK_SIZE ///< delta_before = -delta_after
};
/// Is the segment between two ControlPoints a line or a curve?
enum SegmentMode
{ SEGMENT_LINE
, SEGMENT_CURVE
};
/// To refer to a specific handle of a control point
enum WhichHandle
{ HANDLE_NONE = 0 ///< point is not selected
, HANDLE_MAIN
, HANDLE_BEFORE
, HANDLE_AFTER
};
/// A control point (corner) of a SymbolShape (polygon/bezier-gon)
class ControlPoint : public IntrusivePtrBase<ControlPoint> {
public:
Vector2D pos; ///< position of the control point itself
Vector2D delta_before; ///< delta to bezier control point, for curve before point
Vector2D delta_after; ///< delta to bezier control point, for curve after point
SegmentMode segment_before, segment_after;
LockMode lock;
/// Default constructor
ControlPoint();
/// Constructor for straight lines, takes only the position
ControlPoint(double x, double y);
/// Constructor for curves lines, takes postions, delta_before, delta_after and lock mode
ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock = LOCK_FREE);
/// Must be called after delta_before/delta_after has changed, enforces lock constraints
void onUpdateHandle(WhichHandle wh);
/// Must be called after lock has changed, enforces lock constraints
void onUpdateLock();
/// Get a handle of this control point
Vector2D& getHandle(WhichHandle wh);
/// Get a handle of this control point that is oposite wh
Vector2D& getOther(WhichHandle wh);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Selected handles
/// A specific handle of a ControlPoint
class SelectedHandle {
public:
ControlPointP point; ///< the selected point
WhichHandle handle; ///< the selected handle of the point
// SelectedHandle
SelectedHandle() : handle(HANDLE_NONE) {}
SelectedHandle(const WhichHandle& handle) : handle(handle) { assert (handle == HANDLE_NONE); }
SelectedHandle(const ControlPointP& point, const WhichHandle& handle) : point(point), handle(handle) {}
inline Vector2D& getHandle() const { return point->getHandle(handle); }
inline Vector2D& getOther() const { return point->getOther (handle); }
inline void onUpdateHandle() const { return point->onUpdateHandle(handle); }
/*
bool operator == (const ControlPointP pnt) const;
bool SelectedHandle::operator == (const ControlPointP pnt) const { return point == pnt; }
bool operator == (const WhichHandle& wh) const;
bool SelectedHandle::operator == (const WhichHandle& wh) const { return handle == wh; }
bool operator ! () const;
bool SelectedHandle::operator ! () const { return handle == handleNone; }
*/
};
// ----------------------------------------------------------------------------- : Bounds
/// Bounding box of a symbol part
class Bounds {
public:
inline Bounds() : min(Vector2D::infinity()), max(-Vector2D::infinity()) {}
inline explicit Bounds(const Vector2D& p) : min(p), max(p) {}
inline Bounds(const Vector2D& min, const Vector2D& max) : min(min), max(max) {}
/// Combine with another bounding box
void update(const Bounds& b);
void update(const Vector2D& p);
/// Does this box contain the given point?
bool contains(const Vector2D& p) const;
/// Does this box contain the given rectangle?
bool contains(const Bounds& b) const;
/// Corner or center of this bounding box, dx,dy in <-1, 0, 1>
Vector2D corner(int dx, int dy) const;
Vector2D min, max;
inline operator RealRect () const { return RealRect(min, RealSize(max - min)); }
};
// ----------------------------------------------------------------------------- : SymbolPart
/// A part of a symbol, not necesserly a shape
class SymbolPart : public IntrusivePtrVirtualBase {
public:
/// Name/label for this part
String name;
/// Position and size of the part.
/** this is the smallest axis aligned bounding box that fits around the part */
Bounds bounds;
/// Type of this part
virtual String typeName() const = 0;
/// Create a clone of this symbol part
virtual SymbolPartP clone() const = 0;
/// Icon for this part
virtual int icon() const = 0;
/// Convert to SymbolShape?
virtual SymbolShape* isSymbolShape() { return nullptr; }
virtual const SymbolShape* isSymbolShape() const { return nullptr; }
/// Convert to SymbolSymmetry?
virtual SymbolSymmetry* isSymbolSymmetry() { return nullptr; }
virtual const SymbolSymmetry* isSymbolSymmetry() const { return nullptr; }
/// Convert to SymbolGroup?
virtual SymbolGroup* isSymbolGroup() { return nullptr; }
virtual const SymbolGroup* isSymbolGroup() const { return nullptr; }
/// Does this part contain another?
/** also true if this==that*/
virtual bool isAncestor(const SymbolPart& that) const { return this == &that; }
/// Calculate the position and size of the part (bounds)
virtual void updateBounds();
/// Calculate the position and size of the part using the given rotation matrix
virtual Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) = 0;
DECLARE_REFLECTION_VIRTUAL();
virtual void after_reading(Version) {}
friend void after_reading(SymbolPart&, Version);
};
template <> SymbolPartP read_new<SymbolPart>(Reader& reader);
void after_reading(SymbolPart&, Version);
// ----------------------------------------------------------------------------- : SymbolShape
/// How are symbol parts combined with parts below it?
enum SymbolShapeCombine
{ SYMBOL_COMBINE_MERGE
, SYMBOL_COMBINE_SUBTRACT
, SYMBOL_COMBINE_INTERSECTION
, SYMBOL_COMBINE_DIFFERENCE
, SYMBOL_COMBINE_OVERLAP
, SYMBOL_COMBINE_BORDER
};
/// A sane mod function, always returns a result in the range [0..size)
inline size_t mod(int a, size_t size) {
int m = a % (int)size;
return m >= 0 ? m : m + size;
}
/// A single shape (polygon/bezier-gon) in a Symbol
class SymbolShape : public SymbolPart {
public:
/// The points of this polygon
vector<ControlPointP> points;
/// How is this part combined with parts below it?
SymbolShapeCombine combine;
// Center of rotation, relative to the part, when the part is scaled to [0..1]
Vector2D rotation_center;
SymbolShape();
String typeName() const override;
SymbolPartP clone() const override;
int icon() const override { return combine; }
SymbolShape* isSymbolShape() override { return this; }
const SymbolShape* isSymbolShape() const override { return this; }
/// Get a control point, wraps around
inline ControlPointP getPoint(int id) const {
return points[mod(id, points.size())];
}
/// Enforce lock constraints
void enforceConstraints();
/// Calculate the position and size of the part using the given rotation matrix
Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) override;
DECLARE_REFLECTION_OVERRIDE();
void after_reading(Version) override;
};
// ----------------------------------------------------------------------------- : SymbolGroup
/// A group of symbol parts
class SymbolGroup : public SymbolPart {
public:
vector<SymbolPartP> parts; ///< The parts in this group, first item is on top
SymbolGroup();
String typeName() const override;
SymbolPartP clone() const override;
int icon() const override { return SYMBOL_COMBINE_BORDER + 3; }
SymbolGroup* isSymbolGroup() override { return this; }
const SymbolGroup* isSymbolGroup() const override { return this; }
bool isAncestor(const SymbolPart& that) const override;
Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) override;
DECLARE_REFLECTION_OVERRIDE();
};
// ----------------------------------------------------------------------------- : SymbolSymmetry
enum SymbolSymmetryType
{ SYMMETRY_ROTATION
, SYMMETRY_REFLECTION
};
/// A mirror, reflecting the part of the symbol in the group
/** Can handle rotation symmetry with any number of reflections */
class SymbolSymmetry : public SymbolGroup {
public:
SymbolSymmetryType kind; ///< What kind of symmetry
int copies; ///< How many times is the orignal reflected (including the original itself)
bool clip; ///< Clip the orignal so it doesn't intersect the mirror(s)
Vector2D center; ///< Center point of the mirror
Vector2D handle; ///< A handle pointing in the direction of the original, relative to the center
SymbolSymmetry();
String typeName() const override;
SymbolPartP clone() const override;
int icon() const override { return kind + SYMBOL_COMBINE_BORDER + 1; }
SymbolSymmetry* isSymbolSymmetry() override { return this; }
const SymbolSymmetry* isSymbolSymmetry() const override { return this; }
String expectedName() const;
Bounds calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) override;
DECLARE_REFLECTION_OVERRIDE();
};
// ----------------------------------------------------------------------------- : Symbol
/// An editable symbol, consists of any number of SymbolParts
class Symbol : public SymbolGroup {
public:
/// Actions performed on this symbol and the parts in it
ActionStack actions;
/// Determine the aspect ratio best suited for this symbol
double aspectRatio() const;
DECLARE_REFLECTION_OVERRIDE();
void after_reading(Version) override;
friend void after_reading(Symbol&, Version);
};
void after_reading(Symbol&, Version);
/// A default symbol: a square
SymbolP default_symbol();
// ----------------------------------------------------------------------------- : SymbolView
/// A 'view' of a symbol, is notified when the symbol is updated
/** To listen to events, derived classes should override onAction(const Action&, bool undone)
*/
class SymbolView : public ActionListener {
public:
SymbolView();
~SymbolView();
/// Get the symbol that is currently being viewed
inline SymbolP getSymbol() { return symbol; }
/// Change the symbol that is being viewed
void setSymbol(const SymbolP& symbol);
protected:
/// The symbol that is currently being viewed, should not be modified directly!
SymbolP symbol;
/// Called when another symbol is being viewn (using setSymbol)
virtual void onChangeSymbol() {}
};