Commit 3ed3d772 authored by twanvl's avatar twanvl

initial checkin of C++ port (in progress)

parents
This diff is collapsed.
This diff is collapsed.
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_DATA_ACTION_SYMBOL
#define HEADER_DATA_ACTION_SYMBOL
/** @file data/action/symbol.hpp
*
* Actions operating on Symbols or whole SymbolParts.
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/action_stack.hpp>
#include <data/symbol.hpp>
// ----------------------------------------------------------------------------- : Transform symbol part
/// Anything that changes a part
class SymbolPartAction : public Action {};
/// Anything that changes a part as displayed in the part list
class SymbolPartListAction : public SymbolPartAction {};
// ----------------------------------------------------------------------------- : Moving symbol parts
/// Move some symbol parts
class SymbolPartMoveAction : public SymbolPartAction {
public:
SymbolPartMoveAction(const set<SymbolPartP>& parts);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
set<SymbolPartP> parts; //^ Parts to move
Vector2D delta; //^ How much to move
Vector2D moved; //^ How much has been moved
public:
bool constrain; //^ Constrain movement?
};
// ----------------------------------------------------------------------------- : Rotating symbol parts
/// Transforming symbol parts using a matrix
class SymbolPartMatrixAction : public SymbolPartAction {
public:
SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center);
/// Update this action to move some more
void move(const Vector2D& delta);
protected:
/// Perform the transformation using the given matrix
void transform(const Vector2D& mx, const Vector2D& my);
set<SymbolPartP> parts; //^ Parts to transform
Vector2D center; //^ Center to transform around
};
/// Rotate some symbol parts
class SymbolPartRotateAction : public SymbolPartMatrixAction {
public:
SymbolPartRotateAction(const set<SymbolPartP>& parts, const Vector2D& center);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
/// Update this action to rotate to a different angle
void rotateTo(double newAngle);
/// Update this action to rotate by a deltaAngle
void rotateBy(double deltaAngle);
private:
double angle; //^ How much to rotate?
public:
bool constrain; //^ Constrain movement?
};
// ----------------------------------------------------------------------------- : Shearing symbol parts
/// Shear some symbol parts
class SymbolPartShearAction : public SymbolPartMatrixAction {
public:
SymbolPartShearAction(const set<SymbolPartP>& parts, const Vector2D& center);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
/// Change shear by a given amount
void move(const Vector2D& deltaShear);
private:
Vector2D shear; //^ Shearing, shear.x == 0 || shear.y == 0
void shearBy(const Vector2D& shear);
public:
bool constrain; //^ Constrain movement?
};
// ----------------------------------------------------------------------------- : Scaling symbol parts
/// Scale some symbol parts
class SymbolPartScaleAction : public SymbolPartAction {
public:
SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
/// Change min and max coordinates
void move(const Vector2D& deltaMin, const Vector2D& deltaMax);
/// Update the action's effect
void update();
private:
set<SymbolPartP> parts; //^ Parts to scale
Vector2D oldMin, oldSize; //^ the original pos/size
Vector2D newRealMin, newRealSize; //^ the target pos/sizevoid shearBy(const Vector2D& shear)
Vector2D newMin, newSize; //^ the target pos/size after applying constrains
int scaleX, scaleY; //^ to what corner are we attached?
/// Transform everything in the parts
void transformAll();
/// Transform a single vector
inline Vector2D transform(const Vector2D& v) {
return (v - oldMin).div(oldSize).mul(newSize) + newMin;
}
public:
bool constrain; //^ Constrain movement?
};
// ----------------------------------------------------------------------------- : Change combine mode
/// Change the name of a symbol part
class CombiningModeAction : public SymbolPartListAction {
public:
CombiningModeAction(const set<SymbolPartP>& parts, SymbolPartCombine mode);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
private:
vector<pair<SymbolPartP,SymbolPartCombine> > parts; //^ Affected parts with new combining modes
};
// ----------------------------------------------------------------------------- : Change name
/// Change the name of a symbol part
class SymbolPartNameAction : public SymbolPartListAction {
public:
SymbolPartNameAction(const SymbolPartP& part, const String& name);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
private:
SymbolPartP part; //^ Affected part
String partName; //^ New name
};
// ----------------------------------------------------------------------------- : Add symbol part
/// Adding a part to a symbol, added at the front of the list
/// front = drawn on top
class AddSymbolPartAction : public SymbolPartListAction {
public:
AddSymbolPartAction(Symbol& symbol, const SymbolPartP& part);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
private:
Symbol& symbol; //^ Symbol to add the part to
SymbolPartP part; //^ Part to add
};
// ----------------------------------------------------------------------------- : Remove symbol part
/// Removing parts from a symbol
class RemoveSymbolPartsAction : public SymbolPartListAction {
public:
RemoveSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
private:
Symbol& symbol;
/// Removed parts and their positions, sorted by ascending pos
vector<pair<SymbolPartP, size_t> > removals;
};
// ----------------------------------------------------------------------------- : Duplicate symbol parts
/// Duplicating parts in a symbol
class DuplicateSymbolPartsAction : public SymbolPartListAction {
public:
DuplicateSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
/// Fill a set with all the new parts
void getParts(set<SymbolPartP>& parts);
private:
Symbol& symbol;
/// Duplicates of parts and their positions, sorted by ascending pos
vector<pair<SymbolPartP, size_t> > duplications;
};
// ----------------------------------------------------------------------------- : Reorder symbol parts
/// Change the position of a part in a symbol.
/// This is done by swapping two parts.
class ReorderSymbolPartsAction : public SymbolPartListAction {
public:
ReorderSymbolPartsAction(Symbol& symbol, size_t partId1, size_t partId2);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
private:
Symbol& symbol; //^ Symbol to swap the parts in
public:
size_t partId1, partId2; //^ Indeces of parts to swap
};
// ----------------------------------------------------------------------------- : EOF
#endif
This diff is collapsed.
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_DATA_ACTION_SYMBOL_PART
#define HEADER_DATA_ACTION_SYMBOL_PART
/** @file data/action/symbol_part.hpp
*
* Actions operating on the insides of SymbolParts (ControlPoints and the like).
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/action_stack.hpp>
#include <data/symbol.hpp>
// ----------------------------------------------------------------------------- : Utility
/// Constrain a vector to be horizontal, vertical or diagonal
/// If constraint==false does nothing
Vector2D constrainVector(const Vector2D& v, bool constrain = true, bool onlyDiagonal = false);
// ----------------------------------------------------------------------------- : Move control point
/// Moving a control point in a symbol
class ControlPointMoveAction : public Action {
public:
ControlPointMoveAction(const set<ControlPointP>& points);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
set<ControlPointP> points; //^ Points to move
vector<Vector2D> oldValues; //^ Their old positions
Vector2D delta; //^ Amount we moved
public:
bool constrain; //^ Constrain movement?
};
// ----------------------------------------------------------------------------- : Move handle
/// Moving a handle(before/after) of a control point in a symbol
class HandleMoveAction : public Action {
public:
HandleMoveAction(const SelectedHandle& handle);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
SelectedHandle handle; //^ The handle to move
Vector2D oldHandle; //^ Old value of this handle
Vector2D oldOther; //^ Old value of other handle, needed for contraints
Vector2D delta; //^ Amount we moved
public:
bool constrain; //^ Constrain movement?
};
// ----------------------------------------------------------------------------- : Segment mode
/// Utility class to update a control point
class ControlPointUpdate {
public:
ControlPointUpdate(const ControlPointP& pnt);
/// Perform or undo an update on this control point
void perform();
/// Other value that is swapped with the current one.
/// Should be changed to make perform have an effect
ControlPoint other;
/// The point that is to be changed, should not be updated before perform()
ControlPointP point;
};
/// Changing a line to a curve and vice versa
class SegmentModeAction : public Action {
public:
SegmentModeAction(const ControlPointP& p1, const ControlPointP& p2, SegmentMode mode);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
protected:
ControlPointUpdate point1, point2;
};
// ----------------------------------------------------------------------------- : Locking mode
/// Locking a control point
class LockModeAction : public Action {
public:
LockModeAction(const ControlPointP& p, LockMode mode);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
private:
ControlPointUpdate point; //^ The affected point
};
// ----------------------------------------------------------------------------- : Move curve
/// Dragging a curve; also coverts lines to curves
/** Inherits from SegmentModeAction because it also has that effect
*/
class CurveDragAction : public SegmentModeAction {
public:
CurveDragAction(const ControlPointP& point1, const ControlPointP& point2);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
// Move the curve by this much, it is grabbed at time t
void move(const Vector2D& delta, double t);
};
// ----------------------------------------------------------------------------- : Add control point
/// Insert a new point in a symbol part
class ControlPointAddAction : public Action {
public:
/// Insert a new point in part, after position insertAfter_, at the time t on the segment
ControlPointAddAction(const SymbolPartP& part, UInt insertAfter, double t);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
inline ControlPointP getNewPoint() const { return newPoint; }
private:
SymbolPartP part; //^ SymbolPart we are in
ControlPointP newPoint; //^ The point to insert
UInt insertAfter; //^ Insert after index .. in the array
ControlPointUpdate point1, point2; //^ Update the points around the new point
};
// ----------------------------------------------------------------------------- : Remove control point
/// Action that removes any number of points from a symbol part
/// TODO: If less then 3 points are left removes the entire part!
Action* controlPointRemoveAction(const SymbolPartP& part, const set<ControlPointP>& toDelete);
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| 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 <data/symbol.hpp>
#include <gfx/bezier.hpp>
// ----------------------------------------------------------------------------- : ControlPoint
IMPLEMENT_REFLECTION_ENUM(LockMode) {
VALUE_N("free", LOCK_FREE);
VALUE_N("direction", LOCK_DIR);
VALUE_N("size", LOCK_SIZE);
}
IMPLEMENT_REFLECTION_ENUM(SegmentMode) {
VALUE_N("line", SEGMENT_LINE);
VALUE_N("curve", SEGMENT_CURVE);
}
IMPLEMENT_REFLECTION(ControlPoint) {
REFLECT_N("position", pos);
REFLECT_N("lock", lock);
REFLECT_N("line after", segmentAfter);
if (tag.reading() || segmentBefore == SEGMENT_CURVE) {
REFLECT_N("handle before", deltaBefore);
}
if (tag.reading() || segmentAfter == SEGMENT_CURVE) {
REFLECT_N("handle after", deltaAfter);
}
}
ControlPoint::ControlPoint()
: segmentBefore(SEGMENT_LINE), segmentAfter(SEGMENT_LINE)
, lock(LOCK_FREE)
{}
ControlPoint::ControlPoint(double x, double y)
: segmentBefore(SEGMENT_LINE), segmentAfter(SEGMENT_LINE)
, lock(LOCK_FREE)
, pos(x,y)
{}
ControlPoint::ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock)
: segmentBefore(SEGMENT_CURVE), segmentAfter(SEGMENT_CURVE)
, lock(lock)
, pos(x,y)
, deltaBefore(xb,yb)
, deltaAfter(xa,ya)
{}
void ControlPoint::onUpdateHandle(WhichHandle wh) {
// One handle has changed, update only the other one
if (lock == LOCK_DIR) {
getOther(wh) = -getHandle(wh) * getOther(wh).length() / getHandle(wh).length();
} else if (lock == LOCK_SIZE) {
getOther(wh) = -getHandle(wh);
}
}
void ControlPoint::onUpdateLock() {
// The lock has changed, avarage the handle values
if (lock == LOCK_DIR) {
// deltaBefore = x * deltaAfter
Vector2D dir = (deltaBefore - deltaAfter).normalized();
deltaBefore = dir * deltaBefore.length();
deltaAfter = dir * -deltaAfter.length();
} else if (lock == LOCK_SIZE) {
// deltaBefore = -deltaAfter
deltaBefore = (deltaBefore - deltaAfter) * 0.5;
deltaAfter = -deltaBefore;
}
}
Vector2D& ControlPoint::getHandle(WhichHandle wh) {
if (wh == HANDLE_BEFORE) {
return deltaBefore;
} else {
assert(wh == HANDLE_AFTER);
return deltaAfter;
}
}
Vector2D& ControlPoint::getOther(WhichHandle wh) {
if (wh == HANDLE_BEFORE) {
return deltaAfter;
} else {
assert(wh == HANDLE_AFTER);
return deltaBefore;
}
}
// ----------------------------------------------------------------------------- : SymbolPart
IMPLEMENT_REFLECTION_ENUM(SymbolPartCombine) {
VALUE_N("overlap", PART_OVERLAP);
VALUE_N("merge", PART_MERGE);
VALUE_N("subtract", PART_SUBTRACT);
VALUE_N("intersection", PART_INTERSECTION);
VALUE_N("difference", PART_DIFFERENCE);
VALUE_N("border", PART_BORDER);
}
IMPLEMENT_REFLECTION(SymbolPart) {
REFLECT(name);
REFLECT(combine);
REFLECT_N("point", points);
// Fixes after reading
if (tag.reading()) {
// enforce constraints
enforceConstraints();
calculateBounds();
if (maxPos.x > 100 && maxPos.y > 100) {
// this is a <= 0.1.2 symbol, points range [0...500] instead of [0...1]
// adjust it
FOR_EACH(p, points) {
p->pos /= 500.0;
p->deltaBefore /= 500.0;
p->deltaAfter /= 500.0;
}
if (name.empty()) name = _("Shape");
calculateBounds();
}
}
}
SymbolPart::SymbolPart()
: combine(PART_OVERLAP), rotationCenter(.5, .5)
{}
SymbolPartP SymbolPart::clone() const {
SymbolPartP part = new_shared1<SymbolPart>(*this);
// also clone the control points
FOR_EACH(p, part->points) {
p = new_shared1<ControlPoint>(*p);
}
return part;
}
void SymbolPart::enforceConstraints() {
for (int i = 0 ; i < (int)points.size() ; ++i) {
ControlPointP p1 = getPoint(i);
ControlPointP p2 = getPoint(i + 1);
p2->segmentBefore = p1->segmentAfter;
p1->onUpdateLock();
}
}
void SymbolPart::calculateBounds() {
minPos = Vector2D::infinity();
maxPos = -Vector2D::infinity();
for (int i = 0 ; i < (int)points.size() ; ++i) {
segmentBounds(*getPoint(i), *getPoint(i + 1), minPos, maxPos);
}
}
// ----------------------------------------------------------------------------- : Symbol
IMPLEMENT_REFLECTION(Symbol) {
//%% version?
REFLECT_N("part", parts);
}
// ----------------------------------------------------------------------------- : SymbolView
SymbolView::SymbolView() {}
SymbolView::SymbolView(SymbolP symbol)
: symbol(symbol)
{
if (symbol) symbol->actions.addListener(this);
}
SymbolView::~SymbolView() {
if (symbol) symbol->actions.removeListener(this);
}
void SymbolView::setSymbol(SymbolP newSymbol) {
// no longer listening to old symbol
if (symbol) symbol->actions.removeListener(this);
symbol = newSymbol;
// start listening to new symbol
if (symbol) symbol->actions.addListener(this);
onSymbolChange();
}
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_DATA_SYMBOL
#define HEADER_DATA_SYMBOL
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/reflect.hpp>
#include <util/action_stack.hpp>
#include <util/vector2d.hpp>
DECLARE_POINTER_TYPE(ControlPoint);
DECLARE_POINTER_TYPE(SymbolPart);
DECLARE_POINTER_TYPE(Symbol);
DECLARE_TYPEOF_COLLECTION(ControlPointP);
DECLARE_TYPEOF_COLLECTION(SymbolPartP);
// ----------------------------------------------------------------------------- : ControlPoint
/// Mode of locking for control points in a bezier curve
/** Specificly: the relation between deltaBefore and deltaAfter
*/
enum LockMode
{ LOCK_FREE //^ no locking
, LOCK_DIR //^ deltaBefore = x * deltaAfter
, LOCK_SIZE //^ deltaBefore = -deltaAfter
};
/// 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 SymbolPart (polygon/bezier-gon)
class ControlPoint {
public:
Vector2D pos; //^ position of the control point itself
Vector2D deltaBefore; //^ delta to bezier control point, for curve before point
Vector2D deltaAfter; //^ delta to bezier control point, for curve after point
SegmentMode segmentBefore, segmentAfter;
LockMode lock;
/// Default constructor
ControlPoint();
/// Constructor for straight lines, takes only the position
ControlPoint(double x, double y);
/// Constructor for curves lines, takes postions, deltaBefore, deltaAfter and lock mode
ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock = LOCK_FREE);
/// Must be called after deltaBefore/deltaAfter 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; }
*/
};
// ----------------------------------------------------------------------------- : SymbolPart
/// How are symbol parts combined with parts below it?
enum SymbolPartCombine
{ PART_MERGE
, PART_SUBTRACT
, PART_INTERSECTION
, PART_DIFFERENCE
, PART_OVERLAP
, PART_BORDER
};
/// A single part (polygon/bezier-gon) in a Symbol
class SymbolPart {
public:
/// The points of this polygon
vector<ControlPointP> points;
/// Name/label for this part
String name;
/// How is this part combined with parts below it?
SymbolPartCombine combine;
// Center of rotation, relative to the part, when the part is scaled to [0..1]
Vector2D rotationCenter;
/// Position and size of the part
/// this is the smallest axis aligned bounding box that fits around the part
Vector2D minPos, maxPos;
SymbolPart();
/// Create a clone of this symbol part
SymbolPartP clone() const;
/// Get a control point, wraps around
inline ControlPointP getPoint(int id) const {
return points[id >= 0 ? id % points.size() : id + points.size()];
}
/// Enforce lock constraints
void enforceConstraints();
/// Calculate the position and size of the part
void calculateBounds();
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Symbol
/// An editable symbol, consists of any number of SymbolParts
class Symbol {
public:
/// The parts of this symbol
vector<SymbolPartP> parts;
/// Actions performed on this symbol and the parts in it
ActionStack actions;
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : SymbolView
/// A 'view' of a symbol, is notified when the symbol is updated
class SymbolView : public ActionListener {
public:
SymbolView();
SymbolView(SymbolP symbol);
~SymbolView();
/// Get the symbol that is currently being viewed
inline SymbolP getSymbol() { return symbol; }
/// Change the symbol that is being viewed
void setSymbol(SymbolP symbol);
protected:
/// The symbol that is currently being viewed, should not be modified directly!
SymbolP symbol;
/// Called when the associated symbol is changed, but not when it is initially set!
virtual void onSymbolChange() {}
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| 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 <gfx/bezier.hpp>
#include <gfx/polynomial.hpp>
// ----------------------------------------------------------------------------- : Evaluation
BezierCurve::BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) {
// calculate coefficients
c = (p1 - p0) * 3.0;
b = (p2 - p1) * 3.0 - c;
a = (p3 - p0) - c - b;
d = p0;
}
BezierCurve::BezierCurve(const ControlPoint& p0, const ControlPoint& p3) {
// calculate coefficients
c = p0.deltaAfter * 3.0;
b = (p3.pos + p3.deltaBefore - p0.pos - p0.deltaAfter) * 3.0 - c;
a = (p3.pos - p0.pos) - c - b;
d = p0.pos;
}
void deCasteljau(Vector2D a1, Vector2D a2, Vector2D a3, Vector2D a4,
Vector2D& b2, Vector2D& b3, Vector2D& b4c1,
Vector2D& c2, Vector2D& c3, double t)
{
b2 = a1 + (a2 - a1) * t;
Vector2D mid23 = a2 + (a3 - a2) * t;
c3 = a3 + (a4 - a3) * t;
b3 = b2 + (mid23 - b2) * t;
c2 = mid23 + (c3 - mid23) * t;
b4c1 = b3 + (c2 - b3) * t;
}
void deCasteljau(ControlPoint& a, ControlPoint& b, double t, ControlPoint& mid) {
deCasteljau(a.pos, a.deltaAfter, b.deltaBefore, b.pos, t, mid);
}
void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2D& a4, double t, ControlPoint& out) {
Vector2D half21 = a21 * t;
Vector2D half34 = a34 * (1-t);
Vector2D mid23 = (a1 + a21) * (1-t) + (a34 + a4) * t;
Vector2D mid23h21 = (a1 + half21) * (1-t) + mid23 * t;
Vector2D mid23h34 = (a4 + half34) * t + mid23 * (1-t);
out.pos = mid23h21 * (1-t) + mid23h34 * t;
out.deltaBefore = mid23h21 - out.pos;
out.deltaAfter = mid23h34 - out.pos;
a21 = half21;
a34 = half34;
}
// ----------------------------------------------------------------------------- : Drawing
void curveSubdivide(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, const Rotation& rot, vector<wxPoint>& out, UInt level) {
if (level <= 0) return;
double midtime = (t0+t1) * 0.5f;
Vector2D midpoint = c.pointAt(midtime);
Vector2D d0 = p0 - midpoint;
Vector2D d1 = midpoint - p1;
// Determine treshold for subdivision, greater angle -> subdivide
// greater size -> subdivide
double treshold = fabs( atan2(d0.x,d0.y) - atan2(d1.x,d1.y)) * (p0-p1).lengthSqr();
bool subdivide = treshold >= .0001;
// subdivide left
curveSubdivide(c, p0, midpoint, t0, midtime, rot, out, level - 1);
// add midpoint
if (subdivide) {
out.push_back(rot.tr(midpoint));
}
// subdivide right
curveSubdivide(c, midpoint, p1, midtime, t1, rot, out, level - 1);
}
void segmentSubdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out) {
assert(p0.segmentAfter == p1.segmentBefore);
// always the start
out.push_back(rot.tr(p0.pos));
if (p0.segmentAfter == SEGMENT_CURVE) {
// need more points?
BezierCurve curve(p0,p1);
curveSubdivide(curve, p0.pos, p1.pos, 0, 1, rot, out, 5);
}
}
// ----------------------------------------------------------------------------- : Bounds
void segmentBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max) {
assert(p1.segmentAfter == p2.segmentBefore);
if (p1.segmentAfter == SEGMENT_LINE) {
lineBounds (p1.pos, p2.pos, min, max);
} else {
bezierBounds(p1, p2, min, max);
}
}
void bezierBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max) {
assert(p1.segmentAfter == SEGMENT_CURVE);
// First of all, the corners should be in the bounding box
pointBounds(p1.pos, min, max);
pointBounds(p2.pos, min, max);
// Solve the derivative of the bezier curve to find its extremes
// It's only a quadtratic equation :)
BezierCurve curve(p1,p2);
double roots[4];
UInt count;
count = solveQuadratic(3*curve.a.x, 2*curve.b.x, curve.c.x, roots);
count += solveQuadratic(3*curve.a.y, 2*curve.b.y, curve.c.y, roots + count);
// now check them for min/max
for (UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >=0 && t <= 1) {
pointBounds(curve.pointAt(t), min, max);
}
}
}
void lineBounds(const Vector2D& p1, const Vector2D& p2, Vector2D& min, Vector2D& max) {
pointBounds(p1, min, max);
pointBounds(p2, min, max);
}
void pointBounds(const Vector2D& p, Vector2D& min, Vector2D& max) {
min = piecewise_min(min, p);
max = piecewise_max(max, p);
}
// Is a point inside the bounds <min...max>?
bool pointInBounds(const Vector2D& p, const Vector2D& min, const Vector2D& max) {
return p.x >= min.x && p.y >= min.y &&
p.x <= max.x && p.y <= max.y;
}
// ----------------------------------------------------------------------------- : Point tests
// As a point inside a symbol part?
bool pointInPart(const Vector2D& pos, const SymbolPart& part) {
// Step 1. compare bounding box of the part
if (!pointInBounds(pos, part.minPos, part.maxPos)) return false;
// Step 2. trace ray outward, count intersections
int count = 0;
size_t size = part.points.size();
for(size_t i = 0 ; i < size ; ++i) {
ControlPointP p1 = part.getPoint((int) i);
ControlPointP p2 = part.getPoint((int) i + 1);
if (p1->segmentAfter == SEGMENT_LINE) {
count += intersectLineRay (p1->pos, p2->pos, pos);
} else {
count += intersectBezierRay(*p1, *p2, pos);
}
}
return count & 1; // odd number of intersections
}
// ----------------------------------------------------------------------------- : Finding points
bool posOnSegment(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
if (p1.segmentAfter == SEGMENT_CURVE) {
return posOnBezier(pos, range, p1, p2, pOut, tOut);
} else {
return posOnLine (pos, range, p1.pos, p2.pos, pOut, tOut);
}
}
bool posOnBezier(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
assert(p1.segmentAfter == SEGMENT_CURVE);
// Find intersections with the horizontal and vertical lines through p0
// theoretically we would need to check in all directions, but this covers enough
BezierCurve curve(p1, p2);
double roots[6];
UInt count;
count = solveCubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
count += solveCubic(curve.a.x, curve.b.x, curve.c.x, curve.d.x - pos.x, roots + count); // append intersections
// take the best intersection point
double bestDistSqr = std::numeric_limits<double>::max(); //infinity
for(UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >= 0 && t < 1) {
Vector2D pnt = curve.pointAt(t);
double distSqr = (pnt - pos).lengthSqr();
if (distSqr < bestDistSqr) {
bestDistSqr = distSqr;
pOut = pnt;
tOut = t;
}
}
}
return bestDistSqr <= range * range;
}
bool posOnLine(const Vector2D& pos, double range, const Vector2D& p1, const Vector2D& p2, Vector2D& pOut, double& t) {
Vector2D p21 = p2 - p1;
double p21len = p21.lengthSqr();
if (p21len < 0.00001) return false; // line is too short
t = p21.dot(pos - p1) / p21len; // 'time' on line p1->p2
if (t < 0 || t > 1) return false; // outside segment
pOut = p1 + p21 * t; // point on line
Vector2D dist = pOut - pos; // distance to line
return dist.lengthSqr() <= range * range; // in range?
}
// ----------------------------------------------------------------------------- : Intersection
UInt intersectBezierRay(const ControlPoint& p1, const ControlPoint& p2, const Vector2D& pos) {
// Looking only at the y coordinate
// we can use the cubic formula to find roots, points where the horizontal line
// through pos intersects the (extended) curve
BezierCurve curve(p1,p2);
double roots[3];
UInt count = solveCubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
// now check if the solutions are left of pos.x
UInt solsInRange = 0;
for(UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >= 0 && t < 1 && curve.pointAt(t).x < pos.x) {
solsInRange += 1;
}
}
return solsInRange;
}
bool intersectLineRay(const Vector2D& p1, const Vector2D& p2, const Vector2D& pos) {
// Vector2D intersection = p1 + t * (p2 - p1)
// intersection.y == pos.y
// == p1.y + t * (p2.y - p1.y)
// => t == (pos.y - p1.y) / (p2.y - p1.y)
// intersection.x == p1.x + t * (p2.x - p1.x)
// == p1.x + (pos.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y)
double dy = p2.y - p1.y;
if (fabs(dy) < 0.0000000001) {
// horizontal line
return (p1.x > pos.x || p2.x > pos.x) && // starts to the left of pos
fabs(p1.y - pos.y) < 0.0000000001; // same y as pos
} else {
double dx = p2.x - p1.x;
double t = (pos.y - p1.y) / dy;
if (t < 0.0 || t >= 1.0) return false;
double intersectX = p1.x + t * dx;
return intersectX <= pos.x; // intersection is left of pos
}
}
\ No newline at end of file
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GFX_BEZIER
#define HEADER_GFX_BEZIER
/** @file gfx/bezier.hpp
*
* Bezier curve and line related functions
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/rotation.hpp>
#include <data/symbol.hpp>
// ----------------------------------------------------------------------------- : Evaluation
/// A bezier curve for evaluation
class BezierCurve {
public:
/// coefficients of the equation (x,y) = at^3 + bt^2 + ct + d
Vector2D a, b, c, d;
/// Construct a bezier curve evaluator given the 4 handles
BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3);
/// Construct a bezier curve evaluator given two ControlPoints at the ends
BezierCurve(const ControlPoint& p0, const ControlPoint& p3);
/// Return the point on this curve at time t in [0...1)
inline Vector2D pointAt(double t) const {
return d + (c + (b + a * t) * t) * t;
}
/// Return the tangent on this curve at time t in [0...1)
inline Vector2D tangentAt(double t) const {
return c + ((b * 2) + (a * 3) * t) * t;
}
};
/// Subdivide a curve from a to b, store the result in a control point
/** Also modifies the handles of the points to accomodate the inserted point
* Direct version, using input curve a1,a2,a3,a4 and output curves a1,b2,b3,b4 and c1,c2,c3,a4
*/
void deCasteljau(Vector2D a1, Vector2D a2, Vector2D a3, Vector2D a4,
Vector2D& b2, Vector2D& b3, Vector2D& b4c1,
Vector2D& c2, Vector2D& c3, double t);
/// Subdivide a curve from a to b at time t
/** Stores the point at time t in mid, updates the handles of a and b!
*/
void deCasteljau(ControlPoint& a, ControlPoint& b, double t, ControlPoint& mid);
/// Subdivide a curve from a to b, store the result in a control point
/** Also modifies the handles of the points to accomodate the inserted point!
*/
void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2D& a4, double t, ControlPoint& out);
// ----------------------------------------------------------------------------- : Drawing
/// Devide a segment into a number of straight lines for display purposes
/** Adds the resulting corner points of those lines to out, the last point is not added.
* All points are converted to display coordinates using rot.tr
*/
void segmentSubdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out);
// ----------------------------------------------------------------------------- : Bounds
/// Find a bounding box that fits a segment (either a line or a bezier curve) between p1 and p2.
/** stores the results in min and max.
* min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger.
*/
void segmentBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max);
/// Find a bounding box that fits a curve between p1 and p2, stores the results in min and max.
/** min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger
*/
void bezierBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max);
/// Find a bounding box that fits around p1 and p2, stores the result in min and max
/** min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger
*/
void lineBounds(const Vector2D& p1, const Vector2D& p2, Vector2D& min, Vector2D& max);
/// Find a bounding 'box' that fits around a single point
/** min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger
*/
void pointBounds(const Vector2D& p, Vector2D& min, Vector2D& max);
// ----------------------------------------------------------------------------- : Point tests
/// Is a point inside the given symbol part?
bool pointInPart(const Vector2D& p, const SymbolPart& part);
// ----------------------------------------------------------------------------- : Finding points
/// Finds the position of p0 on the line between p1 and p2, returns true if the point is on (or near) the line
/// the line between p1 and p2 can also be a bezier curve
/** Returns the time on the segment in tOut, and the point on the segment in pOut
*/
bool posOnSegment(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut);
/// Finds the position of p0 on the line between p1 and p2, returns true if the point is on (or near)
/// the bezier curve between p1 and p2
bool posOnBezier (const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut);
/// Finds the position of p0 on the line p1-p2, returns true if the point is withing range of the line
/// if that is the case then (x,y) = p1 + (p2-p1) * out
bool posOnLine (const Vector2D& pos, double range, const Vector2D& p1, const Vector2D& p2, Vector2D& pOut, double& tOut);
// ----------------------------------------------------------------------------- : Intersection
/// Counts the number of intersections between the ray/halfline from (-inf, pos.y) to pos
/// and the bezier curve between p1 and p2.
UInt intersectBezierRay(const ControlPoint& p1, const ControlPoint& p2, const Vector2D& pos);
// Does the line between p1 and p2 intersect the ray (half line) from (-inf, pos.y) to pos?
bool intersectLineRay(const Vector2D& p1, const Vector2D& p2, const Vector2D& pos);
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| 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 "../util/prec.hpp"
#include "../util/reflect.hpp"
#include "gfx.hpp"
#include <algorithm>
using namespace std;
// ----------------------------------------------------------------------------- : Reflection for combining modes
IMPLEMENT_REFLECTION_ENUM(ImageCombine) {
VALUE_N("normal", COMBINE_NORMAL);
VALUE_N("add", COMBINE_ADD);
VALUE_N("subtract", COMBINE_SUBTRACT);
VALUE_N("stamp", COMBINE_STAMP);
VALUE_N("difference", COMBINE_DIFFERENCE);
VALUE_N("negation", COMBINE_NEGATION);
VALUE_N("multiply", COMBINE_MULTIPLY);
VALUE_N("darken", COMBINE_DARKEN);
VALUE_N("lighten", COMBINE_LIGHTEN);
VALUE_N("color dodge", COMBINE_COLOR_DODGE);
VALUE_N("color burn", COMBINE_COLOR_BURN);
VALUE_N("screen", COMBINE_SCREEN);
VALUE_N("overlay", COMBINE_OVERLAY);
VALUE_N("hard light", COMBINE_HARD_LIGHT);
VALUE_N("soft light", COMBINE_SOFT_LIGHT);
VALUE_N("reflect", COMBINE_REFLECT);
VALUE_N("glow", COMBINE_GLOW);
VALUE_N("freeze", COMBINE_FREEZE);
VALUE_N("heat", COMBINE_HEAT);
VALUE_N("and", COMBINE_AND);
VALUE_N("or", COMBINE_OR);
VALUE_N("xor", COMBINE_XOR);
VALUE_N("shadow", COMBINE_SHADOW);
}
// ----------------------------------------------------------------------------- : Combining functions
// Functor for combining functions for a given combining type
template <ImageCombine combine> struct Combine {
static inline int f(int a, int b);
};
// Give a combining function for enum value 'combine'
#define COMBINE_FUN(combine,fun) \
template <> int Combine<combine>::f(int a, int b) { return fun; }
// Based on
// http://www.pegtop.net/delphi/articles/blendmodes/
COMBINE_FUN(COMBINE_NORMAL, b )
COMBINE_FUN(COMBINE_ADD, top(a + b) )
COMBINE_FUN(COMBINE_SUBTRACT, bot(a - b) )
COMBINE_FUN(COMBINE_STAMP, col(a - 2 * b + 256) )
COMBINE_FUN(COMBINE_DIFFERENCE, abs(a - b) )
COMBINE_FUN(COMBINE_NEGATION, 255 - abs(255 - a - b) )
COMBINE_FUN(COMBINE_MULTIPLY, (a * b) / 255 )
COMBINE_FUN(COMBINE_DARKEN, min(a, b) )
COMBINE_FUN(COMBINE_LIGHTEN, max(a, b) )
COMBINE_FUN(COMBINE_COLOR_DODGE,b == 255 ? 255 : top(a * 255 / (255 - b)) )
COMBINE_FUN(COMBINE_COLOR_BURN, b == 0 ? 0 : bot(255 - (255-a) * 255 / b) )
COMBINE_FUN(COMBINE_SCREEN, 255 - (((255 - a) * (255 - b)) / 255) )
COMBINE_FUN(COMBINE_OVERLAY, a < 128
? (a * b) >> 7
: 255 - (((255 - a) * (255 - b)) >> 7) )
COMBINE_FUN(COMBINE_HARD_LIGHT, b < 128
? (a * b) >> 7
: 255 - (((255 - a) * (255 - b)) >> 7) )
COMBINE_FUN(COMBINE_SOFT_LIGHT, b)
COMBINE_FUN(COMBINE_REFLECT, b == 255 ? 255 : top(a * a / (255 - b)) )
COMBINE_FUN(COMBINE_GLOW, a == 255 ? 255 : top(b * b / (255 - a)) )
COMBINE_FUN(COMBINE_FREEZE, b == 0 ? 0 : bot(255 - (255 - a) * (255 - a) / b) )
COMBINE_FUN(COMBINE_HEAT, a == 0 ? 0 : bot(255 - (255 - b) * (255 - b) / a) )
COMBINE_FUN(COMBINE_AND, a & b )
COMBINE_FUN(COMBINE_OR, a | b )
COMBINE_FUN(COMBINE_XOR, a ^ b )
COMBINE_FUN(COMBINE_SHADOW, (b * a * a) / (255 * 255) )
// ----------------------------------------------------------------------------- : Combining
/// Combine image b onto image a using some combining mode.
/// The results are stored in the image A.
template <ImageCombine combine>
void combineImageDo(Image& a, Image b) {
UInt size = a.GetWidth() * a.GetHeight() * 3;
Byte *dataA = a.GetData(), *dataB = b.GetData();
// for each pixel: apply function
for (UInt i = 0 ; i < size ; ++i) {
dataA[i] = Combine<combine>::f(dataA[i], dataB[i]);
}
}
void combineImage(Image& a, const Image& b, ImageCombine combine) {
// Images must have same size
assert(a.GetWidth() == b.GetWidth());
assert(a.GetHeight() == b.GetHeight());
// Copy alpha channel?
if (b.HasAlpha()) {
if (!a.HasAlpha()) a.InitAlpha();
memcpy(a.GetAlpha(), b.GetAlpha(), a.GetWidth() * a.GetHeight());
}
// Combine image data, by dispatching to combineImageDo
switch(combine) {
#define DISPATCH(comb) case comb: combineImageDo<comb>(a,b); return
DISPATCH(COMBINE_NORMAL);
DISPATCH(COMBINE_ADD);
DISPATCH(COMBINE_SUBTRACT);
DISPATCH(COMBINE_STAMP);
DISPATCH(COMBINE_DIFFERENCE);
DISPATCH(COMBINE_NEGATION);
DISPATCH(COMBINE_MULTIPLY);
DISPATCH(COMBINE_DARKEN);
DISPATCH(COMBINE_LIGHTEN);
DISPATCH(COMBINE_COLOR_DODGE);
DISPATCH(COMBINE_COLOR_BURN);
DISPATCH(COMBINE_SCREEN);
DISPATCH(COMBINE_OVERLAY);
DISPATCH(COMBINE_HARD_LIGHT);
DISPATCH(COMBINE_SOFT_LIGHT);
DISPATCH(COMBINE_REFLECT);
DISPATCH(COMBINE_GLOW);
DISPATCH(COMBINE_FREEZE);
DISPATCH(COMBINE_HEAT);
DISPATCH(COMBINE_AND);
DISPATCH(COMBINE_OR);
DISPATCH(COMBINE_XOR);
DISPATCH(COMBINE_SHADOW);
}
}
void drawCombineImage(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine) {
}
\ No newline at end of file
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GFX_GFX
#define HEADER_GFX_GFX
/** @file gfx/gfx.hpp
*
* Graphics/image processing functions.
*/
// ----------------------------------------------------------------------------- : Includes
#include "../util/prec.hpp"
// ----------------------------------------------------------------------------- : Resampling
/// Resample (resize) an image, uses bilenear filtering
/** The algorithm first resizes in horizontally, then vertically,
* the two passes are essentially the same:
* - for each row:
* - each input pixel becomes a fixed amount of output (in 1<<shift fixed point math)
* - for each output pixel:
* - 'eat' input pixels until the total is 1<<shift
* - write the total to the output pixel
* - to ensure the sum of all the pixel amounts is exacly width<<shift an extra rest amount
* is 'eaten' from the first pixel
*
* Uses fixed point numbers internally
*/
void resample(const Image& imgIn, Image& imgOut);
/// Resamples an image, first clips the input image to a specified rectangle,
/// that rectangle is resampledinto the entire output image
void resample_and_clip(const Image& imgIn, Image& imgOut, wxRect rect);
// ----------------------------------------------------------------------------- : Image rotation
/// Rotates an image counter clockwise
/// angle must be a multiple of 90, i.e. {0,90,180,270}
Image rotateImageBy(const Image& image, int angle);
// ----------------------------------------------------------------------------- : Blending
/// Blends two images together, using a horizontal gradient
/** The result is stored in img1
* To the left the color is that of img1, to the right of img2
*/
void hblend(Image& img1, const Image& img2);
/// Blends two images together, using a vertical gradient
void vblend(Image& img1, const Image& img2);
/// Blends two images together, using a third image as a mask
/** The result is stored in img1
* mask is used as a mask, white pixels are taken from img1, black pixels from img2
* color channels are blended separatly
*/
void maskBlend(Image& img1, const Image& img2, const Image& mask);
// ----------------------------------------------------------------------------- : Combining
/// Ways in which images can be combined, similair to what Photoshop supports
enum ImageCombine
{ COMBINE_NORMAL
, COMBINE_ADD
, COMBINE_SUBTRACT
, COMBINE_STAMP
, COMBINE_DIFFERENCE
, COMBINE_NEGATION
, COMBINE_MULTIPLY
, COMBINE_DARKEN
, COMBINE_LIGHTEN
, COMBINE_COLOR_DODGE
, COMBINE_COLOR_BURN
, COMBINE_SCREEN
, COMBINE_OVERLAY
, COMBINE_HARD_LIGHT
, COMBINE_SOFT_LIGHT
, COMBINE_REFLECT
, COMBINE_GLOW
, COMBINE_FREEZE
, COMBINE_HEAT
, COMBINE_AND
, COMBINE_OR
, COMBINE_XOR
, COMBINE_SHADOW
};
/// Combine image b onto image a using some combining function.
/// The results are stored in the image A.
/// This image gets the alpha channel from B, it should then be
/// drawn onto the area where A originated.
void combineImage(Image& a, const Image& b, ImageCombine combine);
/// Draw an image to a DC using a combining function
void drawCombineImage(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine);
// ----------------------------------------------------------------------------- : Utility
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
inline int col(int x) { return top(bot(x)); } //^ top and bottom range check for color values
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| 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 <gfx/polynomial.hpp>
#include <complex>
// ----------------------------------------------------------------------------- : Solving
UInt solveLinear(double a, double b, double* root) {
if (a == 0) {
if (b == 0) {
root[0] = 0;
return 1;
} else {
return 0;
}
} else {
root[0] = -b / a;
return 1;
}
}
UInt solveQuadratic(double a, double b, double c, double* roots) {
if (a == 0) {
return solveLinear(b, c, roots);
} else {
double d = b*b - 4*a*c;
if (d < 0) return 0;
roots[0] = (-b - sqrt(d)) / (2*a);
roots[1] = (-b + sqrt(d)) / (2*a);
return 2;
}
}
UInt solveCubic(double a, double b, double c, double d, double* roots) {
if (a == 0) {
return solveQuadratic(b, c, d, roots);
} else {
return solveCubic(b/a, c/a, d/a, roots);
}
}
// cubic root
template <typename T>
inline T curt(T x) { return pow(x, 1.0 / 3); }
UInt solveCubic(double a, double b, double c, double* roots) {
double p = b - a*a / 3;
double q = c + (2 * a*a*a - 9 * a * b) / 27;
if (p == 0 && q == 0) {
roots[0] = -a / 3;
return 1;
}
complex<double> u;
if (q > 0) {
u = curt(q/2 + sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
} else {
u = curt(q/2 - sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
}
// now for the complex part
// rot1(1, 0)
complex<double> rot2(-0.5, sqrt(3.0) / 2);
complex<double> rot3(-0.5, -sqrt(3.0) / 2);
complex<double> x1 = p / (3.0 * u) - u - a / 3.0;
complex<double> x2 = p / (3.0 * u * rot2) - u * rot2 - a / 3.0;
complex<double> x3 = p / (3.0 * u * rot3) - u * rot3 - a / 3.0;
// check if the solutions are real
UInt count = 0;
if (abs(x1.imag()) < 0.00001) {
roots[count] = x1.real();
count += 1;
}
if (abs(x2.imag()) < 0.00001) {
roots[count] = x2.real();
count += 1;
}
if (abs(x3.imag()) < 0.00001) {
roots[count] = x3.real();
count += 1;
}
return count;
}
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GFX_POLYNOMIAL
#define HEADER_GFX_POLYNOMIAL
/** @file gfx/polynomial.hpp
*
* Solutions to polynomials, used by bezier curve algorithms
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
// ----------------------------------------------------------------------------- : Solving
/// Solve a linear equation a x + b = 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
*/
UInt solveLinear(double a, double b, double* root);
/// Solve a quadratic equation a x^2 + b x + c == 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
*/
UInt solveQuadratic(double a, double b, double c, double* roots);
// Solve a cubic equation a x^3 + b x^2 + c x + d == 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
*/
UInt solveCubic(double a, double b, double c, double d, double* roots);
// Solve a cubic equation x^3 + a x^2 + b x + c == 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
* Based on http://en.wikipedia.org/wiki/Cubic_equation
*/
UInt solveCubic(double a, double b, double c, double* roots);
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| 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 "../util/prec.hpp"
#include "gfx.hpp"
// ----------------------------------------------------------------------------- : Implementation
// Rotates an image
// 'Rotater' is a function object that knows how to 'rotate' a pixel coordinate
template <class Rotater>
Image rotateImageImpl(Image img) {
UInt width = img.GetWidth(), height = img.GetHeight();
// initialize the return image
Image ret;
Rotater::init(ret, width, height);
Byte* in = img.GetData(), *out = ret.GetData();
// rotate each pixel
for (UInt y = 0 ; y < height ; ++y) {
for (UInt x = 0 ; x < width ; ++x) {
memcpy(out + 3 * Rotater::offset(x, y, width, height), in, 3);
in += 3;
}
}
// don't forget alpha
if (img.HasAlpha()) {
ret.InitAlpha();
in = img.GetAlpha();
out = ret.GetAlpha();
for (UInt y = 0 ; y < height ; ++y) {
for (UInt x = 0 ; x < width ; ++x) {
out[Rotater::offset(x, y, width, height)] = *in;
in += 1;
}
}
}
// ret is rotated image
return ret;
}
// ----------------------------------------------------------------------------- : Rotations
// Function object to handle rotation
struct Rotate90 {
/// Init a rotated image, where the source is w * h pixels
inline static void init(Image& img, UInt w, UInt h) {
img.Create(h, w, false);
}
/// Offset in the target data, x, y, w, h are SOURCE coordintes
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
int mx = y;
int my = w - x - 1;
return h * my + mx; // note: h, since that is the width of the target image
}
};
struct Rotate180 {
inline static void init(Image& img, UInt w, UInt h) {
img.Create(w, h, false);
}
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
UInt mx = w - x - 1;
UInt my = h - y - 1;
return w * my + mx;
}
};
struct Rotate270 {
inline static void init(Image& img, UInt w, UInt h) {
img.Create(h, w, false);
}
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
UInt mx = h - y - 1;
UInt my = x;
return h * my + mx;
}
};
// ----------------------------------------------------------------------------- : Interface
Image rotateImageBy(const Image& image, int angle) {
if (angle == 90) {
return rotateImageImpl<Rotate90>(image);
} else if (angle == 180){
return rotateImageImpl<Rotate180>(image);
} else if (angle == 270){
return rotateImageImpl<Rotate270>(image);
} else{
return image;
}
}
This diff is collapsed.
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_BASIC_SHAPE_EDITOR
#define HEADER_GUI_SYMBOL_BASIC_SHAPE_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <gui/symbol/editor.hpp>
class wxSpinCtrl;
// ----------------------------------------------------------------------------- : SymbolBasicShapeEditor
/// Editor for drawing basic shapes such as rectangles and polygons
class SymbolBasicShapeEditor : public SymbolEditorBase {
public:
SymbolBasicShapeEditor(SymbolControl* control);
// --------------------------------------------------- : Drawing
virtual void draw(DC& dc);
// --------------------------------------------------- : UI
virtual void initUI (wxToolBar* tb, wxMenuBar* mb);
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb);
virtual void onUpdateUI(wxUpdateUIEvent& e);
virtual void onCommand(int id);
virtual int modeToolId();
// --------------------------------------------------- : Mouse events
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev);
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev);
virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
// --------------------------------------------------- : Other events
virtual void onKeyChange(wxKeyEvent& ev);
virtual bool isEditing();
// --------------------------------------------------- : Data
private:
int mode;
SymbolPartP shape;
Vector2D start;
Vector2D end;
bool drawing;
// controls
wxSpinCtrl* sides;
wxStaticText* sidesL;
/// Cancel the drawing
void stopActions();
/// Make the shape
/** when centered: a = center, b-a = radius
* otherwise: a = top left, b = bottom right
*/
void makeShape(const Vector2D& a, const Vector2D& b, bool constrained, bool centered);
/// Make the shape, centered in c, with radius r
void makeCenteredShape(const Vector2D& c, Vector2D r, bool constrained);
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| 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 <gui/symbol/control.hpp>
#include <gui/symbol/window.hpp>
#include <gui/symbol/editor.hpp>
#include <gui/symbol/select_editor.hpp>
#include <gui/symbol/point_editor.hpp>
#include <gui/symbol/basic_shape_editor.hpp>
#include <gui/util.hpp>
#include <data/action/symbol.hpp>
#include <util/window_id.hpp>
#include <wx/dcbuffer.h>
// ----------------------------------------------------------------------------- : SymbolControl
SymbolControl::SymbolControl(SymbolWindow* parent, int id, const SymbolP& symbol)
: wxControl(parent, id)
, SymbolViewer(symbol)
, parent(parent)
{
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
}
void SymbolControl::switchEditor(const SymbolEditorBaseP& e) {
if (editor) editor->destroyUI(parent->GetToolBar(), parent->GetMenuBar());
editor = e;
if (editor) editor->initUI (parent->GetToolBar(), parent->GetMenuBar());
Refresh(false);
}
void SymbolControl::onSymbolChange() {
selectedParts.clear();
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
Refresh(false);
}
void SymbolControl::onModeChange(wxCommandEvent& ev) {
switch (ev.GetId()) {
case ID_MODE_SELECT:
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
break;
case ID_MODE_ROTATE:
switchEditor(new_shared2<SymbolSelectEditor>(this, true));
break;
case ID_MODE_POINTS:
if (selectedParts.size() == 1) {
singleSelection = *selectedParts.begin();
switchEditor(new_shared2<SymbolPointEditor>(this, singleSelection));
}
break;
case ID_MODE_SHAPES:
if (!selectedParts.empty()) {
selectedParts.clear();
signalSelectionChange();
}
switchEditor(new_shared1<SymbolBasicShapeEditor>(this));
break;
}
}
void SymbolControl::onExtraTool(wxCommandEvent& ev) {
if (editor) editor->onCommand(ev.GetId());
}
void SymbolControl::onAction(const Action& action) {
TYPE_CASE_(action, SymbolPartAction) {
Refresh(false);
}
}
void SymbolControl::onUpdateSelection() {
switch(editor->modeToolId()) {
case ID_MODE_POINTS:
// can only select a single part!
if (selectedParts.size() > 1) {
SymbolPartP part = *selectedParts.begin();
selectedParts.clear();
selectedParts.insert(part);
signalSelectionChange();
} else if (selectedParts.empty()) {
selectedParts.insert(singleSelection);
signalSelectionChange();
}
if (singleSelection != *selectedParts.begin()) {
// begin editing another part
singleSelection = *selectedParts.begin();
editor = new_shared2<SymbolPointEditor>(this, singleSelection);
Refresh(false);
}
break;
case ID_MODE_SHAPES:
if (!selectedParts.empty()) {
// there can't be a selection
selectedParts.clear();
signalSelectionChange();
}
break;
default:
Refresh(false);
break;
}
}
void SymbolControl::selectPart(const SymbolPartP& part) {
selectedParts.clear();
selectedParts.insert(part);
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
signalSelectionChange();
}
void SymbolControl::activatePart(const SymbolPartP& part) {
selectedParts.clear();
selectedParts.insert(part);
switchEditor(new_shared2<SymbolPointEditor>(this, part));
}
void SymbolControl::signalSelectionChange() {
parent->onSelectFromControl();
}
bool SymbolControl::isEditing() {
return editor && editor->isEditing();
}
// ----------------------------------------------------------------------------- : Drawing
void SymbolControl::draw(DC& dc) {
// clear the background
clearDC(dc, Color(0, 128, 0));
// draw symbol iself
SymbolViewer::draw(dc);
// draw editing overlay
if (editor) {
editor->draw(dc);
}
}
void SymbolControl::onPaint(wxPaintEvent& e) {
wxBufferedPaintDC dc(this);
dc.BeginDrawing();
draw(dc);
dc.EndDrawing();
}
// ----------------------------------------------------------------------------- : Events
// Mouse events, convert position, forward event
void SymbolControl::onLeftDown(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onLeftDown(pos, ev);
lastPos = pos;
ev.Skip(); // for focus
}
void SymbolControl::onLeftUp(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onLeftUp(pos, ev);
lastPos = pos;
}
void SymbolControl::onLeftDClick(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onLeftDClick(pos, ev);
lastPos = pos;
}
void SymbolControl::onRightDown(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onRightDown(pos, ev);
lastPos = pos;
}
void SymbolControl::onMotion(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
// Dragging something?
if (ev.LeftIsDown()) {
if (editor) editor->onMouseDrag(lastPos, pos, ev);
} else {
if (editor) editor->onMouseMove(lastPos, pos, ev);
}
lastPos = pos;
}
// Key events, just forward
void SymbolControl::onKeyChange(wxKeyEvent& ev) {
if (editor) editor->onKeyChange(ev);
ev.Skip(); // so we get char events
}
void SymbolControl::onChar(wxKeyEvent& ev) {
if (editor) editor->onChar(ev);
else ev.Skip();
}
void SymbolControl::onSize(wxSizeEvent& ev) {
wxSize s = ev.GetSize();
rotation.setZoom(min(s.GetWidth(), s.GetHeight()));
Refresh(false);
}
void SymbolControl::onUpdateUI(wxUpdateUIEvent& ev) {
if (!editor) return;
switch (ev.GetId()) {
case ID_MODE_SELECT: case ID_MODE_ROTATE: case ID_MODE_POINTS: case ID_MODE_SHAPES: //case ID_MODE_PAINT:
ev.Check(editor->modeToolId() == ev.GetId());
if (ev.GetId() == ID_MODE_POINTS) {
// can only edit points when a single part is selected <TODO?>
ev.Enable(selectedParts.size() == 1);
}
break;
case ID_MODE_PAINT:
ev.Enable(false); // TODO
break;
default:
if (ev.GetId() >= ID_CHILD_MIN && ev.GetId() < ID_CHILD_MAX) {
editor->onUpdateUI(ev); // foward to editor
}
}
}
// ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(SymbolControl, wxControl)
EVT_PAINT (SymbolControl::onPaint)
EVT_SIZE (SymbolControl::onSize)
EVT_LEFT_UP (SymbolControl::onLeftUp)
EVT_LEFT_DOWN (SymbolControl::onLeftDown)
EVT_RIGHT_DOWN (SymbolControl::onRightDown)
EVT_LEFT_DCLICK (SymbolControl::onLeftDClick)
EVT_MOTION (SymbolControl::onMotion)
EVT_KEY_UP (SymbolControl::onKeyChange)
EVT_KEY_DOWN (SymbolControl::onKeyChange)
EVT_CHAR (SymbolControl::onChar)
END_EVENT_TABLE ()
\ No newline at end of file
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_CONTROL
#define HEADER_GUI_SYMBOL_CONTROL
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <gui/symbol/viewer.hpp>
class SymbolWindow;
DECLARE_POINTER_TYPE(SymbolEditorBase);
// ----------------------------------------------------------------------------- : SymbolControl
/// Control for editing symbols
/** What kind of editing is done is determined by the contained SymbolEditorBase object
* That object handles all events and the drawing. This class is mostly just a proxy.
*/
class SymbolControl : public wxControl, public SymbolViewer {
public:
SymbolControl(SymbolWindow* parent, int id, const SymbolP& symbol);
virtual void onSymbolChange();
virtual void onAction(const Action&);
// Forward command to editor
void onExtraTool(wxCommandEvent& ev);
// Switch to some editing mode
void onModeChange(wxCommandEvent& ev);
/// Handle UpdateUIEvents propagated from the SymbolWindow
/** Handles events for editing mode related stuff
*/
void onUpdateUI(wxUpdateUIEvent& ev);
/// The selection has changed, tell the part list
void signalSelectionChange();
/// Activate a part, open it in the point editor
void activatePart(const SymbolPartP& part);
/// Select a specific part from the symbol
/// The editor is switched to the select editor
void selectPart(const SymbolPartP& part);
/// Update the selection
void onUpdateSelection();
/// Are we editing?
bool isEditing();
private:
/// Switch the a different editor object
void switchEditor(const SymbolEditorBaseP& e);
/// Draw the editor
void draw(DC& dc);
private:
DECLARE_EVENT_TABLE();
// --------------------------------------------------- : Data
public:
/// What parts are selected
set<SymbolPartP> selectedParts;
SymbolPartP singleSelection;
/// Parent window
SymbolWindow* parent;
private:
/// The current editor
SymbolEditorBaseP editor;
/// Last mouse position
Vector2D lastPos;
// --------------------------------------------------- : Events
void onLeftDown (wxMouseEvent& ev);
void onLeftUp (wxMouseEvent& ev);
void onLeftDClick(wxMouseEvent& ev);
void onRightDown (wxMouseEvent& ev);
void onMotion (wxMouseEvent& ev);
void onPaint (wxPaintEvent& e);
void onKeyChange(wxKeyEvent& ev);
void onChar (wxKeyEvent& ev);
void onSize (wxSizeEvent& ev);
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| 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 <gui/symbol/editor.hpp>
#include <gui/symbol/window.hpp>
// ----------------------------------------------------------------------------- : SymbolEditorBase
void SymbolEditorBase::SetStatusText(const String& text) {
control.parent->SetStatusText(text);
}
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_EDITOR
#define HEADER_GUI_SYMBOL_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <gui/symbol/control.hpp>
class SymbolControl;
// ----------------------------------------------------------------------------- : SymbolEditorBase
/// Base class for editors of symbols.
/** A symbol editor is like a FieldEditor, events are forwarded to it.
* Differrent SymbolEditors represent different tools.
* NOTE : Do not confuse with SymbolEditor (a FieldEditor)
*/
class SymbolEditorBase {
protected:
/// The control for which we are editing
SymbolControl& control;
inline SymbolP getSymbol() { return control.getSymbol(); }
void SetStatusText(const String& text);
public:
SymbolEditorBase(SymbolControl* control)
: control(*control)
{}
virtual ~SymbolEditorBase() {};
// --------------------------------------------------- : Drawing
/// Drawing for this control,
virtual void draw(DC& dc) = 0;
// --------------------------------------------------- : UI
/// Init extra toolbar items and menus needed for this panel
virtual void initUI(wxToolBar* tb, wxMenuBar* mb) {}
/// Destroy the extra items added by initUI
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb) {}
/// Update the UI by enabling/disabling items
virtual void onUpdateUI(wxUpdateUIEvent& ev) {}
/// Respond to one of the extra menu/tool items
virtual void onCommand(int id) {}
/// Tool id used in the symbol window
virtual int modeToolId() = 0;
// --------------------------------------------------- : Mouse events
/// The left mouse button has been pressed, at the given position (internal coordinates)
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {}
/// The left mouse button has been released, at the given position (internal coordinates)
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {}
/// The left mouse button has been double clicked, at the given position (internal coordinates)
virtual void onLeftDClick (const Vector2D& pos, wxMouseEvent& ev) {}
/// The right mouse button has been pressed, at the given position (internal coordinates)
virtual void onRightDown (const Vector2D& pos, wxMouseEvent& ev) {}
/// The mouse is being moved, no mouse buttons are pressed
virtual void onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {}
/// The mouse is being moved while mouse buttons are being pressed
virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {}
// --------------------------------------------------- : Keyboard events
/// A key is pressed or released, should be used for modifier keys (Shift/Ctrl/Alt)
virtual void onKeyChange (wxKeyEvent& ev) {}
/// A key is pressed/clicked
virtual void onChar (wxKeyEvent& ev) {}
// --------------------------------------------------- : Other events
/// A context menu is requested
virtual void onContextMenu(wxContextMenuEvent& ev) {}
/// Is the user currently editing, i.e. dragging the mouse?
/** This disables undo/redo, so the current action is not
* undone while it is in progress.
*/
virtual bool isEditing() { return false; }
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| 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 <gui/symbol/part_list.hpp>
#include <gui/util.hpp>
#include <data/action/symbol.hpp>
#include <wx/imaglist.h>
// ----------------------------------------------------------------------------- : Constructor
SymbolPartList::SymbolPartList(Window* parent, int id, SymbolP symbol)
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize,
wxLC_REPORT | wxLC_NO_HEADER | wxLC_VIRTUAL | wxLC_EDIT_LABELS)
, SymbolView(symbol)
{
// Create image list
wxImageList* images = new wxImageList(16,16);
// NOTE: this is based on the order of the SymbolPartCombine enum!
images->Add(loadResourceImage(_("COMBINE_OR")));
images->Add(loadResourceImage(_("COMBINE_SUB")));
images->Add(loadResourceImage(_("COMBINE_AND")));
images->Add(loadResourceImage(_("COMBINE_XOR")));
images->Add(loadResourceImage(_("COMBINE_OVER")));
images->Add(loadResourceImage(_("COMBINE_BORDER")));
AssignImageList(images, wxIMAGE_LIST_SMALL);
// create columns
InsertColumn(0, _("Name"));
update();
}
// ----------------------------------------------------------------------------- : View events
void SymbolPartList::onSymbolChange() {
update();
}
void SymbolPartList::onAction(const Action& action) {
TYPE_CASE(action, ReorderSymbolPartsAction) {
if (selected == (long) action.partId1) {
selectItem((long) action.partId2);
} else if (selected == (long) action.partId2) {
selectItem((long) action.partId1);
}
}
TYPE_CASE_(action, SymbolPartListAction) {
update();
}
}
// ----------------------------------------------------------------------------- : Other
String SymbolPartList::OnGetItemText(long item, long col) const {
assert(col == 0);
return getPart(item)->name;
}
int SymbolPartList::OnGetItemImage(long item) const {
return getPart(item)->combine;
}
SymbolPartP SymbolPartList::getPart(long item) const {
return symbol->parts.at(item);
}
void SymbolPartList::selectItem(long item) {
selected = (long)item;
long count = GetItemCount();
for (long i = 0 ; i < count ; ++i) {
SetItemState(i, i == selected ? wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED : 0,
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
}
}
void SymbolPartList::getSelectedParts(set<SymbolPartP>& sel) {
sel.clear();
long count = GetItemCount();
for (long i = 0 ; i < count ; ++ i) {
bool selected = GetItemState(i, wxLIST_STATE_SELECTED);
if (selected) {
sel.insert(symbol->parts.at(i));
}
}
}
void SymbolPartList::selectParts(const set<SymbolPartP>& sel) {
long count = GetItemCount();
for (long i = 0 ; i < count ; ++ i) {
// is that part selected?
bool selected = sel.find(symbol->parts.at(i)) != sel.end();
SetItemState(i, selected ? wxLIST_STATE_SELECTED : 0,
wxLIST_STATE_SELECTED);
}
}
void SymbolPartList::update() {
if (symbol->parts.empty()) {
// deleting all items requires a full refresh on win32
SetItemCount(0);
Refresh(true);
} else {
SetItemCount((long) symbol->parts.size() );
Refresh(false);
}
}
// ----------------------------------------------------------------------------- : Event handling
void SymbolPartList::onSelect(wxListEvent& ev) {
selected = ev.GetIndex();
ev.Skip();
}
void SymbolPartList::onDeselect(wxListEvent& ev) {
selected = -1;
ev.Skip();
}
void SymbolPartList::onLabelEdit(wxListEvent& ev){
symbol->actions.add(
new SymbolPartNameAction(getPart(ev.GetIndex()), ev.GetLabel())
);
}
void SymbolPartList::onSize(wxSizeEvent& ev) {
wxSize s = GetClientSize();
SetColumnWidth(0, s.GetWidth() - 2);
}
void SymbolPartList::onDrag(wxMouseEvent& ev) {
if (!ev.Dragging() || selected == -1) return;
// reorder the list of parts
int flags;
long item = HitTest(ev.GetPosition(), flags);
if (flags & wxLIST_HITTEST_ONITEM) {
if (item > 0) EnsureVisible(item - 1);
if (item < GetItemCount() - 1) EnsureVisible(item + 1);
if (item != selected) {
// swap the two items
symbol->actions.add(new ReorderSymbolPartsAction(*symbol, item, selected));
selectItem(item); // deselect all other items, to prevent 'lassoing' them
}
}
}
// ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(SymbolPartList, wxListCtrl)
EVT_LIST_ITEM_SELECTED (wxID_ANY, SymbolPartList::onSelect)
EVT_LIST_ITEM_DESELECTED (wxID_ANY, SymbolPartList::onDeselect)
EVT_LIST_END_LABEL_EDIT (wxID_ANY, SymbolPartList::onLabelEdit)
EVT_SIZE ( SymbolPartList::onSize)
EVT_MOTION ( SymbolPartList::onDrag)
END_EVENT_TABLE ()
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_PART_LIST
#define HEADER_GUI_SYMBOL_PART_LIST
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <wx/listctrl.h>
// ----------------------------------------------------------------------------- : SymbolPartList
// A list view of parts of a symbol
class SymbolPartList : public wxListCtrl, public SymbolView {
public:
SymbolPartList(Window* parent, int id, SymbolP symbol = SymbolP());
/// Update the list
void update();
/// Is there a selection?
inline bool hasSelection() const { return selected != -1; }
/// Return the last part that was selected
/** @pre hasSelection()
*/
inline SymbolPartP getSelection() const { return getPart(selected); }
/// Get a set of selected parts
void getSelectedParts(set<SymbolPartP>& sel);
/// Select the specified parts, and nothing else
void selectParts(const set<SymbolPartP>& sel);
/// Another symbol is being viewed
void onSymbolChange();
/// Event handler for changes to the symbol
virtual void onAction(const Action& a);
protected:
/// Get the text of an item
virtual String OnGetItemText(long item, long col) const;
/// Get the icon of an item
virtual int OnGetItemImage(long item) const;
private:
/// The selected item, or -1 if there is no selection
long selected;
/// Get a part from the symbol
SymbolPartP getPart(long item) const;
/// Select an item, also in the list control
/// Deselects all other items
void selectItem(long item);
// --------------------------------------------------- : Event handling
DECLARE_EVENT_TABLE();
void onSelect (wxListEvent& ev);
void onDeselect (wxListEvent& ev);
void onLabelEdit(wxListEvent& ev);
void onSize (wxSizeEvent& ev);
void onDrag (wxMouseEvent& ev);
};
// ----------------------------------------------------------------------------- : EOF
#endif
This diff is collapsed.
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_POINT_EDITOR
#define HEADER_GUI_SYMBOL_POINT_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <gui/symbol/editor.hpp>
class HandleMoveAction;
class ControlPointMoveAction;
class CurveDragAction;
// ----------------------------------------------------------------------------- : SymbolPointEditor
// Symbol editor for editing control points and handles
class SymbolPointEditor : public SymbolEditorBase {
public:
SymbolPointEditor(SymbolControl* control, const SymbolPartP& part);
// --------------------------------------------------- : Drawing
virtual void draw(DC& dc);
private:
/// Draws a gradient on the selected line to indicate curve dragging
void drawHoveredLine(DC& dc);
/// Draw all handles belonging to selected points
void drawHandles(DC& dc);
/// Draws the point to be inserted
void drawNewPoint(DC& dc);
/// Draw a single control point
void drawControlPoint(DC& dc, const ControlPoint& p, bool drawHandleBefore, bool drawHandleAfter);
/// Draws a handle as a box
void drawHandleBox(DC& dc, UInt px, UInt py, bool active);
/// Draws a handle as a circle
void drawHandleCircle(DC& dc, UInt px, UInt py);
enum WhichPen {
PEN_NORMAL, //^ Pen for normal handles
PEN_HOVER, //^ Pen for hovered handles
PEN_LINE, //^ Pen for the line to handles
PEN_MAIN, //^ Pen for the main handle
PEN_NEW_POINT //^ Pen for the new point
};
/// Retrieve a pen for the drawing of parts of handles
wxPen handlePen(WhichPen p, LockMode lock);
/// Retrieve a pen for the drawing of other things
wxPen otherPen(WhichPen p);
public:
// --------------------------------------------------- : UI
virtual void initUI(wxToolBar* tb, wxMenuBar* mb);
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb);
virtual void onUpdateUI(wxUpdateUIEvent& e);
virtual void onCommand(int id);
virtual int modeToolId();
// --------------------------------------------------- : Mouse events
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev);
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev);
virtual void onLeftDClick(const Vector2D& pos, wxMouseEvent& ev);
virtual void onMouseMove(const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
virtual void onMouseDrag(const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
// --------------------------------------------------- : Other events
virtual void onKeyChange(wxKeyEvent& ev);
virtual void onChar(wxKeyEvent& ev);
virtual bool isEditing();
private:
// --------------------------------------------------- : Data
// The symbol part we are editing
SymbolPartP part;
// Actions in progress
// All are owned by the action stack, or they are 0
HandleMoveAction* handleMoveAction;
ControlPointMoveAction* controlPointMoveAction;
CurveDragAction* curveDragAction;
// Selection
enum Selection {
SELECTED_NONE, //^ no selection
SELECTED_POINTS, //^ some points are selected
SELECTED_HANDLE, //^ a handle is selected
SELECTED_LINE, //^ a line is selected
SELECTED_NEW_POINT //^ a new point on a line (used for hovering)
};
Selection selection;
// points
set<ControlPointP> selectedPoints;
// handle
SelectedHandle selectedHandle;
// line
ControlPointP selectedLine1, selectedLine2; // selected the line between these points
double selectedLineT; // time on the line of the selection
// Mouse feedback
Selection hovering;
// handle
SelectedHandle hoverHandle; // the handle currently under the cursor
// new point
Vector2D newPoint;
// line
ControlPointP hoverLine1, hoverLine2; // hovering on the line between these points
double hoverLineT;
int hoverLine1Idx; // index of hoverLine1 in the list of points
// Gui stock
wxBitmap background;
wxCursor pointSelect, pointAdd, pointCurve, pointMove;
// --------------------------------------------------- : Selection
/// Clears the selection
void selectNothing();
/// Select a point, if toggle then toggles the selection of the point
void selectPoint(const ControlPointP& point, bool toggle);
void selectHandle(const SelectedHandle& h, const wxMouseEvent& keystate);
void selectLine(const wxMouseEvent& keystate);
/// Is a point selected?
bool pointSelected(const ControlPointP& pnt);
bool pointSelected(const ControlPoint& pnt);
/// Is the mouse pointer above a point?
bool pointHovered(const ControlPointP& pnt);
bool pointHovered(const ControlPoint& pnt);
/// Is the mouse pointer above a handle of a point?
bool handleHovered(const ControlPointP& pnt, WhichHandle wh);
bool handleHovered(const ControlPoint& pnt, WhichHandle wh);
// --------------------------------------------------- : Actions
// Finalize actions; new events start new actions
void resetActions();
void deleteSelection();
void onChangeSegment(SegmentMode mode);
void onChangeLock (LockMode mode);
// --------------------------------------------------- : Finding items
/// Finds the item that is currently being hovered, stores the results in hover*
void findHoveredItem(const Vector2D& pos, bool altDown);
/// Is the specified position on a curve?
/// If so, sets hoverLine*, and set hovering=hoveringLine
bool checkPosOnCurve(const Vector2D& pos);
/// Finds a handle at or near pos
SelectedHandle findHandle(const Vector2D& pos);
/// Is the manhatan distance between two points <= range?
bool inRange(const Vector2D& a, const Vector2D& b, double range);
};
// ----------------------------------------------------------------------------- : EOF
#endif
This diff is collapsed.
//+----------------------------------------------------------------------------+
//| 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) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_SELECT_EDITOR
#define HEADER_GUI_SYMBOL_SELECT_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <gui/symbol/editor.hpp>
DECLARE_POINTER_TYPE(SymbolPartMoveAction);
DECLARE_POINTER_TYPE(SymbolPartScaleAction);
DECLARE_POINTER_TYPE(SymbolPartRotateAction);
DECLARE_POINTER_TYPE(SymbolPartShearAction);
// ----------------------------------------------------------------------------- : SymbolSelectEditor
/// Editor that allows the user to select symbol parts
class SymbolSelectEditor : public SymbolEditorBase {
public:
SymbolSelectEditor(SymbolControl* control, bool rotate);
// --------------------------------------------------- : Drawing
virtual void draw(DC& dc);
private:
/// Draw handles on all sides
void drawHandles(DC& dc);
/// Draw a handle, dx and dy indicate the side, can be {-1,0,1}
void drawHandle(DC& dc, int dx, int dy);
/// Draw the rotation center
void drawRotationCenter(DC& dc, const Vector2D& pos);
public:
// --------------------------------------------------- : UI
virtual void initUI (wxToolBar* tb, wxMenuBar* mb);
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb);
virtual void onUpdateUI(wxUpdateUIEvent& e);
virtual void onCommand(int id);
virtual int modeToolId();
// --------------------------------------------------- : Mouse events
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev);
virtual void onLeftDClick (const Vector2D& pos, wxMouseEvent& ev);
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev);
virtual void onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
// --------------------------------------------------- : Other events
virtual void onKeyChange (wxKeyEvent& ev);
virtual void onChar (wxKeyEvent& ev);
virtual bool isEditing();
private:
// The part under the mouse cursor
SymbolPartP highlightPart;
// Actions
// All are either owned by the symbol's action stack or equal 0
SymbolPartMoveAction* moveAction;
SymbolPartScaleAction* scaleAction;
SymbolPartRotateAction* rotateAction;
SymbolPartShearAction* shearAction;
// Bounding box of selection
Vector2D minV, maxV;
// Where is the rotation center?
Vector2D center;
// At what angle is the handle we started draging for rotation
double startAngle;
// what side are we dragging/rotating on?
int scaleX, scaleY;
// Do we want to rotate?
bool rotate;
// Graphics assets
wxCursor cursorRotate;
wxCursor cursorShearX;
wxCursor cursorShearY;
Bitmap handleRotateTL, handleRotateTR, handleRotateBL, handleRotateBR;
Bitmap handleShearX, handleShearY;
Bitmap handleCenter;
/// Is the mouse on a scale/rotate handle?
bool onHandle(const Vector2D& mpos, int dx, int dy);
/// Is the mouse on any handle?
/** Returns the handle coordinates [-1..1] in d?Out
*/
bool onAnyHandle(const Vector2D& mpos, int* dxOut, int* dyOut);
/// Angle between center and pos
double angleTo(const Vector2D& pos);
/// Return the position of a handle, dx,dy in <-1, 0, 1>
Vector2D handlePos(int dx, int dy);
/// Find the first part at the given position
SymbolPartP findPart(const Vector2D& pos);
/// Update minV and maxV to be the bounding box of the selectedParts
/// Updates center to be the rotation center of the parts
void updateBoundingBox();
/// Reset all the actions to 0
void resetActions();
};
// ----------------------------------------------------------------------------- : EOF
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment