initial checkin of C++ port (in progress)

git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@2 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
twanvl
2006-10-01 14:08:07 +00:00
parent f5c0071da6
commit ddfb1a5089
56 changed files with 8781 additions and 0 deletions
+368
View File
@@ -0,0 +1,368 @@
//+----------------------------------------------------------------------------+
//| 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/action/symbol.hpp>
#include <data/action/symbol_part.hpp>
typedef pair<SymbolPartP,SymbolPartCombine> pair_part_combine_t;
typedef pair<SymbolPartP,size_t > pair_part_size_t;
DECLARE_TYPEOF_COLLECTION(pair_part_combine_t);
DECLARE_TYPEOF_COLLECTION(pair_part_size_t);
// ----------------------------------------------------------------------------- : Moving symbol parts
SymbolPartMoveAction::SymbolPartMoveAction(const set<SymbolPartP>& parts)
: parts(parts)
, constrain(false)
{}
String SymbolPartMoveAction::getName(bool toUndo) const {
return parts.size() == 1 ? _("Move shape") : _("Move shapes");
}
void SymbolPartMoveAction::perform(bool toUndo) {
// move the points back
FOR_EACH(p, parts) {
p->minPos -= moved;
p->maxPos -= moved;
FOR_EACH(pnt, p->points) {
pnt->pos -= moved;
}
}
moved = -moved;
}
void SymbolPartMoveAction::move(const Vector2D& deltaDelta) {
delta += deltaDelta;
// Move each point by deltaDelta, possibly constrained
Vector2D d = constrainVector(delta, constrain);
Vector2D dd = d - moved;
FOR_EACH(p, parts) {
p->minPos += dd;
p->maxPos += dd;
FOR_EACH(pnt, p->points) {
pnt->pos += dd;
}
}
moved = d;
}
// ----------------------------------------------------------------------------- : Rotating symbol parts
SymbolPartMatrixAction::SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center)
: parts(parts)
, center(center)
{}
void SymbolPartMatrixAction::transform(const Vector2D& mx, const Vector2D& my) {
// Transform each point
FOR_EACH(p, parts) {
FOR_EACH(pnt, p->points) {
pnt->pos = (pnt->pos - center).mul(mx,my) + center;
pnt->deltaBefore = pnt->deltaBefore.mul(mx,my);
pnt->deltaAfter = pnt->deltaAfter .mul(mx,my);
}
// bounds change after transforming
p->calculateBounds();
}
}
SymbolPartRotateAction::SymbolPartRotateAction(const set<SymbolPartP>& parts, const Vector2D& center)
: SymbolPartMatrixAction(parts, center)
, constrain(false)
, angle(0)
{}
String SymbolPartRotateAction::getName(bool toUndo) const {
return parts.size() == 1 ? _("Rotate shape") : _("Rotate shapes");
}
void SymbolPartRotateAction::perform(bool toUndo) {
// move the points back
rotateBy(-angle);
angle = -angle;
}
void SymbolPartRotateAction::rotateTo(double newAngle) {
double oldAngle = angle;
angle = newAngle;
// constrain?
if (constrain) {
// multiples of 2pi/24 i.e. 24 stops
double mult = (2 * M_PI) / 24;
angle = floor(angle / mult + 0.5) * mult;
}
if (oldAngle != angle) rotateBy(angle - oldAngle);
}
void SymbolPartRotateAction::rotateBy(double deltaAngle) {
// Rotation 'matrix'
transform(
Vector2D(cos(deltaAngle), -sin(deltaAngle)),
Vector2D(sin(deltaAngle), cos(deltaAngle))
);
}
// ----------------------------------------------------------------------------- : Shearing symbol parts
SymbolPartShearAction::SymbolPartShearAction(const set<SymbolPartP>& parts, const Vector2D& center)
: SymbolPartMatrixAction(parts, center)
, constrain(false)
{}
String SymbolPartShearAction::getName(bool toUndo) const {
return parts.size() == 1 ? _("Shear shape") : _("Shear shapes");
}
void SymbolPartShearAction::perform(bool toUndo) {
// move the points back
// the vector shear = (x,y) is used as:
// <1 x>
// <y 1>
// inverse is:
// <1 -x> /
// <-y 1> / (1 - xy)
// we have: xy = 0 => (1 - xy) = 1
shearBy(-shear);
}
void SymbolPartShearAction::move(const Vector2D& deltaShear) {
shear += deltaShear;
shearBy(deltaShear);
}
void SymbolPartShearAction::shearBy(const Vector2D& shear) {
// Shear 'matrix'
transform(
Vector2D(1, shear.x),
Vector2D(shear.y, 1)
);
}
// ----------------------------------------------------------------------------- : Scaling symbol parts
SymbolPartScaleAction::SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY)
: parts(parts)
, constrain(false)
, scaleX(scaleX), scaleY(scaleY)
{
// Find min and max coordinates
oldMin = Vector2D::infinity();
Vector2D oldMax = -Vector2D::infinity();
FOR_EACH(p, parts) {
oldMin = piecewise_min(oldMin, p->minPos);
oldMax = piecewise_max(oldMax, p->maxPos);
}
// new == old
newMin = newRealMin = oldMin;
newSize = newRealSize = oldSize = oldMax - oldMin;
}
String SymbolPartScaleAction::getName(bool toUndo) const {
return parts.size() == 1 ? _("Scale shape") : _("Scale shapes");
}
void SymbolPartScaleAction::perform(bool toUndo) {
swap(oldMin, newMin);
swap(oldSize, newSize);
transformAll();
}
void SymbolPartScaleAction::move(const Vector2D& deltaMin, const Vector2D& deltaMax) {
newRealMin += deltaMin;
newRealSize += deltaMax - deltaMin;
update();
}
void SymbolPartScaleAction::update() {
// Move each point so the range <oldMin...oldMax> maps to <newMin...newMax>
// we have already moved to the current <newMin...newMax>
Vector2D tmpMin = oldMin, tmpSize = oldSize; // the size before any scaling
oldMin = newMin; oldSize = newSize; // the size before this move
// the size after the move
newMin = newRealMin; newSize = newRealSize;
if (constrain && scaleX != 0 && scaleY != 0) {
Vector2D scale = newSize.div(tmpSize);
scale = constrainVector(scale, true, true);
newSize = tmpSize.mul(scale);
newMin += (newRealSize - newSize).mul(Vector2D(scaleX == -1 ? 1 : 0, scaleY == -1 ? 1 : 0));
}
// now move all points
transformAll();
// restore oldMin/Size
oldMin = tmpMin; oldSize = tmpSize;
}
void SymbolPartScaleAction::transformAll() {
Vector2D scale = newSize.div(oldSize);
FOR_EACH(p, parts) {
p->minPos = transform(p->minPos);
p->maxPos = transform(p->maxPos);
// make sure that max >= min
if (p->minPos.x > p->maxPos.x) swap(p->minPos.x, p->maxPos.x);
if (p->minPos.y > p->maxPos.y) swap(p->minPos.y, p->maxPos.y);
// scale all points
FOR_EACH(pnt, p->points) {
pnt->pos = transform(pnt->pos);
// also scale handles
pnt->deltaBefore = pnt->deltaBefore.mul(scale);
pnt->deltaAfter = pnt->deltaAfter .mul(scale);
}
}
}
// ----------------------------------------------------------------------------- : Change combine mode
CombiningModeAction::CombiningModeAction(const set<SymbolPartP>& parts, SymbolPartCombine mode) {
FOR_EACH(p, parts) {
this->parts.push_back(make_pair(p,mode));
}
}
String CombiningModeAction::getName(bool toUndo) const {
return _("Change combine mode");
}
void CombiningModeAction::perform(bool toUndo) {
FOR_EACH(pm, parts) {
swap(pm.first->combine, pm.second);
}
}
// ----------------------------------------------------------------------------- : Change name
SymbolPartNameAction::SymbolPartNameAction(const SymbolPartP& part, const String& name)
: part(part), partName(name)
{}
String SymbolPartNameAction::getName(bool toUndo) const {
return _("Change shape name");
}
void SymbolPartNameAction::perform(bool toUndo) {
swap(part->name, partName);
}
// ----------------------------------------------------------------------------- : Add symbol part
AddSymbolPartAction::AddSymbolPartAction(Symbol& symbol, const SymbolPartP& part)
: symbol(symbol), part(part)
{}
String AddSymbolPartAction::getName(bool toUndo) const {
return _("Add ") + part->name;
}
void AddSymbolPartAction::perform(bool toUndo) {
if (toUndo) {
assert(!symbol.parts.empty());
symbol.parts.erase (symbol.parts.begin());
} else {
symbol.parts.insert(symbol.parts.begin(), part);
}
}
// ----------------------------------------------------------------------------- : Remove symbol part
RemoveSymbolPartsAction::RemoveSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
: symbol(symbol)
{
size_t index = 0;
FOR_EACH(p, symbol.parts) {
if (parts.find(p) != parts.end()) {
removals.push_back(make_pair(p, index)); // remove this part
}
++index;
}
}
String RemoveSymbolPartsAction::getName(bool toUndo) const {
return removals.size() == 1 ? _("Remove shape") : _("Remove shapes");
}
void RemoveSymbolPartsAction::perform(bool toUndo) {
if (toUndo) {
// reinsert the parts
// ascending order, this is the reverse of removal
FOR_EACH(r, removals) {
assert(r.second <= symbol.parts.size());
symbol.parts.insert(symbol.parts.begin() + r.second, r.first);
}
} else {
// remove the parts
// descending order, because earlier removals shift the rest of the vector
FOR_EACH_REVERSE(r, removals) {
assert(r.second < symbol.parts.size());
symbol.parts.erase(symbol.parts.begin() + r.second);
}
}
}
// ----------------------------------------------------------------------------- : Duplicate symbol parts
DuplicateSymbolPartsAction::DuplicateSymbolPartsAction(Symbol& symbol, const set<SymbolPartP>& parts)
: symbol(symbol)
{
UInt index = 0;
FOR_EACH(p, symbol.parts) {
index += 1;
if (parts.find(p) != parts.end()) {
// duplicate this part
duplications.push_back(make_pair(p->clone(), index));
index += 1; // the clone also takes up space on the vector
}
}
}
String DuplicateSymbolPartsAction::getName(bool toUndo) const {
return duplications.size() == 1 ? _("Duplicate shape") : _("Duplicate shapes");
}
void DuplicateSymbolPartsAction::perform(bool toUndo) {
if (toUndo) {
// remove the clones
// walk in reverse order, otherwise we will shift the vector
FOR_EACH_REVERSE(d, duplications) {
assert(d.second < symbol.parts.size());
symbol.parts.erase(symbol.parts.begin() + d.second);
}
} else {
// insert the clones
FOR_EACH(d, duplications) {
assert(d.second <= symbol.parts.size());
symbol.parts.insert(symbol.parts.begin() + d.second, d.first);
}
}
}
void DuplicateSymbolPartsAction::getParts(set<SymbolPartP>& parts) {
parts.clear();
FOR_EACH(d, duplications) {
parts.insert(d.first);
}
}
// ----------------------------------------------------------------------------- : Reorder symbol parts
ReorderSymbolPartsAction::ReorderSymbolPartsAction(Symbol& symbol, size_t partId1, size_t partId2)
: symbol(symbol), partId1(partId1), partId2(partId2)
{}
String ReorderSymbolPartsAction::getName(bool toUndo) const {
return _("Reorder");
}
void ReorderSymbolPartsAction::perform(bool toUndo) {
assert(partId1 < symbol.parts.size());
assert(partId2 < symbol.parts.size());
swap(symbol.parts[partId1], symbol.parts[partId2]);
}
+241
View File
@@ -0,0 +1,241 @@
//+----------------------------------------------------------------------------+
//| 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
+379
View File
@@ -0,0 +1,379 @@
//+----------------------------------------------------------------------------+
//| 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/action/symbol_part.hpp>
#include <gfx/bezier.hpp>
DECLARE_TYPEOF_COLLECTION(Vector2D);
// ----------------------------------------------------------------------------- : Utility
inline double sgn(double v) { return v > 0 ? 1 : -1; }
Vector2D constrainVector(const Vector2D& v, bool constrain, bool onlyDiagonal) {
if (!constrain) return v;
double ax = abs(v.x), ay = abs(v.y);
if (ax * 2 < ay && !onlyDiagonal) {
return Vector2D(0, v.y); // vertical
} else if(ay * 2 < ax && !onlyDiagonal) {
return Vector2D(v.x, 0); // horizontal
} else {
return Vector2D( // diagonal
sgn(v.x) * (ax + ay) / 2,
sgn(v.y) * (ax + ay) / 2
);
}
}
// ----------------------------------------------------------------------------- : Move control point
ControlPointMoveAction::ControlPointMoveAction(const set<ControlPointP>& points)
: points(points)
, constrain(false)
{
oldValues.reserve(points.size());
FOR_EACH(p, points) {
oldValues.push_back(p->pos);
}
}
String ControlPointMoveAction::getName(bool toUndo) const {
return points.size() == 1 ? _("Move point") : _("Move points");
}
void ControlPointMoveAction::perform(bool toUndo) {
/*
set<ControlPointP>::const_iterator it = points.begin();
vector<Vector2D> ::iterator it2 = oldValues.begin();
for( ; it != points.end() && it2 != oldValues.end() ; ++it, ++it2) {
swap<Vector2D>((*it)->pos, *it2);
}
*/
FOR_EACH_2(p,points, op,oldValues) {
swap(p->pos, op);
}
}
void ControlPointMoveAction::move (const Vector2D& deltaDelta) {
delta += deltaDelta;
// Move each point by delta, possibly constrained
Vector2D d = constrainVector(delta, constrain);
set<ControlPointP>::const_iterator it = points.begin();
vector<Vector2D> ::iterator it2 = oldValues.begin();
for( ; it != points.end() && it2 != oldValues.end() ; ++it, ++it2) {
(*it)->pos = (*it2) + d;
}
}
// ----------------------------------------------------------------------------- : Move handle
HandleMoveAction::HandleMoveAction(const SelectedHandle& handle)
: handle(handle)
, constrain(false)
, oldHandle(handle.getHandle())
, oldOther (handle.getOther())
{}
String HandleMoveAction::getName(bool toUndo) const {
return _("Move handle");
}
void HandleMoveAction::perform(bool toUndo) {
swap(oldHandle, handle.getHandle());
swap(oldOther, handle.getOther());
}
void HandleMoveAction::move(const Vector2D& deltaDelta) {
delta += deltaDelta;
handle.getHandle() = constrainVector(oldHandle + delta, constrain);
handle.getOther() = oldOther;
handle.onUpdateHandle();
}
// ----------------------------------------------------------------------------- : Segment mode
ControlPointUpdate::ControlPointUpdate(const ControlPointP& pnt)
: other(*pnt)
, point(pnt)
{}
void ControlPointUpdate::perform() {
swap(other, *point);
}
SegmentModeAction::SegmentModeAction(const ControlPointP& p1, const ControlPointP& p2, SegmentMode mode)
: point1(p1), point2(p2)
{
if (p1->segmentAfter == mode) return;
point1.other.segmentAfter = point2.other.segmentBefore = mode;
if (mode == SEGMENT_LINE) {
point1.other.deltaAfter = Vector2D(0,0);
point2.other.deltaBefore = Vector2D(0,0);
point1.other.lock = LOCK_FREE;
point2.other.lock = LOCK_FREE;
} else if (mode == SEGMENT_CURVE) {
point1.other.deltaAfter = (p2->pos - p1->pos) / 3.0f;
point2.other.deltaBefore = (p1->pos - p2->pos) / 3.0f;
}
}
String SegmentModeAction::getName(bool toUndo) const {
SegmentMode mode = toUndo ? point1.point->segmentAfter : point1.other.segmentAfter;
if (mode == SEGMENT_LINE) return _("Convert to line");
else return _("Convert to curve");
}
void SegmentModeAction::perform(bool toUndo) {
point1.perform();
point2.perform();
}
// ----------------------------------------------------------------------------- : Locking mode
LockModeAction::LockModeAction(const ControlPointP& p, LockMode lock)
: point(p)
{
point.other.lock = lock;
point.other.onUpdateLock();
}
String LockModeAction::getName(bool toUndo) const {
return _("Lock point");
}
void LockModeAction::perform(bool toUndo) {
point.perform();
}
// ----------------------------------------------------------------------------- : Move curve
CurveDragAction::CurveDragAction(const ControlPointP& point1, const ControlPointP& point2)
: SegmentModeAction(point1, point2, SEGMENT_CURVE)
{}
String CurveDragAction::getName(bool toUndo) const {
return _("Move curve");
}
void CurveDragAction::perform(bool toUndo) {
SegmentModeAction::perform(toUndo);
}
void CurveDragAction::move(const Vector2D& delta, double t) {
// Logic:
// Assuming old point is p, new point is p'
// Point on old bezier curve is:
// p = a t^3 + 3b (1-t) t^2 + 3c (1-t)^2 t + d (1-t)^2
// Point on new bezier curve is:
// p_(' = a t^3 + 3b') (1-t) t^2 + 3c' (1-t)^2 t + d (1-t)^2
// We now want to change control points b and c, the closer we are to b (t close to 0)
// the more effect we have on b, so we substitute:
// b' = b + x t
// c' = c + x (1-t)
// Solving for x we get:
// x = (p'-p) / ( t (1-t) ( t^2 + (1-t)^2) )
// Naming:
// delta = p' - p
// pointDelta = x * t * (1-t)
Vector2D pointDelta = delta / (3 * (t * t + (1-t) * (1-t)));
point1.point->deltaAfter += pointDelta / t;
point2.point->deltaBefore += pointDelta / (1-t);
point1.point->onUpdateHandle(HANDLE_AFTER);
point2.point->onUpdateHandle(HANDLE_BEFORE);
}
// ----------------------------------------------------------------------------- : Add control point
ControlPointAddAction::ControlPointAddAction(const SymbolPartP& part, UInt insertAfter, double t)
: point1(part->getPoint(insertAfter))
, point2(part->getPoint(insertAfter + 1))
, part(part)
, insertAfter(insertAfter)
, newPoint(new ControlPoint())
{
// calculate new point
if (point1.other.segmentAfter == SEGMENT_CURVE) {
// calculate new handles using de Casteljau's subdivision algorithm
deCasteljau(point1.other, point2.other, t, *newPoint);
// unlock if needed
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
if (point2.other.lock == LOCK_SIZE) point2.other.lock = LOCK_DIR;
newPoint->lock = LOCK_DIR;
newPoint->segmentBefore = SEGMENT_CURVE;
newPoint->segmentAfter = SEGMENT_CURVE;
} else {
newPoint->pos = point1.other.pos * (1 - t) + point2.other.pos * t;
newPoint->lock = LOCK_FREE;
newPoint->segmentBefore = SEGMENT_LINE;
newPoint->segmentAfter = SEGMENT_LINE;
}
}
String ControlPointAddAction::getName(bool toUndo) const {
return _("Add control point");
}
void ControlPointAddAction::perform(bool toUndo) {
if (toUndo) { // remove the point
part->points.erase( part->points.begin() + insertAfter + 1);
} else {
part->points.insert(part->points.begin() + insertAfter + 1, newPoint);
}
// update points before/after
point1.perform();
point2.perform();
}
// ----------------------------------------------------------------------------- : Remove control point
/// Sqaure root that caries the sign over the root
/// or formally: ssqrt(x) = Re<sqrt(x)> - Im<sqrt(x)> = x / sqrt(|x|)
double ssqrt(double x) {
if (x > 0) return sqrt(x);
else return -sqrt(-x);
}
// Remove a single control point
class SinglePointRemoveAction : public Action {
public:
SinglePointRemoveAction(const SymbolPartP& part, UInt position);
virtual String getName(bool toUndo) const { return _("Delete point"); }
virtual void perform(bool toUndo);
private:
SymbolPartP part;
UInt position;
ControlPointP point; //^ Removed point
ControlPointUpdate point1, point2; //^ Points before/after
};
SinglePointRemoveAction::SinglePointRemoveAction(const SymbolPartP& part, UInt position)
: part(part)
, position(position)
, point (part->getPoint(position - 1))
, point1(part->getPoint(position - 1))
, point2(part->getPoint(position + 1))
{
if (point1.other.segmentAfter == SEGMENT_CURVE || point2.other.segmentBefore == SEGMENT_CURVE) {
// try to preserve curve
Vector2D before = point->deltaBefore;
Vector2D after = point->deltaAfter;
// convert both segments to curves first
if (point1.other.segmentAfter != SEGMENT_CURVE) {
point1.other.deltaAfter = -
before = (point1.other.pos - point->pos) / 3.0;
point1.other.segmentAfter = SEGMENT_CURVE;
}
if (point2.other.segmentBefore != SEGMENT_CURVE) {
point2.other.deltaBefore = -
after = (point2.other.pos - point->pos) / 3.0;
point2.other.segmentBefore = SEGMENT_CURVE;
}
// The inverse of adding a point, reconstruct the original handles
// before being subdivided using de Casteljau's algorithm
// length of handles
double bl = before.length() + 0.00000001; // prevent division by 0
double al = after .length() + 0.00000001;
double totl = bl + al;
// set new handle sizes
point1.other.deltaAfter *= totl / bl;
point2.other.deltaBefore *= totl / al;
// Also take in acount cases where the point does not correspond to a freshly added point.
// distance from the point to the curve as it would be in the above case can be used,
// in the case of a point just added this distance = 0
BezierCurve c(point1.other, point2.other);
double t = bl / totl;
Vector2D p = c.pointAt(t);
Vector2D distP = point->pos - p;
// adjust handle sizes
point1.other.deltaAfter *= ssqrt(distP.dot(point1.other.deltaAfter) /point1.other.deltaAfter.lengthSqr()) + 1;
point2.other.deltaBefore *= ssqrt(distP.dot(point2.other.deltaBefore)/point2.other.deltaBefore.lengthSqr()) + 1;
// unlock if needed
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
if (point2.other.lock == LOCK_SIZE) point2.other.lock = LOCK_DIR;
} else {
// just lines, keep it that way
}
}
void SinglePointRemoveAction::perform(bool toUndo) {
if (toUndo) {
// reinsert the point
part->points.insert(part->points.begin() + position, point);
} else {
// remove the point
part->points.erase( part->points.begin() + position);
}
// update points around removed point
point1.perform();
point2.perform();
}
DECLARE_POINTER_TYPE(SinglePointRemoveAction);
DECLARE_TYPEOF_COLLECTION(SinglePointRemoveActionP);
/// Remove a set of points from a symbol part
/// Internally represented as a list of Single Point Remove Actions
/// Not all points mat be removed, at least two points must remain
class ControlPointRemoveAction : public Action {
public:
ControlPointRemoveAction(const SymbolPartP& part, const set<ControlPointP>& toDelete);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
private:
vector<SinglePointRemoveActionP> removals;
};
ControlPointRemoveAction::ControlPointRemoveAction(const SymbolPartP& part, const set<ControlPointP>& toDelete) {
int index = 0;
// find points to remove, in reverse order
FOR_EACH(point, part->points) {
if (toDelete.find(point) != toDelete.end()) {
// remove this point
removals.push_back(new_shared2<SinglePointRemoveAction>(part, index));
}
++index;
}
}
String ControlPointRemoveAction::getName(bool toUndo) const {
return removals.size() == 1 ? _("Delete point") : _("Delete points");
}
void ControlPointRemoveAction::perform(bool toUndo) {
if (toUndo) {
FOR_EACH(r, removals) r->perform(toUndo);
} else {
// in reverse order, because positions of later points will
// change after removal of earlier points.
FOR_EACH_REVERSE(r, removals) r->perform(toUndo);
}
}
Action* controlPointRemoveAction(const SymbolPartP& part, const set<ControlPointP>& toDelete) {
if (part->points.size() - toDelete.size() < 2) {
// TODO : remove part?
//new_shared<ControlPointRemoveAllAction>(part);
return 0; // no action
} else {
return new ControlPointRemoveAction(part, toDelete);
}
}
+158
View File
@@ -0,0 +1,158 @@
//+----------------------------------------------------------------------------+
//| 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
+183
View File
@@ -0,0 +1,183 @@
//+----------------------------------------------------------------------------+
//| 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();
}
+192
View File
@@ -0,0 +1,192 @@
//+----------------------------------------------------------------------------+
//| 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
+252
View File
@@ -0,0 +1,252 @@
//+----------------------------------------------------------------------------+
//| 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
}
}
+130
View File
@@ -0,0 +1,130 @@
//+----------------------------------------------------------------------------+
//| 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
+139
View File
@@ -0,0 +1,139 @@
//+----------------------------------------------------------------------------+
//| 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) {
}
+109
View File
@@ -0,0 +1,109 @@
//+----------------------------------------------------------------------------+
//| 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
+87
View File
@@ -0,0 +1,87 @@
//+----------------------------------------------------------------------------+
//| 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;
}
+43
View File
@@ -0,0 +1,43 @@
//+----------------------------------------------------------------------------+
//| 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
+96
View File
@@ -0,0 +1,96 @@
//+----------------------------------------------------------------------------+
//| 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;
}
}
+284
View File
@@ -0,0 +1,284 @@
//+----------------------------------------------------------------------------+
//| 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/basic_shape_editor.hpp>
#include <util/window_id.hpp>
#include <data/action/symbol.hpp>
#include <wx/spinctrl.h>
// ----------------------------------------------------------------------------- : SymbolBasicShapeEditor
SymbolBasicShapeEditor::SymbolBasicShapeEditor(SymbolControl* control)
: SymbolEditorBase(control)
, drawing(false)
, mode(ID_SHAPE_CIRCLE)
{
control->SetCursor(*wxCROSS_CURSOR);
}
// ----------------------------------------------------------------------------- : Drawing
void SymbolBasicShapeEditor::draw(DC& dc) {
// highlight the part we are drawing
if (drawing) {
control.highlightPart(dc, *shape, HIGHLIGHT_BORDER);
}
}
// ----------------------------------------------------------------------------- : UI
void SymbolBasicShapeEditor::initUI(wxToolBar* tb, wxMenuBar* mb) {
sides = new wxSpinCtrl( tb, ID_SIDES, _("3"), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 3, 50, 3);
sidesL = new wxStaticText(tb, ID_SIDES, _(" sides: "));
sides->SetSize(50, -1);
tb->AddSeparator();
tb->AddTool(ID_SHAPE_CIRCLE, _("Ellipse"), Bitmap(_("TOOL_CIRCLE")), wxNullBitmap, wxITEM_CHECK, _("Circle / Ellipse"), _("Draw circles and ellipses"));
tb->AddTool(ID_SHAPE_RECTANGLE, _("Rectangle"), Bitmap(_("TOOL_RECTANGLE")), wxNullBitmap, wxITEM_CHECK, _("Square / Rectangle"), _("Draw squares and rectangles"));
tb->AddTool(ID_SHAPE_POLYGON, _("Polygon"), Bitmap(_("TOOL_TRIANGLE")), wxNullBitmap, wxITEM_CHECK, _("Polygon"), _("Draw triangles, pentagons and other regular polygons"));
tb->AddTool(ID_SHAPE_STAR, _("Star"), Bitmap(_("TOOL_STAR")), wxNullBitmap, wxITEM_CHECK, _("Star"), _("Draw stars"));
tb->AddControl(sidesL);
tb->AddControl(sides);
tb->Realize();
control.SetCursor(*wxCROSS_CURSOR);
}
void SymbolBasicShapeEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
tb->DeleteTool(ID_SHAPE_CIRCLE);
tb->DeleteTool(ID_SHAPE_RECTANGLE);
tb->DeleteTool(ID_SHAPE_POLYGON);
tb->DeleteTool(ID_SHAPE_STAR);
tb->RemoveChild(sidesL);
tb->RemoveChild(sides);
// HACK: hardcoded size of rest of toolbar
tb->DeleteToolByPos(4); // delete separator
tb->DeleteToolByPos(4); // delete sidesL
tb->DeleteToolByPos(4); // delete sides
#if wxVERSION_NUMBER < 2600
delete sides;
delete sidesL;
#endif
}
void SymbolBasicShapeEditor::onUpdateUI(wxUpdateUIEvent& ev) {
if (ev.GetId() >= ID_SHAPE && ev.GetId() < ID_SHAPE_MAX) {
ev.Check(ev.GetId() == mode);
} else if (ev.GetId() == ID_SIDES) {
ev.Enable(mode == ID_SHAPE_POLYGON || mode == ID_SHAPE_STAR);
} else {
ev.Enable(false); // we don't know about this item
}
}
void SymbolBasicShapeEditor::onCommand(int id) {
if (id >= ID_SHAPE && id < ID_SHAPE_MAX) {
// change shape mode
mode = id;
}
}
int SymbolBasicShapeEditor::modeToolId() { return ID_MODE_SHAPES; }
// ----------------------------------------------------------------------------- : Mouse events
void SymbolBasicShapeEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {
// Start drawing
drawing = true;
start = end = pos;
SetStatusText(_("Drag to resize shape, Ctrl constrains shape, Shift centers shape"));
}
void SymbolBasicShapeEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {
if (drawing && shape) {
// Finalize the shape
getSymbol()->actions.add(new AddSymbolPartAction(*getSymbol(), shape));
// Select the part
control.selectPart(shape);
// no need to clean up, this editor is replaced
// // Clean up
// stopActions()
}
}
void SymbolBasicShapeEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
// Resize the object
if (drawing) {
end = to;
makeShape(start, end, ev.ControlDown(), ev.ShiftDown());
control.Refresh(false);
}
}
// ----------------------------------------------------------------------------- : Other events
void SymbolBasicShapeEditor::onKeyChange(wxKeyEvent& ev) {
if (drawing) {
if (ev.GetKeyCode() == WXK_CONTROL || ev.GetKeyCode() == WXK_SHIFT) {
// changed constrains
makeShape(start, end, ev.ControlDown(), ev.ShiftDown());
control.Refresh(false);
} else if (ev.GetKeyCode() == WXK_ESCAPE) {
// cancel drawing
stopActions();
}
}
}
bool SymbolBasicShapeEditor::isEditing() { return drawing; }
// ----------------------------------------------------------------------------- : Generating shapes
void SymbolBasicShapeEditor::stopActions() {
shape = SymbolPartP();
drawing = false;
switch (mode) {
case ID_SHAPE_CIRCLE:
SetStatusText(_("Click and drag to draw a ellipse, hold Ctrl for a circle"));
break;
case ID_SHAPE_RECTANGLE:
SetStatusText(_("Click and drag to draw a rectangle, hold Ctrl for a square"));
break;
case ID_SHAPE_POLYGON:
SetStatusText(_("Click and drag to draw a polygon"));
break;
case ID_SHAPE_STAR:
SetStatusText(_("Click and drag to draw a star"));
break;
}
control.Refresh(false);
}
inline double sgn(double d) {
return d < 0 ? - 1 : 1;
}
void SymbolBasicShapeEditor::makeShape(const Vector2D& a, const Vector2D& b, bool constrained, bool centered) {
// constrain
Vector2D size = b - a;
if (constrained) {
if (abs(size.x) > abs(size.y)) {
size.y = sgn(size.y) * abs(size.x);
} else {
size.x = sgn(size.x) * abs(size.y);
}
}
// make shape
if (centered) {
makeCenteredShape(a, size, constrained);
} else {
makeCenteredShape(a + size / 2, size / 2, constrained);
}
}
// TODO : Move out of this class
void SymbolBasicShapeEditor::makeCenteredShape(const Vector2D& c, Vector2D r, bool constrained) {
shape = new_shared<SymbolPart>();
// What shape to make?
switch (mode) {
case ID_SHAPE_CIRCLE: {
// A circle / ellipse
if (constrained) {
shape->name = _("Circle");
} else {
shape->name = _("Ellipse");
}
// a circle has 4 control points, the first is: (x+r, y) db(0, kr) da(0, -kr)
// kr is a magic constant
const double kr = 0.5522847498f; // = 4/3 * (sqrt(2) - 1)
shape->points.push_back(new_shared7<ControlPoint>(c.x + r.x, c.y, 0, kr * r.y, 0, -kr * r.y, LOCK_SIZE));
shape->points.push_back(new_shared7<ControlPoint>(c.x, c.y - r.y, kr * r.x, 0, -kr * r.x, 0, LOCK_SIZE));
shape->points.push_back(new_shared7<ControlPoint>(c.x - r.x, c.y, 0, -kr * r.y, 0, kr * r.y, LOCK_SIZE));
shape->points.push_back(new_shared7<ControlPoint>(c.x, c.y + r.y, -kr * r.x, 0, kr * r.x, 0, LOCK_SIZE));
break;
} case ID_SHAPE_RECTANGLE: {
// A rectangle / square
if (constrained) {
shape->name = _("Square");
} else {
shape->name = _("Rectangle");
}
// a rectangle just has four corners
shape->points.push_back(new_shared2<ControlPoint>(c.x - r.x, c.y - r.y));
shape->points.push_back(new_shared2<ControlPoint>(c.x + r.x, c.y - r.y));
shape->points.push_back(new_shared2<ControlPoint>(c.x + r.x, c.y + r.y));
shape->points.push_back(new_shared2<ControlPoint>(c.x - r.x, c.y + r.y));
break;
} default: {
// A polygon or star
int n = sides->GetValue(); // number of sides
switch (n) {
case 3: shape->name = _("Triangle");
case 4: shape->name = _("Rhombus");
case 5: shape->name = _("Pentagon");
case 6: shape->name = _("Hexagon");
default: shape->name = _("Polygon");
}
// Example: n == 7
// a a..g = corners
// g b O = center
// f O c ra = radius, |Oa|
// e d
double alpha = 2 * M_PI / n; // internal angle /_aOb
// angle between point touching side and point on top
// floor((n+1)/4) == number of sides between these two points
// beta = /_aOc
double beta = alpha * ((n+1)/4);
// define:
// width = 2 = |fc|
// lb = |ac|
// gamma = (pi - beta) / 2
// equations:
// lb * sin(gamma) == 1 (right angled tri /_\ aXc where X is halfway fc)
// lb / sin(beta) == ra / sin(gamma) (law of sines in /_\ abc)
// solving leads to:
// sin(gamma) == cos(beta/2)
double lb = 1 / cos(beta/2);
double ra = lb / sin(beta) * cos(beta/2);
// now we know the center of the polygon:
double y = c.y + (ra - 1) * r.y;
if (mode == ID_SHAPE_POLYGON) {
// we can generate points
for(int i = 0 ; i < n ; ++i) {
double theta = alpha * i;
shape->points.push_back(new_shared2<ControlPoint>(
c.x + ra * r.x * sin(theta),
y - ra * r.y * cos(theta)
));
}
} else {
// a star is made using a smaller, inverted polygon at the inside
// points are interleaved
// rb = radius of smaller polygon
// lc = length of a side
double lc = ra * sin(alpha) / cos(alpha/2);
// ld = length of side skipping one corner
double delta = alpha * 2;
double ld = ra * sin(delta) / cos(delta/2);
// Using symmetry: /_\gab ~ /_\axb where x is intersection
// gives ratio lc/ld
// converting back to radius using ra/lb = cos(beta/2) / sin(beta)
// NOTE: This is only correct for n<=6, but gives acceptable results for higher n
double rb = (ld - 2 * lc * (lc/ld)) * ra / lb;
for(int i = 0 ; i < n ; ++i) {
double theta = alpha * i;
// from a
shape->points.push_back(new_shared2<ControlPoint>(
c.x + ra * r.x * sin(theta),
y - ra * r.y * cos(theta)
));
// from b
theta = alpha * (i + 0.5);
shape->points.push_back(new_shared2<ControlPoint>(
c.x + rb * r.x * sin(theta),
y - rb * r.y * cos(theta)
));
}
}
break;
}
}
}
+73
View File
@@ -0,0 +1,73 @@
//+----------------------------------------------------------------------------+
//| 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
+236
View File
@@ -0,0 +1,236 @@
//+----------------------------------------------------------------------------+
//| 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 ()
+103
View File
@@ -0,0 +1,103 @@
//+----------------------------------------------------------------------------+
//| 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
+16
View File
@@ -0,0 +1,16 @@
//+----------------------------------------------------------------------------+
//| 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);
}
+91
View File
@@ -0,0 +1,91 @@
//+----------------------------------------------------------------------------+
//| 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
+155
View File
@@ -0,0 +1,155 @@
//+----------------------------------------------------------------------------+
//| 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 ()
+72
View File
@@ -0,0 +1,72 @@
//+----------------------------------------------------------------------------+
//| 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
+529
View File
@@ -0,0 +1,529 @@
//+----------------------------------------------------------------------------+
//| 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/point_editor.hpp>
#include <gui/symbol/window.hpp>
#include <gfx/bezier.hpp>
#include <data/action/symbol_part.hpp>
#include <util/window_id.hpp>
#include <util/error.hpp>
// ----------------------------------------------------------------------------- : SymbolPointEditor
SymbolPointEditor::SymbolPointEditor(SymbolControl* control, const SymbolPartP& part)
: SymbolEditorBase(control)
, part(part)
, selection(SELECTED_NONE)
, hovering(SELECTED_NONE)
// Load gui stock
, pointSelect(_("CUR_POINT"), wxBITMAP_TYPE_CUR_RESOURCE)
, pointAdd (_("CUR_POINT_ADD"), wxBITMAP_TYPE_CUR_RESOURCE)
, pointCurve (_("CUR_POINT_CURVE"),wxBITMAP_TYPE_CUR_RESOURCE)
, pointMove (_("CUR_POINT_MOVE"), wxBITMAP_TYPE_CUR_RESOURCE)
{
resetActions();
// // fix pen joins
// penHandleHover.join = wxJOIN_MITER;
// penMainHover.join = wxJOIN_MITER;
}
// ----------------------------------------------------------------------------- : Drawing
void SymbolPointEditor::draw(DC& dc) {
// highlight the part
control.highlightPart(dc, *part, HIGHLIGHT_BORDER);
// handles etc.
if (hovering == SELECTED_LINE) {
drawHoveredLine(dc);
}
drawHandles(dc);
if (hovering == SELECTED_NEW_POINT) {
drawNewPoint(dc);
}
}
void SymbolPointEditor::drawHoveredLine(DC& dc) {
BezierCurve c(*hoverLine1, *hoverLine2);
wxPoint prevPoint = control.rotation.tr(hoverLine1->pos);
for(int i = 1 ; i <= 100 ; ++i) {
// Draw 100 segments of the curve
double t = double(i)/100.0f;
wxPoint curPoint = control.rotation.tr(c.pointAt(t));
double selectPercent = 1.0 - 1.2 * sqrt(fabs(hoverLineT-t)); // amount to color
if (selectPercent > 0) {
// gradient color
Color color(
col(300 - 300 * selectPercent),
col(300 * selectPercent),
col(0)
);
dc.SetPen(wxPen(color, 3));
dc.DrawLine(prevPoint, curPoint);
}
prevPoint = curPoint;
}
}
void SymbolPointEditor::drawHandles(DC& dc) {
dc.SetPen(Color(0,0,128));
dc.SetBrush(Color(128,128,255));
for (int i = 0 ; (size_t)i < part->points.size() ; ++i) {
// determine which handles to draw
bool selected = pointSelected(*part->getPoint(i));
bool selBefore = selected || pointSelected(*part->getPoint(i-1));
bool selAfter = selected || pointSelected(*part->getPoint(i+1));
// and draw them
drawControlPoint(dc, *part->getPoint(i), selBefore, selAfter);
}
}
void SymbolPointEditor::drawNewPoint(DC& dc) {
dc.SetPen(*wxGREEN_PEN);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
wxPoint p = control.rotation.tr(newPoint);
drawHandleBox(dc, p.x, p.y, true);
}
void SymbolPointEditor::drawControlPoint(DC& dc, const ControlPoint& p, bool drawHandleBefore, bool drawHandleAfter) {
// Position
wxPoint p0 = control.rotation.tr(p.pos);
// Sub handles
if (drawHandleBefore || drawHandleAfter) {
dc.SetBrush(*wxTRANSPARENT_BRUSH);
// Before handle
if (drawHandleBefore && p.segmentBefore == SEGMENT_CURVE) {
wxPoint p1 = control.rotation.tr(p.pos + p.deltaBefore);
dc.SetPen(handlePen(PEN_LINE, p.lock));
dc.DrawLine(p0.x, p0.y, p1.x, p1.y);
dc.SetPen(handlePen(handleHovered(p, HANDLE_BEFORE) ? PEN_HOVER : PEN_NORMAL, p.lock));
drawHandleCircle(dc, p1.x, p1.y);
}
// After handle
if (drawHandleAfter && p.segmentAfter == SEGMENT_CURVE) {
wxPoint p1 = control.rotation.tr(p.pos + p.deltaAfter);
dc.SetPen(handlePen(PEN_LINE, p.lock));
dc.DrawLine(p0.x, p0.y, p1.x, p1.y);
dc.SetPen(handlePen(handleHovered(p, HANDLE_AFTER) ? PEN_HOVER : PEN_NORMAL, p.lock));
drawHandleCircle(dc, p1.x, p1.y);
}
}
// Main handle
// last, so it draws over lines to handles
bool selected = pointSelected(p);
wxPen hp(*wxBLACK, pointHovered(p) ? 2 : 1);
hp.SetJoin(wxJOIN_MITER);
dc.SetPen(hp);
dc.SetBrush(selected ? *wxGREEN_BRUSH : *wxTRANSPARENT_BRUSH);
drawHandleBox(dc, p0.x, p0.y, selected);
}
void SymbolPointEditor::drawHandleBox(DC& dc, UInt px, UInt py, bool active) {
dc.DrawRectangle(px - 3, py - 3, 7, 7);
if (!active) {
dc.SetPen(*wxWHITE_PEN);
dc.DrawRectangle(px - 2, py - 2, 5, 5);
}
}
void SymbolPointEditor::drawHandleCircle(DC& dc, UInt px, UInt py) {
dc.DrawCircle(px, py, 4);
}
wxPen SymbolPointEditor::handlePen(WhichPen p, LockMode lock) {
Color col;
if (lock == LOCK_FREE) col = Color(100, 100, 255);
if (lock == LOCK_DIR) col = Color(153, 0, 204);
if (lock == LOCK_SIZE) col = Color(204, 50, 50);
switch(p) {
case PEN_NORMAL: return wxPen(col);
case PEN_HOVER: return wxPen(col, 2);
case PEN_LINE: return wxPen(col, 1, wxDOT);
default: throw InternalError(_("SymbolPointEditor::handlePen"));
}
}
// ----------------------------------------------------------------------------- : UI
void SymbolPointEditor::initUI(wxToolBar* tb, wxMenuBar* mb) {
// Initialize toolbar
tb->AddSeparator();
tb->AddTool(ID_SEGMENT_LINE, _("Line"), Bitmap(_("TOOL_LINE")), wxNullBitmap, wxITEM_CHECK, _("To straigt line"), _("Makes the selected line straight"));
tb->AddTool(ID_SEGMENT_CURVE, _("Curve"), Bitmap(_("TOOL_CURVE")), wxNullBitmap, wxITEM_CHECK, _("To curve"), _("Makes the selected line curved"));
tb->AddSeparator();
tb->AddTool(ID_LOCK_FREE, _("Free"), Bitmap(_("TOOL_LOCK_FREE")), wxNullBitmap, wxITEM_CHECK, _("Unlock node"), _("Allows the two control points on the node to be moved freely"));
tb->AddTool(ID_LOCK_DIR, _("Smooth"), Bitmap(_("TOOL_LOCK_DIR")), wxNullBitmap, wxITEM_CHECK, _("Make node smooth"), _("Makes the selected node smooth by placing the two control points opposite each other"));
tb->AddTool(ID_LOCK_SIZE, _("Symmetric"), Bitmap(_("TOOL_LOCK_SIZE")), wxNullBitmap, wxITEM_CHECK, _("Make node symmetric"), _("Makes the selected node symetric"));
tb->Realize();
// TODO : menu bar
//mb->Insert(2, curveMenu, _("&Curve"))
}
void SymbolPointEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
tb->DeleteTool(ID_SEGMENT_LINE);
tb->DeleteTool(ID_SEGMENT_CURVE);
tb->DeleteTool(ID_LOCK_FREE);
tb->DeleteTool(ID_LOCK_DIR);
tb->DeleteTool(ID_LOCK_SIZE);
// HACK: hardcoded size of rest of toolbar
tb->DeleteToolByPos(4); // delete separator
tb->DeleteToolByPos(4); // delete separator
// TODO : menu bar
//mb->Remove(2)
}
void SymbolPointEditor::onUpdateUI(wxUpdateUIEvent& ev) {
// enable
bool enabled = false, checked = false;
switch (ev.GetId()) {
case ID_SEGMENT_LINE: case ID_SEGMENT_CURVE:
enabled = selection == SELECTED_LINE;
break;
case ID_LOCK_FREE: case ID_LOCK_DIR: case ID_LOCK_SIZE:
enabled = selection == SELECTED_POINTS &&
selectedPoints.size() == 1 &&
(*selectedPoints.begin())->segmentBefore == SEGMENT_CURVE &&
(*selectedPoints.begin())->segmentAfter == SEGMENT_CURVE;
break;
default:
ev.Enable(false); // we don't know this item
return;
}
// check
if (enabled) {
switch (ev.GetId()) {
case ID_SEGMENT_LINE: case ID_SEGMENT_CURVE:
checked = selectedLine1->segmentAfter == ev.GetId() - ID_SEGMENT;
break;
case ID_LOCK_FREE: case ID_LOCK_DIR: case ID_LOCK_SIZE:
checked = (*selectedPoints.begin())->lock == ev.GetId() - ID_LOCK;
break;
}
}
ev.Enable(enabled);
ev.Check(checked);
}
void SymbolPointEditor::onCommand(int id) {
switch (id) {
case ID_SEGMENT_LINE: case ID_SEGMENT_CURVE:
onChangeSegment( static_cast<SegmentMode>(id - ID_SEGMENT) );
case ID_LOCK_FREE: case ID_LOCK_DIR: case ID_LOCK_SIZE:
onChangeLock( static_cast<LockMode>(id - ID_LOCK) );
}
}
int SymbolPointEditor::modeToolId() { return ID_MODE_POINTS; }
// ----------------------------------------------------------------------------- : Mouse events
void SymbolPointEditor::onLeftDown(const Vector2D& pos, wxMouseEvent& ev) {
SelectedHandle handle = findHandle(pos);
if (handle.handle) {
selectHandle(handle, ev);
} else if (hovering == SELECTED_LINE) {
selectLine(ev);
} else if (hovering == SELECTED_NEW_POINT) {
selectLine(ev);
} else {
selectNothing();
}
// update window
control.Refresh(false);
}
void SymbolPointEditor::onLeftUp(const Vector2D& pos, wxMouseEvent& ev) {
// Left up => finalize all actions, new events start new actions
resetActions();
}
void SymbolPointEditor::onLeftDClick(const Vector2D& pos, wxMouseEvent& ev) {
findHoveredItem(pos, false);
if (hovering == SELECTED_NEW_POINT) {
// Add point
ControlPointAddAction* act = new ControlPointAddAction(part, hoverLine1Idx, hoverLineT);
getSymbol()->actions.add(act);
// select the new point
selectPoint(act->getNewPoint(), false);
selection = SELECTED_POINTS;
} else if (hovering == SELECTED_HANDLE && hoverHandle.handle == HANDLE_MAIN) { //%%%%%%% ||/&&
// Delete point
selectedPoints.clear();
selectPoint(hoverHandle.point, false);
getSymbol()->actions.add(controlPointRemoveAction(part, selectedPoints));
selectedPoints.clear();
selection = SELECTED_NONE;
}
// refresh
findHoveredItem(pos, false);
control.Refresh(false);
}
void SymbolPointEditor::onMouseMove(const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
// Moving the mouse without dragging => select a point/handle
findHoveredItem(to, ev.AltDown());
control.Refresh(false);
}
void SymbolPointEditor::onMouseDrag(const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
Vector2D delta = to - from;
if (selection == SELECTED_LINE && ev.AltDown()) {
// Drag the curve
if (controlPointMoveAction) controlPointMoveAction = 0;
if (!curveDragAction) {
curveDragAction = new CurveDragAction(selectedLine1, selectedLine2);
getSymbol()->actions.add(curveDragAction);
}
curveDragAction->move(delta, selectedLineT);
control.Refresh(false);
} else if (selection == SELECTED_POINTS || selection == SELECTED_LINE) {
// Move all selected points
if (curveDragAction) curveDragAction = 0;
if (!controlPointMoveAction) {
// create action we can add this movement to
controlPointMoveAction = new ControlPointMoveAction(selectedPoints);
getSymbol()->actions.add(controlPointMoveAction);
}
controlPointMoveAction->constrain = ev.ControlDown(); // ctrl constrains
controlPointMoveAction->move(delta);
newPoint += delta;
control.Refresh(false);
} else if (selection == SELECTED_HANDLE) {
// Move the selected handle
if (!handleMoveAction) {
handleMoveAction = new HandleMoveAction(selectedHandle);
getSymbol()->actions.add(handleMoveAction);
}
handleMoveAction->constrain = ev.ControlDown(); // ctrl constrains
handleMoveAction->move(delta);
control.Refresh(false);
}
}
// ----------------------------------------------------------------------------- : Other events
void SymbolPointEditor::onKeyChange(wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_ALT && (hovering == SELECTED_LINE || hovering == SELECTED_NEW_POINT)) {
if (ev.AltDown()) {
hovering = SELECTED_LINE;
control.SetCursor(pointCurve);
SetStatusText(_("Drag to move curve"));
} else {
hovering = SELECTED_NEW_POINT;
control.SetCursor(pointAdd);
SetStatusText(_("Alt + drag to move curve; double click to add control point on this line"));
}
control.Refresh(false);
} else if (ev.GetKeyCode() == WXK_CONTROL) {
// constrain changed
if (controlPointMoveAction) {
controlPointMoveAction->constrain = ev.ControlDown();
controlPointMoveAction->move(Vector2D()); //refresh action
control.Refresh(false);
} else if (handleMoveAction) {
handleMoveAction->constrain = ev.ControlDown();
handleMoveAction->move(Vector2D()); //refresh action
control.Refresh(false);
}
}
}
void SymbolPointEditor::onChar(wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_DELETE) {
deleteSelection();
} else {
ev.Skip();
}
}
bool SymbolPointEditor::isEditing() {
return handleMoveAction || controlPointMoveAction || curveDragAction;
}
// ----------------------------------------------------------------------------- : Selection
void SymbolPointEditor::selectNothing() {
selection = SELECTED_NONE;
selectedPoints.clear();
}
void SymbolPointEditor::selectPoint(const ControlPointP& point, bool toggle) {
set<ControlPointP>::iterator inSet = selectedPoints.find(point);
if (toggle) {
if (inSet == selectedPoints.end()) {
selectedPoints.insert(point);
} else {
selectedPoints.erase(inSet);
}
} else {
if (inSet == selectedPoints.end()) {
selectedPoints.clear();
selectedPoints.insert(point);
}
}
}
void SymbolPointEditor::selectHandle(const SelectedHandle& h, const wxMouseEvent& keystate) {
if (h.handle == HANDLE_MAIN) {
selection = SELECTED_POINTS;
selectPoint(h.point, keystate.ShiftDown());
} else {
selection = SELECTED_HANDLE;
selectedHandle = h;
}
}
void SymbolPointEditor::selectLine(const wxMouseEvent& keystate) {
selection = SELECTED_LINE;
selectedLine1 = hoverLine1;
selectedLine2 = hoverLine2;
selectedLineT = hoverLineT;
if (!keystate.ShiftDown()) selectedPoints.clear();
selectPoint(selectedLine1, true);
selectPoint(selectedLine2, true);
}
bool SymbolPointEditor::pointSelected(const ControlPointP& pnt) {
return selectedPoints.find(pnt) != selectedPoints.end();
}
bool SymbolPointEditor::pointSelected(const ControlPoint& pnt) {
FOR_EACH(s, selectedPoints) {
if (s.get() == &pnt) return true;
}
return false;
}
bool SymbolPointEditor::pointHovered(const ControlPointP& pnt) {
return handleHovered(pnt, HANDLE_MAIN);
}
bool SymbolPointEditor::pointHovered(const ControlPoint& pnt) {
return handleHovered(pnt, HANDLE_MAIN);
}
bool SymbolPointEditor::handleHovered(const ControlPointP& pnt, WhichHandle wh) {
return hovering == SELECTED_HANDLE && hoverHandle.point == pnt && hoverHandle.handle == wh;
}
bool SymbolPointEditor::handleHovered(const ControlPoint& pnt, WhichHandle wh) {
return hovering == SELECTED_HANDLE && hoverHandle.point && hoverHandle.point.get() == &pnt && hoverHandle.handle == wh;
}
// ----------------------------------------------------------------------------- : Actions
void SymbolPointEditor::resetActions() {
handleMoveAction = nullptr;
controlPointMoveAction = nullptr;
curveDragAction = nullptr;
}
void SymbolPointEditor::deleteSelection() {
if (!selectedPoints.empty()) {
getSymbol()->actions.add(controlPointRemoveAction(part, selectedPoints));
selectedPoints.clear();
resetActions();
control.Refresh(false);
}
}
void SymbolPointEditor::onChangeSegment(SegmentMode mode) {
assert(selectedLine1);
assert(selectedLine2);
if (selectedLine1->segmentAfter == mode) return;
getSymbol()->actions.add(new SegmentModeAction(selectedLine1, selectedLine2, mode));
control.Refresh(false);
}
void SymbolPointEditor::onChangeLock(LockMode mode) {
getSymbol()->actions.add(new LockModeAction(*selectedPoints.begin(), mode));
control.Refresh(false);
}
// ----------------------------------------------------------------------------- : Finding items
void SymbolPointEditor::findHoveredItem(const Vector2D& pos, bool altDown) {
// is there a point currently under the cursor?
hoverHandle = findHandle(pos);
// change cursor and statusbar if point is under it
if (hoverHandle.handle) {
hovering = SELECTED_HANDLE;
control.SetCursor(pointMove);
SetStatusText(_("Click and drag to move control point"));
} else {
// Not on a point or handle, maybe the cursor is on a curve
if (checkPosOnCurve(pos)) {
if (altDown) {
hovering = SELECTED_LINE;
control.SetCursor(pointCurve);
SetStatusText(_("Drag to move curve"));
} else {
hovering = SELECTED_NEW_POINT;
control.SetCursor(pointAdd);
SetStatusText(_("Alt + drag to move curve; double click to add control point on this line"));
}
} else {
hovering = SELECTED_NONE;
control.SetCursor(*wxSTANDARD_CURSOR);
SetStatusText(_(""));
}
}
}
bool SymbolPointEditor::checkPosOnCurve(const Vector2D& pos) {
double range = control.rotation.trInvS(3); // less then 3 pixels away is still a hit
size_t size = part->points.size();
for(int i = 0 ; (size_t)i < size ; ++i) {
// Curve between these lines
hoverLine1 = part->getPoint(i);
hoverLine2 = part->getPoint(i + 1);
if (posOnSegment(pos, range, *hoverLine1, *hoverLine2, newPoint, hoverLineT)) {
// mouse is on this line
hoverLine1Idx = i;
return true;
}
}
return false;
}
SelectedHandle SymbolPointEditor::findHandle(const Vector2D& pos) {
double range = control.rotation.trInvS(3); // less then 3 pixels away is still a hit
// Is there a main handle there?
FOR_EACH(p, part->points) {
if (inRange(p->pos, pos, range)) {
// point is at pos
return SelectedHandle(p, HANDLE_MAIN);
}
}
// Is there a sub handle there?
// only check visible handles
for (int i = 0 ; (size_t)i < part->points.size() ; ++i) {
ControlPointP p = part->getPoint(i);
bool sel = pointSelected(p);
bool before = sel || pointSelected(part->getPoint(i-1)); // are the handles visible?
bool after = sel || pointSelected(part->getPoint(i+1));
if (before && p->segmentBefore == SEGMENT_CURVE) {
if (inRange(p->pos + p->deltaBefore, pos, range)) {
return SelectedHandle(p, HANDLE_BEFORE);
}
}
if (after && p->segmentAfter == SEGMENT_CURVE) {
if (inRange(p->pos + p->deltaAfter, pos, range)) {
return SelectedHandle(p, HANDLE_AFTER);
}
}
}
// Nothing found
return HANDLE_NONE;
}
bool SymbolPointEditor::inRange(const Vector2D& a, const Vector2D& b, double range) {
return abs(a.x - b.x) <= range &&
abs(a.y - b.y) <= range;
}
+169
View File
@@ -0,0 +1,169 @@
//+----------------------------------------------------------------------------+
//| 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
+424
View File
@@ -0,0 +1,424 @@
//+----------------------------------------------------------------------------+
//| 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/select_editor.hpp>
#include <gui/symbol/window.hpp>
#include <gui/util.hpp>
#include <util/window_id.hpp>
#include <data/action/symbol.hpp>
#include <gfx/gfx.hpp>
// ----------------------------------------------------------------------------- : SymbolSelectEditor
SymbolSelectEditor::SymbolSelectEditor(SymbolControl* control, bool rotate)
: SymbolEditorBase(control)
, rotate(rotate)
, cursorRotate(_("CUR_ROTATE"), wxBITMAP_TYPE_CUR_RESOURCE)
, cursorShearX(_("CUR_SHEAR_X"), wxBITMAP_TYPE_CUR_RESOURCE)
, cursorShearY(_("CUR_SHEAR_Y"), wxBITMAP_TYPE_CUR_RESOURCE)
{
// Load resource images
Image rot = loadResourceImage(_("HANDLE_ROTATE"));
handleRotateTL = wxBitmap(rot);
handleRotateTR = wxBitmap(rotateImageBy(rot,90));
handleRotateBR = wxBitmap(rotateImageBy(rot,180));
handleRotateBL = wxBitmap(rotateImageBy(rot,270));
Image shear = loadResourceImage(_("HANDLE_SHEAR_X"));
handleShearX = wxBitmap(shear);
handleShearY = wxBitmap(rotateImageBy(shear,90));
handleCenter = wxBitmap(loadResourceImage(_("HANDLE_CENTER")));
// Make sure all parts have updated bounds
FOR_EACH(p, getSymbol()->parts) {
p->calculateBounds();
}
resetActions();
}
// ----------------------------------------------------------------------------- : Drawing
void SymbolSelectEditor::draw(DC& dc) {
// highlight selected parts
FOR_EACH(p, control.selectedParts) {
control.highlightPart(dc, *p, HIGHLIGHT_INTERIOR);
}
// highlight the part under the cursor
if (highlightPart) {
control.highlightPart(dc, *highlightPart, HIGHLIGHT_BORDER);
}
// draw handles
drawHandles(dc);
}
void SymbolSelectEditor::drawHandles(DC& dc) {
if (control.selectedParts.empty()) return;
if (rotateAction) return; // not when rotating
updateBoundingBox();
// Draw handles on all sides
for (int dx = -1 ; dx <= 1 ; ++dx) {
for (int dy = -1 ; dy <= 1 ; ++dy) {
if (dx != 0 || dy != 0) {
// no handle in the center
drawHandle(dc, dx, dy);
}
}
}
// Draw rotation center?
if (rotate) {
drawRotationCenter(dc, center);
}
}
void SymbolSelectEditor::drawHandle(DC& dc, int dx, int dy) {
wxPoint p = control.rotation.tr(handlePos(dx, dy));
p.x += 4 * dx;
p.y += 4 * dy;
if (rotate) {
// rotate or shear handle
if (dx == 0) dc.DrawBitmap(handleShearX, p.x - 10, p.y - 3 - (dy < 0 ? 1 : 0));
if (dy == 0) dc.DrawBitmap(handleShearY, p.x - 3 - (dx < 0 ? 1 : 0), p.y - 10);
else {
// rotate
if (dx == -1 && dy == -1) dc.DrawBitmap(handleRotateTL, p.x - 5, p.y - 5);
if (dx == -1 && dy == 1) dc.DrawBitmap(handleRotateTR, p.x - 5, p.y - 11);
if (dx == 1 && dy == -1) dc.DrawBitmap(handleRotateBL, p.x - 11, p.y - 5);
if (dx == 1 && dy == 1) dc.DrawBitmap(handleRotateBR, p.x - 11, p.y - 11);
}
} else {
// resize handle
dc.SetBrush(*wxBLUE_BRUSH);
dc.SetPen( *wxWHITE_PEN);
dc.DrawRectangle(p.x - 3, p.y - 3, 6, 6);
}
}
void SymbolSelectEditor::drawRotationCenter(DC& dc, const Vector2D& pos) {
wxPoint p = control.rotation.tr(pos);
dc.DrawBitmap(handleCenter, p.x - 9, p.y - 9);
}
// ----------------------------------------------------------------------------- : UI
void SymbolSelectEditor::initUI(wxToolBar* tb, wxMenuBar* mb) {
tb->AddSeparator();
tb->AddTool(ID_PART_MERGE, _("Merge"), loadResourceImage(_("COMBINE_OR")), wxNullBitmap, wxITEM_CHECK, _("Merge with shapes below"), _("Merges this shape with those below it"));
tb->AddTool(ID_PART_SUBTRACT, _("Subtract"), loadResourceImage(_("COMBINE_SUB_DARK")), wxNullBitmap, wxITEM_CHECK, _("Subtract from shapes below"), _("Subtracts this shape from shapes below it, leaves only the area in that shape that is not in this shape"));
tb->AddTool(ID_PART_INTERSECTION, _("Intersect"), loadResourceImage(_("COMBINE_AND_DARK")), wxNullBitmap, wxITEM_CHECK, _("Intersect with shapes below"), _("Intersects this shape with shapes below it, leaves only the area in both shapes"));
// note: difference doesn't work (yet)
tb->AddTool(ID_PART_OVERLAP, _("Overlap"), loadResourceImage(_("COMBINE_OVER")), wxNullBitmap, wxITEM_CHECK, _("Place above other shapes"), _("Place this shape, and its border above shapes below it"));
tb->AddTool(ID_PART_BORDER, _("Border"), loadResourceImage(_("COMBINE_BORDER")), wxNullBitmap, wxITEM_CHECK, _("Draw as a border"), _("Draws this shape as a border"));
tb->Realize();
}
void SymbolSelectEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
tb->DeleteTool(ID_PART_MERGE);
tb->DeleteTool(ID_PART_SUBTRACT);
tb->DeleteTool(ID_PART_INTERSECTION);
tb->DeleteTool(ID_PART_OVERLAP);
tb->DeleteTool(ID_PART_BORDER);
// HACK: hardcoded size of rest of toolbar
tb->DeleteToolByPos(4); // delete separator
}
void SymbolSelectEditor::onUpdateUI(wxUpdateUIEvent& ev) {
if (ev.GetId() >= ID_PART && ev.GetId() < ID_PART_MAX) {
if (control.selectedParts.empty()) {
ev.Check(false);
ev.Enable(false);
} else {
ev.Enable(true);
bool check = true;
FOR_EACH(p, control.selectedParts) {
if (p->combine != ev.GetId() - ID_PART) {
check = false;
break;
}
}
ev.Check(check);
}
} else if (ev.GetId() == ID_EDIT_DUPLICATE) {
ev.Enable(!control.selectedParts.empty());
} else {
ev.Enable(false); // we don't know about this item
}
}
void SymbolSelectEditor::onCommand(int id) {
if (id >= ID_PART && id < ID_PART_MAX) {
// change combine mode
getSymbol()->actions.add(new CombiningModeAction(
control.selectedParts,
static_cast<SymbolPartCombine>(id - ID_PART)
));
control.Refresh(false);
} else if (id == ID_EDIT_DUPLICATE && !isEditing()) {
// duplicate selection, not when dragging
DuplicateSymbolPartsAction* action = new DuplicateSymbolPartsAction(
*getSymbol(), control.selectedParts
);
getSymbol()->actions.add(action);
control.Refresh(false);
}
}
int SymbolSelectEditor::modeToolId() {
return rotate ? ID_MODE_ROTATE : ID_MODE_SELECT;
}
// ----------------------------------------------------------------------------- : Mouse Events
void SymbolSelectEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {
}
void SymbolSelectEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {
if (isEditing()) {
// stop editing
resetActions();
} else {
// mouse not moved, change selection
// Are we on a handle?
int dx, dy;
if (onAnyHandle(pos, &dx, &dy)) return; // don't change the selection
// Select the part under the cursor
SymbolPartP part = findPart(pos);
if (part) {
if (ev.ShiftDown()) {
// toggle selection
set<SymbolPartP>::iterator it = control.selectedParts.find(part);
if (it != control.selectedParts.end()) {
control.selectedParts.erase(it);
} else {
control.selectedParts.insert(part);
}
} else {
if (control.selectedParts.find(part) != control.selectedParts.end()) {
// already selected, don't change selection
// instead switch between rotate and resize mode
rotate = !rotate;
} else {
// select the part under the cursor
control.selectedParts.clear();
control.selectedParts.insert(part);
}
}
} else if (!ev.ShiftDown()) {
// select nothing
control.selectedParts.clear();
}
// selection has changed
updateBoundingBox();
control.signalSelectionChange();
}
control.Refresh(false);
}
void SymbolSelectEditor::onLeftDClick(const Vector2D& pos, wxMouseEvent& ev) {
// start editing the points of the clicked part
highlightPart = findPart(pos);
if (highlightPart) {
control.activatePart(highlightPart);
}
}
void SymbolSelectEditor::onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& e) {
// can we highlight a part?
highlightPart = findPart(to);
// are we on a handle?
int dx, dy;
if (!control.selectedParts.empty() && onAnyHandle(to, &dx, &dy)) {
// we are on a handle, don't highlight
highlightPart = SymbolPartP();
if (rotate) {
// shear or rotating?
if (dx == 0 || dy == 0) {
SetStatusText(String(_("Drag to shear selected shape")) + (control.selectedParts.size() > 1 ? _("s") : _("")));
control.SetCursor(dx == 0 ? cursorShearX : cursorShearY);
} else {
SetStatusText(String(_("Drag to rotate selected shape")) + (control.selectedParts.size() > 1 ? _("s") : _("")) + _(", Ctrl constrains angle to multiples of 15 degrees"));
control.SetCursor(cursorRotate);
}
} else {
SetStatusText(String(_("Drag to resize selected shape")) + (control.selectedParts.size() > 1 ? _("s") : _("")) + _(", Ctrl constrains size"));
// what cursor to use?
if (dx == dy) control.SetCursor(wxCURSOR_SIZENWSE);
else if (dx == -dy) control.SetCursor(wxCURSOR_SIZENESW);
else if (dx == 0) control.SetCursor(wxCURSOR_SIZENS);
else if (dy == 0) control.SetCursor(wxCURSOR_SIZEWE);
}
} else {
if (highlightPart) {
SetStatusText(_("Click to select shape, drag to move shape, double click to edit shape"));
} else {
SetStatusText(_(""));
}
control.SetCursor(*wxSTANDARD_CURSOR);
}
control.Refresh(false);
}
void SymbolSelectEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
if (control.selectedParts.empty()) return;
if (!isEditing()) {
// we don't have an action yet, determine what to do
// note: base it on the from position, which is the position where dragging started
if (onAnyHandle(from, &scaleX, &scaleY)) {
if (rotate) {
if (scaleX == 0 || scaleY == 0) {
// shear, center/fixed point on the opposite side
shearAction = new SymbolPartShearAction(control.selectedParts, handlePos(-scaleX, -scaleY));
getSymbol()->actions.add(shearAction);
} else {
// rotate
rotateAction = new SymbolPartRotateAction(control.selectedParts, center);
getSymbol()->actions.add(rotateAction);
startAngle = angleTo(to);
}
} else {
// we are on a handle; start scaling
scaleAction = new SymbolPartScaleAction(control.selectedParts, scaleX, scaleY);
getSymbol()->actions.add(scaleAction);
}
} else {
// move
moveAction = new SymbolPartMoveAction(control.selectedParts);
getSymbol()->actions.add(moveAction);
}
}
// now update the action
if (moveAction) {
// move the selected parts
moveAction->constrain = ev.ControlDown();
moveAction->move(to - from);
} else if (scaleAction) {
// scale the selected parts
Vector2D delta = to-from;
Vector2D dMin, dMax;
if (scaleX == -1) dMin.x = delta.x;
if (scaleX == 1) dMax.x = delta.x;
if (scaleY == -1) dMin.y = delta.y;
if (scaleY == 1) dMax.y = delta.y;
scaleAction->constrain = ev.ControlDown();
scaleAction->move(dMin, dMax);
} else if (rotateAction) {
// rotate the selected parts
double angle = angleTo(to);
rotateAction->constrain = ev.ControlDown();
rotateAction->rotateTo(startAngle - angle);
} else if (shearAction) {
// shear the selected parts
Vector2D delta = to-from;
delta = delta.mul(Vector2D(scaleY, scaleX));
delta = delta.div(maxV - minV);
shearAction->constrain = ev.ControlDown();
shearAction->move(delta);
}
control.Refresh(false);
}
// ----------------------------------------------------------------------------- : Key Events
void SymbolSelectEditor::onKeyChange (wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_CONTROL) {
// changed constrains
if (moveAction) {
moveAction->constrain = ev.ControlDown();
moveAction->move(Vector2D()); // apply constrains
control.Refresh(false);
} else if (scaleAction) {
// only allow constrained scaling in diagonal direction
scaleAction->constrain = ev.ControlDown();
scaleAction->update(); // apply constrains
control.Refresh(false);
} else if (rotateAction) {
rotateAction->constrain = ev.ControlDown();
rotateAction->rotateBy(0); // apply constrains
control.Refresh(false);
}
}
}
void SymbolSelectEditor::onChar(wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_DELETE) {
// delete selected parts
getSymbol()->actions.add(new RemoveSymbolPartsAction(*getSymbol(), control.selectedParts));
control.selectedParts.clear();
resetActions();
control.Refresh(false);
} else {
ev.Skip();
}
}
bool SymbolSelectEditor::isEditing() {
return moveAction || scaleAction || rotateAction || shearAction;
}
// ----------------------------------------------------------------------------- : Other
Vector2D SymbolSelectEditor::handlePos(int dx, int dy) {
return Vector2D(
0.5 * (maxV.x + minV.x + dx * (maxV.x - minV.x)),
0.5 * (maxV.y + minV.y + dy * (maxV.y - minV.y))
);
}
bool SymbolSelectEditor::onHandle(const Vector2D& mpos, int dx, int dy) {
wxPoint p = control.rotation.tr(handlePos(dx, dy));
wxPoint mp = control.rotation.tr(mpos);
p.x = p.x + 4 * dx;
p.y = p.y + 4 * dy;
return mp.x >= p.x - 4 && mp.x < p.x + 4 &&
mp.y >= p.y - 4 && mp.y < p.y + 4;
}
bool SymbolSelectEditor::onAnyHandle(const Vector2D& mpos, int* dxOut, int* dyOut) {
for (int dx = -1 ; dx < 1 ; ++dx) {
for (int dy = -1 ; dy < 1 ; ++dy) {
if ((dx != 0 || dy != 0) && onHandle(mpos, dx, dy)) { // (0,0) == center, not a handle
*dxOut = dx;
*dyOut = dy;
return true;
}
}
}
return false;
}
double SymbolSelectEditor::angleTo(const Vector2D& pos) {
return atan2(center.x - pos.x, center.y - pos.y);
}
SymbolPartP SymbolSelectEditor::findPart(const Vector2D& pos) {
FOR_EACH(p, getSymbol()->parts) {
if (pointInPart(pos, *p)) return p;
}
return SymbolPartP();
}
void SymbolSelectEditor::updateBoundingBox() {
// Find min and max coordinates
minV = Vector2D::infinity();
maxV = -Vector2D::infinity();
FOR_EACH(p, control.selectedParts) {
minV = piecewise_min(minV, p->minPos);
maxV = piecewise_max(maxV, p->maxPos);
}
// Find rotation center
center = Vector2D(0,0);
FOR_EACH(p, control.selectedParts) {
Vector2D size = p->maxPos - p->minPos;
size = size.mul(p->rotationCenter);
center += p->minPos + size;
}
center /= control.selectedParts.size();
}
void SymbolSelectEditor::resetActions() {
moveAction = nullptr;
scaleAction = nullptr;
rotateAction = nullptr;
shearAction = nullptr;
}
+117
View File
@@ -0,0 +1,117 @@
//+----------------------------------------------------------------------------+
//| 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
+220
View File
@@ -0,0 +1,220 @@
//+----------------------------------------------------------------------------+
//| 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/viewer.hpp>
// ----------------------------------------------------------------------------- : Constructor
SymbolViewer::SymbolViewer(const SymbolP& symbol, double borderRadius)
: borderRadius(borderRadius)
, SymbolView(symbol)
, rotation(0, RealRect(0,0,500,500))
{}
// ----------------------------------------------------------------------------- : Drawing
typedef shared_ptr<wxMemoryDC> MemoryDCP;
// Return a temporary DC with the same size as the parameter
MemoryDCP getTempDC(DC& dc) {
wxSize s = dc.GetSize();
Bitmap buffer(s.GetWidth(), s.GetHeight(), 1);
MemoryDCP newDC(new wxMemoryDC);
newDC->SelectObject(buffer);
// On windows 9x it seems that bitmaps are not black by default
#if !BITMAPS_DEFAULT_BLACK
newDC->SetPen(*wxTRANSPARENT_PEN);
newDC->SetBrush(*wxBLACK_BRUSH);
newDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
#endif
return newDC;
}
// Combine the temporary DCs used in the drawing with the main dc
void combineBuffers(DC& dc, DC* borders, DC* interior) {
wxSize s = dc.GetSize();
if (borders) dc.Blit(0, 0, s.GetWidth(), s.GetHeight(), borders, 0, 0, wxOR);
if (interior) dc.Blit(0, 0, s.GetWidth(), s.GetHeight(), interior, 0, 0, wxAND_INVERT);
}
void SymbolViewer::draw(DC& dc) {
bool paintedSomething = false;
bool buffersFilled = false;
// Temporary dcs
MemoryDCP borderDC;
MemoryDCP interiorDC;
// Check if we can paint directly to the dc
// This will fail if there are parts with combine == intersection
FOR_EACH(p, symbol->parts) {
if (p->combine == PART_INTERSECTION) {
paintedSomething = true;
break;
}
}
// Draw all parts, in reverse order (bottom to top)
FOR_EACH_REVERSE(p, symbol->parts) {
const SymbolPart& part = *p;
if (part.combine == PART_OVERLAP && buffersFilled) {
// We will be overlapping some previous parts, write them to the screen
combineBuffers(dc, borderDC.get(), interiorDC.get());
// Clear the buffers
buffersFilled = false;
paintedSomething = true;
wxSize s = dc.GetSize();
if (borderDC) {
borderDC->SetBrush(*wxBLACK_BRUSH);
borderDC->SetPen( *wxTRANSPARENT_PEN);
borderDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
}
interiorDC->SetBrush(*wxBLACK_BRUSH);
interiorDC->DrawRectangle(0, 0, s.GetWidth(), s.GetHeight());
}
if (!paintedSomething) {
// No need to buffer
if (!interiorDC) interiorDC = getTempDC(dc);
combineSymbolPart(part, dc, *interiorDC, true, false);
buffersFilled = true;
} else {
if (!borderDC) borderDC = getTempDC(dc);
if (!interiorDC) interiorDC = getTempDC(dc);
// Draw this part to the buffer
combineSymbolPart(part, *borderDC, *interiorDC, false, false);
buffersFilled = true;
}
}
// Output the final parts from the buffer
if (buffersFilled) {
combineBuffers(dc, borderDC.get(), interiorDC.get());
}
}
void SymbolViewer::highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style) {
// create point list
vector<wxPoint> points;
size_t size = part.points.size();
for(size_t i = 0 ; i < size ; ++i) {
segmentSubdivide(*part.getPoint((int)i), *part.getPoint((int)i+1), rotation, points);
}
// draw
if (style == HIGHLIGHT_BORDER) {
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.SetPen (wxPen(Color(255,0,0), 2));
dc.DrawPolygon((int)points.size(), &points[0]);
} else {
dc.SetLogicalFunction(wxOR);
dc.SetBrush(Color(0,0,64));
dc.SetPen (*wxTRANSPARENT_PEN);
dc.DrawPolygon((int)points.size(), &points[0]);
if (part.combine == PART_SUBTRACT || part.combine == PART_BORDER) {
dc.SetLogicalFunction(wxAND);
dc.SetBrush(Color(192,192,255));
dc.DrawPolygon((int)points.size(), &points[0]);
}
dc.SetLogicalFunction(wxCOPY);
}
}
void SymbolViewer::combineSymbolPart(const SymbolPart& part, DC& border, DC& interior, bool directB, bool directI) {
// what color should the interior be?
// use black when drawing to the screen
int interiorCol = directI ? 0 : 255;
// how to draw depends on combining mode
switch(part.combine) {
case PART_OVERLAP:
case PART_MERGE: {
drawSymbolPart(part, &border, &interior, 255, interiorCol, directB);
break;
} case PART_SUBTRACT: {
border.SetLogicalFunction(wxAND);
drawSymbolPart(part, &border, &interior, 0, interiorCol ^ 255, directB);
border.SetLogicalFunction(wxCOPY);
break;
} case PART_INTERSECTION: {
MemoryDCP keepBorder = getTempDC(border);
MemoryDCP keepInterior = getTempDC(interior);
drawSymbolPart(part, keepBorder.get(), keepInterior.get(), 255, 255, false);
// combine the temporary dcs with the result using the AND operator
wxSize s = border.GetSize();
border .Blit(0, 0, s.GetWidth(), s.GetHeight(), &*keepBorder, 0, 0, wxAND);
interior.Blit(0, 0, s.GetWidth(), s.GetHeight(), &*keepInterior, 0, 0, wxAND);
break;
} case PART_DIFFERENCE: {
// TODO
break;
} case PART_BORDER: {
// draw border as interior
drawSymbolPart(part, nullptr, &border, 0, 255, false);
break;
}
}
}
void SymbolViewer::drawSymbolPart(const SymbolPart& part, DC* border, DC* interior, int borderCol, int interiorCol, bool directB) {
// create point list
vector<wxPoint> points;
size_t size = part.points.size();
for(size_t i = 0 ; i < size ; ++i) {
segmentSubdivide(*part.getPoint((int)i), *part.getPoint((int)i+1), rotation, points);
}
// draw border
if (border) {
if (directB) {
// white/green
border->SetBrush(Color(borderCol, min(255,borderCol + 128), borderCol));
} else {
// white/black
border->SetBrush(Color(borderCol, borderCol, borderCol));
}
border->SetPen(wxPen(*wxWHITE, rotation.trS(borderRadius)));
border->DrawPolygon((int)points.size(), &points[0]);
}
// draw interior
if (interior) {
interior->SetBrush(Color(interiorCol,interiorCol,interiorCol));
interior->SetPen(*wxTRANSPARENT_PEN);
interior->DrawPolygon((int)points.size(), &points[0]);
}
}
/*
void SymbolViewer::calcBezierPoint(const ControlPointP& p0, const ControlPointP& p1, Point*& p_out, UInt count) {
BezierCurve c(*p0, *p1);
// add start point
*p_out = toDisplay(*p0);
++p_out;
// recursively calculate rest of curve
calcBezierOpt(c, *p0, *p1, 0.0f, 1.0f, p_out, count-1);
}
void SymbolViewer::calcBezierOpt(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, Point*& p_out, mutable UInt count) {
if (count <= 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
calcBezierOpt(c, p0, midpoint, t0, midtime, p_out, count/2);
// add midpoint
if (subdivide) {
*p_out = toDisplay(midpoint);
++p_out;
count -= 1;
}
// subdivide right
calcBezierOpt(c, midpoint, p1, midtime, t1, p_out, count/2);
}
*/
+74
View File
@@ -0,0 +1,74 @@
//+----------------------------------------------------------------------------+
//| 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_VIEWER
#define HEADER_GUI_SYMBOL_VIEWER
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/rotation.hpp>
#include <data/symbol.hpp>
#include <gfx/bezier.hpp>
// ----------------------------------------------------------------------------- : Symbol Viewer
enum HighlightStyle {
HIGHLIGHT_BORDER,
HIGHLIGHT_INTERIOR
};
/// Class that knows how to draw a symbol
class SymbolViewer : public SymbolView {
public:
// --------------------------------------------------- : Data
SymbolViewer(const SymbolP& symbol, double borderRadius = 0.05);
// drawing
double borderRadius;
// --------------------------------------------------- : Point translation
Rotation rotation; //^ Object that handles rotation, scaling and translation
// --------------------------------------------------- : Drawing
public:
/// Draw the symbol to a dc
void draw(DC& dc);
void highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style);
private:
/// Combines a symbol part with what is currently drawn, the border and interior are drawn separatly
/** directB/directI are true if the border/interior is the screen dc, false if it
* is a temporary 1 bit one
*/
void combineSymbolPart(const SymbolPart& part, DC& border, DC& interior, bool directB, bool directI);
/// Draw a symbol part, draws the border and the interior to separate DCs
/** The DCs may be null. directB should be true when drawing the border directly to the screen.
* The **Col parameters give the color to use for the (interior of) the border and the interior
* default should be white (255) border and black (0) interior.
*/
void drawSymbolPart(const SymbolPart& part, DC* border, DC* interior, int borderCol, int interiorCol, bool directB);
/*
// ------------------- Bezier curve calculation
// Calculate the points on a bezier curve between p0 and p1
// Stores the Points in p_out, at most count points are stored
// after this call p_out points to just beyond the last point
void calcBezierPoint(const ControlPointP& p0, const ControlPointP& p1, wxPoint*& p_out, UInt count);
// Subdivide a bezier curve by adding at most count points
// p0 = c(t0), p1 = c(p1)
// subdivides linearly between t0 and t1, and only when necessary
// adds points to p_out and increments the pointer when a point is added
void calcBezierOpt(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, wxPoint*& p_out, UInt count);
*/};
// ----------------------------------------------------------------------------- : EOF
#endif
+283
View File
@@ -0,0 +1,283 @@
//+----------------------------------------------------------------------------+
//| 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/window.hpp>
#include <gui/symbol/control.hpp>
#include <gui/symbol/part_list.hpp>
#include <gui/icon_menu.hpp>
#include <util/window_id.hpp>
#include <util/io/reader.hpp>
#include <wx/filename.h>
#include <wx/wfstream.h>
// ----------------------------------------------------------------------------- : Window ids
enum SymIDs
{ idFileNew = wxID_NEW
, idFileOpen = wxID_OPEN
, idFileSave = wxID_SAVE
, idFileSaveAs = wxID_SAVEAS
, idFileStore = 0
, idFileExit = wxID_EXIT
, idExtraTools = 1000
, idExtraToolsMax = idExtraTools + 500
, idEditUndo = wxID_UNDO
, idEditRedo = wxID_REDO
, idEditDuplicate = 1100 // idExtraTools + 100
, idModeSelect = idFileStore + 1
, idModeRotate
, idModePoints
, idModeShapes
, idModePaint
, idModeMax
, idPartList
, idControl
};
// ------------------------------------------------------------------------------------------------ : Default symbol
// A default symbol part, a square, moved by d
SymbolPartP defaultSymbolPart(double d) {
SymbolPartP part = new_shared<SymbolPart>();
part->points.push_back(new_shared2<ControlPoint>(d + .2, d + .2));
part->points.push_back(new_shared2<ControlPoint>(d + .2, d + .8));
part->points.push_back(new_shared2<ControlPoint>(d + .8, d + .8));
part->points.push_back(new_shared2<ControlPoint>(d + .8, d + .2));
part->name = _("Square");
return part;
}
// A default symbol, a square
SymbolP defaultSymbol() {
SymbolP symbol = new_shared<Symbol>();
symbol->parts.push_back(defaultSymbolPart(0));
return symbol;
}
// ----------------------------------------------------------------------------- : Constructor
SymbolWindow::SymbolWindow(Window* parent) {
init(parent, defaultSymbol());
}
SymbolWindow::SymbolWindow(Window* parent, String filename) {
// TODO
init(parent, defaultSymbol());
}
void SymbolWindow::init(Window* parent, SymbolP symbol) {
Create(parent, wxID_ANY, _("Symbol Editor"), wxDefaultPosition, wxSize(600,600), wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE);
inSelectionEvent = false;
// Menu bar
wxMenuBar* menuBar = new wxMenuBar();
IconMenu* menuFile = new IconMenu();
menuFile->Append(ID_FILE_NEW, _("TOOL_NEW"), _("&New...\tCtrl+N"), _("Create a new symbol"));
menuFile->Append(ID_FILE_OPEN, _("TOOL_OPEN"), _("&Open...\tCtrl+O"), _("Open a symbol"));
menuFile->Append(ID_FILE_SAVE, _("TOOL_SAVE"), _("&Save\tCtrl+S"), _("Save the symbol"));
menuFile->Append(ID_FILE_SAVE_AS, _("Save &As...\tF12"), _("Save the symbol under a diferent filename"));
menuFile->AppendSeparator();
menuFile->Append(ID_FILE_STORE, _("TOOL_APPLY"), _("S&tore\tCtrl+Enter"), _("Stores the symbol in the set"));
menuFile->AppendSeparator();
menuFile->Append(ID_FILE_EXIT, _("&Close\tAlt+F4"), _("Closes the symbol editor"));
menuBar->Append(menuFile, _("&File"));
IconMenu* menuEdit = new IconMenu();
menuEdit->Append(ID_EDIT_UNDO, _("TOOL_UNDO"), _("&Undo\tCtrl+Z"), _("Undoes the last action"));
menuEdit->Append(ID_EDIT_REDO, _("TOOL_REDO"), _("&Redo\tF4"), _(""));
menuEdit->AppendSeparator();
menuEdit->Append(ID_EDIT_DUPLICATE, _("TOOL_DUPLICATE"), _("&Duplicate\tCtrl+D"),_("Duplicates the selected shapes"));
menuBar->Append(menuEdit, _("&Edit"));
IconMenu* menuTool = new IconMenu();
menuTool->Append(ID_MODE_SELECT, _("TOOL_MODE_SELECT"), _("&Select\tF5"), _("Select and move shapes"), wxITEM_CHECK);
menuTool->Append(ID_MODE_ROTATE, _("TOOL_MODE_ROTATE"), _("&Rotate\tF6"), _("Rotate and shear shapes"), wxITEM_CHECK);
menuTool->Append(ID_MODE_POINTS, _("TOOL_MODE_CURVE"), _("&Points\tF7"), _("Edit control points for a shape in the symbol"), wxITEM_CHECK);
menuTool->Append(ID_MODE_SHAPES, _("TOOL_CIRCLE"), _("&Basic Shapes\tF8"), _("Draw basic shapes, such as rectangles and circles"), wxITEM_CHECK);
menuTool->Append(ID_MODE_PAINT, _("TOOL_MODE_PAINT"), _("P&aint\tF9"), _("Paint on the shape using a paintbrush"), wxITEM_CHECK);
menuBar->Append(menuTool, _("&Tool"));
SetMenuBar(menuBar);
// Statusbar
CreateStatusBar();
SetStatusText(_(""));
// Toolbar
wxToolBar* tb = CreateToolBar(wxTB_FLAT | wxNO_BORDER | wxTB_HORIZONTAL | wxTB_TEXT);
tb->AddTool(ID_FILE_STORE, _("Store"), Bitmap(_("TOOL_APPLY")), wxNullBitmap, wxITEM_NORMAL, _("Store symbol in set"), _("Stores the symbol in the set"));
tb->AddSeparator();
tb->AddTool(ID_EDIT_UNDO, _("Undo"), Bitmap(_("TOOL_UNDO")), wxNullBitmap, wxITEM_NORMAL, _("Undo"), _("Undoes the last action"));
tb->AddTool(ID_EDIT_REDO, _("Redo"), Bitmap(_("TOOL_REDO")), wxNullBitmap, wxITEM_NORMAL, _("Redo"), _("Redoes the last action undone"));
tb->Realize();
// Edit mode toolbar
wxPanel* emp = new wxPanel(this, wxID_ANY);
wxToolBar* em = new wxToolBar(emp, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | wxTB_VERTICAL | wxTB_TEXT | wxTB_HORZ_LAYOUT);
em->AddTool(ID_MODE_SELECT,_("Select"), Bitmap(_("TOOL_MODE_SELECT")), wxNullBitmap, wxITEM_CHECK, _("Select (F5)"), _("Select and move parts of the symbol"));
em->AddTool(ID_MODE_ROTATE,_("Rotate"), Bitmap(_("TOOL_MODE_ROTATE")), wxNullBitmap, wxITEM_CHECK, _("Rotate (F6)"), _("Rotate and shear parts of the symbol"));
em->AddSeparator();
em->AddTool(ID_MODE_POINTS,_("Points"), Bitmap(_("TOOL_MODE_CURVE")), wxNullBitmap, wxITEM_CHECK, _("Points (F7)"), _("Edit control points for a shape in the symbol"));
em->AddSeparator();
em->AddTool(ID_MODE_SHAPES,_("Basic Shapes"), Bitmap(_("TOOL_CIRCLE")), wxNullBitmap, wxITEM_CHECK, _("Basic Shapes (F8)"), _("Draw basic shapes, such as rectangles and circles"));
em->AddSeparator();
em->AddTool(ID_MODE_PAINT, _("Paint"), Bitmap(_("TOOL_MODE_PAINT")), wxNullBitmap, wxITEM_CHECK, _("Paint on shape (F9)"), _("Paint on the shape using a paintbrush"));
em->AddSeparator();
em->Realize();
// Controls
control = new SymbolControl (this, ID_CONTROL, symbol);
parts = new SymbolPartList(this, ID_PART_LIST, symbol);
// Lay out
wxSizer* es = new wxBoxSizer(wxHORIZONTAL);
es->Add(em, 0, wxEXPAND | wxTOP | wxBOTTOM | wxALIGN_CENTER, 1);
emp->SetSizer(es);
wxSizer* s = new wxBoxSizer(wxHORIZONTAL);
wxSizer* v = new wxBoxSizer(wxVERTICAL);
v->Add(emp, 0, wxEXPAND);
v->Add(parts, 1, wxEXPAND);
s->Add(v, 0, wxEXPAND);
s->Add(control, 1, wxEXPAND);
SetSizer(s);
}
// ----------------------------------------------------------------------------- : Event handling
void SymbolWindow::onFileNew(wxCommandEvent& ev) {
SymbolP symbol = defaultSymbol();
parts->setSymbol(symbol);
control->setSymbol(symbol);
}
void SymbolWindow::onFileOpen(wxCommandEvent& ev) {
String name = wxFileSelector(_("Open symbol"),_(""),_(""),_(""),_("Symbol files|*.mse-symbol;*.bmp|MSE2 symbol files (*.mse-symbol)|*.mse-symbol|MSE1 symbol files (*.bmp)|*.bmp"),wxOPEN|wxFILE_MUST_EXIST, this);
if (!name.empty()) {
wxFileName n(name);
String ext = n.GetExt();
SymbolP symbol;
if (ext.Lower() == _("bmp")) {
//% symbol = importSymbol(wxImage(name));
} else {
Reader reader(new_shared1<wxFileInputStream>(name), name);
reader.handle(symbol);
}
// show...
parts->setSymbol(symbol);
control->setSymbol(symbol);
}
}
void SymbolWindow::onFileSave(wxCommandEvent& ev) {
}
void SymbolWindow::onFileSaveAs(wxCommandEvent& ev) {
}
void SymbolWindow::onFileStore(wxCommandEvent& ev) {
}
void SymbolWindow::onFileExit(wxCommandEvent& ev) {
Close();
}
void SymbolWindow::onEditUndo(wxCommandEvent& ev) {
if (!control->isEditing()) {
control->getSymbol()->actions.undo();
control->Refresh(false);
}
}
void SymbolWindow::onEditRedo(wxCommandEvent& ev) {
if (!control->isEditing()) {
control->getSymbol()->actions.redo();
control->Refresh(false);
}
}
void SymbolWindow::onModeChange(wxCommandEvent& ev) {
control->onModeChange(ev);
}
void SymbolWindow::onExtraTool(wxCommandEvent& ev) {
control->onExtraTool(ev);
}
void SymbolWindow::onUpdateUI(wxUpdateUIEvent& ev) {
switch(ev.GetId()) {
// file menu
case idFileStore: {
// ev.Enable(value);
break;
// undo/redo
} case idEditUndo: {
ev.Enable(control->getSymbol()->actions.canUndo());
String label = control->getSymbol()->actions.undoName();
ev.SetText(label + _("\tCtrl+Z"));
GetToolBar()->SetToolShortHelp(ID_EDIT_UNDO, label);
break;
} case idEditRedo: {
ev.Enable(control->getSymbol()->actions.canRedo());
String label = control->getSymbol()->actions.redoName();
ev.SetText(label + _("\tF4"));
GetToolBar()->SetToolShortHelp(ID_EDIT_REDO, label);
break;
} default: {
// items created by the editor control
control->onUpdateUI(ev);
break;
}
}
}
void SymbolWindow::onSelectFromList(wxListEvent& ev) {
if (inSelectionEvent) return ;
inSelectionEvent = true;
parts->getSelectedParts(control->selectedParts);
control->onUpdateSelection();
inSelectionEvent = false;
}
void SymbolWindow::onActivateFromList(wxListEvent& ev) {
control->activatePart(control->getSymbol()->parts.at(ev.GetIndex()));
}
void SymbolWindow::onSelectFromControl() {
if (inSelectionEvent) return ;
inSelectionEvent = true;
parts->selectParts(control->selectedParts);
inSelectionEvent = false;
}
// ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(SymbolWindow, wxFrame)
EVT_MENU (ID_FILE_NEW, SymbolWindow::onFileNew)
EVT_MENU (ID_FILE_OPEN, SymbolWindow::onFileOpen)
EVT_MENU (ID_FILE_SAVE, SymbolWindow::onFileSave)
EVT_MENU (ID_FILE_SAVE_AS, SymbolWindow::onFileSaveAs)
EVT_MENU (ID_FILE_STORE, SymbolWindow::onFileStore)
EVT_MENU (ID_FILE_EXIT, SymbolWindow::onFileExit)
EVT_MENU (ID_EDIT_UNDO, SymbolWindow::onEditUndo)
EVT_MENU (ID_EDIT_REDO, SymbolWindow::onEditRedo)
EVT_TOOL_RANGE (ID_MODE_MIN, ID_MODE_MAX, SymbolWindow::onModeChange)
EVT_TOOL_RANGE (ID_CHILD_MIN, ID_CHILD_MAX, SymbolWindow::onExtraTool)
EVT_UPDATE_UI (wxID_ANY, SymbolWindow::onUpdateUI)
EVT_LIST_ITEM_SELECTED (ID_PART_LIST, SymbolWindow::onSelectFromList)
EVT_LIST_ITEM_DESELECTED (ID_PART_LIST, SymbolWindow::onSelectFromList)
EVT_LIST_ITEM_ACTIVATED (ID_PART_LIST, SymbolWindow::onActivateFromList)
END_EVENT_TABLE ()
+75
View File
@@ -0,0 +1,75 @@
//+----------------------------------------------------------------------------+
//| 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_WINDOW
#define HEADER_GUI_SYMBOL_WINDOW
// ----------------------------------------------------------------------------- : Includes
#include "../../util/prec.hpp"
#include <data/symbol.hpp>
#include <wx/listctrl.h>
//#include "control.hpp"
class SymbolControl;
class SymbolPartList;
// ----------------------------------------------------------------------------- : SymbolWindow
/// The window for editing symbols
class SymbolWindow : public Frame {
public:
/// Construct a SymbolWindow
SymbolWindow(Window* parent);
/// Construct a SymbolWindow showing a symbol from a file
SymbolWindow(Window* parent, String filename);
// /// Construct a SymbolWindow showing a symbol from a set
// SymbolWindow(Window* parent);
private:
// --------------------------------------------------- : Children
/// Actual initialisation
void init(Window* parent, SymbolP symbol);
SymbolControl* control; //^ The control for editing/displaying the symbol
SymbolPartList* parts; //^ A list of parts in the symbol
// when editing a symbol field
// SymbolValueP value
// SetP set
// --------------------------------------------------- : Event handling
DECLARE_EVENT_TABLE();
void onFileNew (wxCommandEvent&);
void onFileOpen (wxCommandEvent&);
void onFileSave (wxCommandEvent&);
void onFileSaveAs(wxCommandEvent&);
void onFileStore (wxCommandEvent&);
void onFileExit (wxCommandEvent&);
void onEditUndo (wxCommandEvent&);
void onEditRedo (wxCommandEvent&);
void onModeChange(wxCommandEvent&);
void onExtraTool (wxCommandEvent&);
void onUpdateUI(wxUpdateUIEvent& e);
/// Changing selected parts in the list
void onSelectFromList(wxListEvent& ev);
/// Activating a part: open the point editor
void onActivateFromList(wxListEvent& ev);
bool inSelectionEvent; //^ Prevent recursion in onSelect...
public:
void onSelectFromControl();
};
// ----------------------------------------------------------------------------- : EOF
#endif
+43
View File
@@ -0,0 +1,43 @@
//+----------------------------------------------------------------------------+
//| 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/util.hpp>
#include <util/error.hpp>
#include <wx/mstream.h>
// ----------------------------------------------------------------------------- : DC related
/// Fill a DC with a single color
void clearDC(DC& dc, const wxBrush& brush) {
wxSize size = dc.GetSize();
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(brush);
dc.DrawRectangle(0, 0, size.GetWidth(), size.GetHeight());
}
// ----------------------------------------------------------------------------- : Image related
Image loadResourceImage(String name) {
#ifdef __WXMSW__
// Load resource
// based on wxLoadUserResource
// The image can be in an IMAGE resource, in any file format
HRSRC hResource = ::FindResource(wxGetInstance(), name, _("IMAGE"));
if ( hResource == 0 ) throw InternalError(_("Resource not found: ") + name);
HGLOBAL hData = ::LoadResource(wxGetInstance(), hResource);
if ( hData == 0 ) throw InternalError(_("Resource not an image: ") + name);
char* data = (char *)::LockResource(hData);
if ( !data ) throw InternalError(_("Resource cannot be locked: ") + name);
int len = ::SizeofResource(wxGetInstance(), hResource);
wxMemoryInputStream stream(data, len);
return wxImage(stream);
#endif
}
+29
View File
@@ -0,0 +1,29 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL
#define HEADER_GUI_UTIL
/** @file gui/util.hpp
* Utility functions for use in the gui. Most are related to drawing.
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
// ----------------------------------------------------------------------------- : DC related
/// Fill a DC with a single color
void clearDC(DC& dc, const wxBrush& brush = *wxBLACK_BRUSH);
// ----------------------------------------------------------------------------- : Resource related
/// Load an image from a resource
Image loadResourceImage(String name);
// ----------------------------------------------------------------------------- : EOF
#endif
+29
View File
@@ -0,0 +1,29 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mse", "mse.vcproj", "{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
Debug Unicode = Debug Unicode
Release = Release
Release Profile Unicode = Release Profile Unicode
Release Unicode = Release Unicode
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Debug.ActiveCfg = Debug|Win32
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Debug.Build.0 = Debug|Win32
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Debug Unicode.ActiveCfg = Debug Unicode|Win32
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Debug Unicode.Build.0 = Debug Unicode|Win32
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release.ActiveCfg = Release|Win32
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release.Build.0 = Release|Win32
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release Profile Unicode.ActiveCfg = Release Unicode|Win32
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release Profile Unicode.Build.0 = Release Unicode|Win32
{A78D6E04-7637-4C7A-AC63-DAAC3C4DB81B}.Release Unicode.ActiveCfg = Release Unicode|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal
+846
View File
@@ -0,0 +1,846 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="mse"
ProjectGUID="{DB9DCD62-4679-47DD-8BEE-8A0191161A51}"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="../build/$(ConfigurationName)"
IntermediateDirectory="../build/$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=".; &quot;$(WX_PATH)/include&quot;; &quot;$(WX_PATH)\lib\vc_lib\mswd&quot;"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;__WINDOWS__;__WXMSW__;DEBUG=1;__WXDEBUG__;__WIN95__;__WIN32__;WINVER=0x0400;STRICT"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
BufferSecurityCheck="TRUE"
RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="util/prec.hpp"
PrecompiledHeaderFile=""
AssemblerListingLocation="$(OutDir)"
ProgramDataBaseFileName="$(IntDir)/vc70.pdb"
BrowseInformation="1"
WarningLevel="4"
WarnAsError="TRUE"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib odbc32.lib odbccp32.lib wxbase26d.lib wxmsw26d_core.lib"
OutputFile="$(OutDir)/mse.exe"
LinkIncremental="2"
IgnoreDefaultLibraryNames="libcd.lib,libcid.lib"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/mse.pdb"
SubSystem="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="_DEBUG"
MkTypLibCompatible="TRUE"
SuppressStartupBanner="TRUE"
TargetEnvironment="1"
TypeLibraryName="$(OutDir)/mse.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="../build/$(ConfigurationName)"
IntermediateDirectory="../build/$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="TRUE">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
GlobalOptimizations="TRUE"
InlineFunctionExpansion="1"
OmitFramePointers="TRUE"
OptimizeForProcessor="0"
OptimizeForWindowsApplication="TRUE"
AdditionalIncludeDirectories=".; &quot;$(WX_PATH)/include&quot;; &quot;$(WX_PATH)\lib\vc_lib\msw&quot;"
PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;__WINDOWS__;__WXMSW__;__WIN95__;__WIN32__;WINVER=0x0400;STRICT"
StringPooling="TRUE"
RuntimeLibrary="4"
BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="TRUE"
RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="util/prec.hpp"
PrecompiledHeaderFile=""
AssemblerListingLocation="$(OutDir)"
ProgramDataBaseFileName="$(IntDir)/vc70.pdb"
WarningLevel="3"
WarnAsError="FALSE"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib wxbase26.lib wxmsw26_core.lib wxjpeg.lib wxpng.lib wxtiff.lib wxzlib.lib"
OutputFile="$(OutDir)/mse.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
IgnoreDefaultLibraryNames="libc.lib,libci.lib"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/mse.pdb"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="NDEBUG"
MkTypLibCompatible="TRUE"
SuppressStartupBanner="TRUE"
TargetEnvironment="1"
TypeLibraryName="$(OutDir)/mse.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Debug Unicode|Win32"
OutputDirectory="../build/$(ConfigurationName)"
IntermediateDirectory="../build/$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=".; &quot;$(WX_PATH)/include&quot;; &quot;$(WX_PATH)\lib\vc_lib\mswud&quot;"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;__WINDOWS__;__WXMSW__;DEBUG=1;__WXDEBUG__;__WIN95__;__WIN32__;WINVER=0x0400;STRICT;UNICODE;_UNICODE;wxUSE_UNICODE"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
BufferSecurityCheck="TRUE"
RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="util/prec.hpp"
PrecompiledHeaderFile=""
AssemblerListingLocation="$(OutDir)"
ProgramDataBaseFileName="$(IntDir)/vc70.pdb"
BrowseInformation="1"
WarningLevel="4"
WarnAsError="TRUE"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib wxbase26ud.lib wxmsw26ud_core.lib wxjpegd.lib wxpngd.lib wxtiffd.lib wxzlibd.lib"
OutputFile="$(OutDir)/mse.exe"
LinkIncremental="2"
IgnoreDefaultLibraryNames="libcd.lib,libcid.lib"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/mse.pdb"
SubSystem="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="_DEBUG"
MkTypLibCompatible="TRUE"
SuppressStartupBanner="TRUE"
TargetEnvironment="1"
TypeLibraryName="$(OutDir)/mse.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release Unicode|Win32"
OutputDirectory="../build/$(ConfigurationName)"
IntermediateDirectory="../build/$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="TRUE">
<Tool
Name="VCCLCompilerTool"
Optimization="3"
GlobalOptimizations="TRUE"
InlineFunctionExpansion="1"
FavorSizeOrSpeed="2"
OmitFramePointers="TRUE"
OptimizeForProcessor="3"
OptimizeForWindowsApplication="TRUE"
AdditionalIncludeDirectories=".; &quot;$(WX_PATH)/include&quot;; &quot;$(WX_PATH)\lib\vc_lib\mswu&quot;"
PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;__WINDOWS__;__WXMSW__;__WIN95__;__WIN32__;WINVER=0x0400;STRICT;UNICODE;_UNICODE;wxUSE_UNICODE"
StringPooling="TRUE"
RuntimeLibrary="4"
BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="TRUE"
EnableEnhancedInstructionSet="1"
RuntimeTypeInfo="TRUE"
UsePrecompiledHeader="0"
PrecompiledHeaderThrough="util/prec.hpp"
PrecompiledHeaderFile=""
AssemblerListingLocation="$(OutDir)"
ProgramDataBaseFileName="$(IntDir)/vc70.pdb"
WarningLevel="3"
WarnAsError="FALSE"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="rpcrt4.lib wsock32.lib comctl32.lib wxbase26u.lib wxmsw26u_core.lib wxjpeg.lib wxpng.lib wxtiff.lib wxzlib.lib"
OutputFile="$(OutDir)/mse.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
IgnoreDefaultLibraryNames="libc.lib,libci.lib"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/mse.pdb"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="NDEBUG"
MkTypLibCompatible="TRUE"
SuppressStartupBanner="TRUE"
TargetEnvironment="1"
TypeLibraryName="$(OutDir)/mse.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="gui"
Filter="">
<File
RelativePath=".\gui\icon_menu.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\icon_menu.hpp">
</File>
<File
RelativePath=".\gui\util.cpp">
</File>
<File
RelativePath=".\gui\util.hpp">
</File>
<Filter
Name="main"
Filter="">
<File
RelativePath=".\gui\main\panel.hpp">
</File>
<File
RelativePath=".\gui\main\window.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\main\window.hpp">
</File>
</Filter>
<Filter
Name="control"
Filter="">
</Filter>
<Filter
Name="editor"
Filter="">
</Filter>
<Filter
Name="symbol"
Filter="">
<File
RelativePath=".\gui\symbol\basic_shape_editor.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\symbol\basic_shape_editor.hpp">
</File>
<File
RelativePath=".\gui\symbol\control.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\symbol\control.hpp">
</File>
<File
RelativePath=".\gui\symbol\editor.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\symbol\editor.hpp">
</File>
<File
RelativePath=".\gui\symbol\part_list.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\symbol\part_list.hpp">
</File>
<File
RelativePath=".\gui\symbol\point_editor.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\symbol\point_editor.hpp">
</File>
<File
RelativePath=".\gui\symbol\select_editor.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\symbol\select_editor.hpp">
</File>
<File
RelativePath=".\gui\symbol\viewer.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\symbol\viewer.hpp">
</File>
<File
RelativePath=".\gui\symbol\window.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)2.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Debug Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)2.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gui\symbol\window.hpp">
</File>
</Filter>
</Filter>
<Filter
Name="resource"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
<File
RelativePath=".\resource\mse.rc">
</File>
</Filter>
<Filter
Name="symbol"
Filter="">
</Filter>
<Filter
Name="data"
Filter="">
<File
RelativePath=".\data\card.hpp">
</File>
<File
RelativePath=".\data\card_style.hpp">
</File>
<File
RelativePath=".\data\field.hpp">
</File>
<File
RelativePath=".\data\game.hpp">
</File>
<File
RelativePath=".\data\locale.hpp">
</File>
<File
RelativePath=".\data\set.hpp">
</File>
<File
RelativePath=".\data\symbol.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\data\symbol.hpp">
</File>
<Filter
Name="action"
Filter="">
<File
RelativePath=".\data\action\symbol.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/symbol_action.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/symbol_action.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Debug Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/symbol_action.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/symbol_action.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\data\action\symbol.hpp">
</File>
<File
RelativePath=".\data\action\symbol_part.cpp">
</File>
<File
RelativePath=".\data\action\symbol_part.hpp">
</File>
</Filter>
</Filter>
<Filter
Name="util"
Filter="">
<File
RelativePath=".\util\action_stack.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\util\action_stack.hpp">
</File>
<File
RelativePath=".\util\error.cpp">
</File>
<File
RelativePath=".\util\error.hpp">
</File>
<File
RelativePath=".\util\for_each.hpp">
</File>
<File
RelativePath=".\util\index_map.hpp">
</File>
<File
RelativePath=".\util\prec.hpp">
</File>
<File
RelativePath=".\util\real_point.hpp">
</File>
<File
RelativePath=".\util\reflect.hpp">
</File>
<File
RelativePath=".\util\rotation.cpp">
</File>
<File
RelativePath=".\util\rotation.hpp">
</File>
<File
RelativePath=".\util\smart_ptr.hpp">
</File>
<File
RelativePath=".\util\string.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\util\string.hpp">
</File>
<File
RelativePath=".\util\vector2d.hpp">
</File>
<File
RelativePath=".\util\window_id.hpp">
</File>
<Filter
Name="io"
Filter="">
<File
RelativePath=".\util\io\package.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\util\io\package.hpp">
</File>
<File
RelativePath=".\util\io\reader.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\util\io\reader.hpp">
</File>
<File
RelativePath=".\util\io\writer.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\util\io\writer.hpp">
</File>
</Filter>
</Filter>
<Filter
Name="gfx"
Filter="">
<File
RelativePath=".\gfx\bezier.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gfx\bezier.hpp">
</File>
<File
RelativePath=".\gfx\combine_image.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gfx\gfx.hpp">
</File>
<File
RelativePath=".\gfx\polynomial.cpp">
</File>
<File
RelativePath=".\gfx\polynomial.hpp">
</File>
<File
RelativePath=".\gfx\resample_text.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\gfx\rotate_image.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="script"
Filter="">
</Filter>
<File
RelativePath=".\code_template.cpp">
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release Unicode|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\code_template.hpp">
</File>
<File
RelativePath=".\main.cpp">
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>
+108
View File
@@ -0,0 +1,108 @@
//+----------------------------------------------------------------------------+
//| 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 <algorithm>
#include "action_stack.hpp"
#include "for_each.hpp"
// ----------------------------------------------------------------------------- : Action stack
DECLARE_TYPEOF_COLLECTION(Action*);
DECLARE_TYPEOF_COLLECTION(ActionListener*);
ActionStack::ActionStack()
: savePoint(nullptr)
{}
ActionStack::~ActionStack() {
// we own the actions, delete them
FOR_EACH(a, undoActions) delete a;
FOR_EACH(a, redoActions) delete a;
}
void ActionStack::add(Action* action, bool allowMerge) {
if (!action) return; // no action
action->perform(false); // TODO: delete action if perform throws
redoActions.clear();
tellListeners(*action);
// try to merge?
if (allowMerge && !undoActions.empty() && undoActions.back()->merge(action)) {
// merged with top undo action
delete action;
} else {
undoActions.push_back(action);
}
}
void ActionStack::undo() {
assert(canUndo());
Action* action = undoActions.back();
action->perform(true);
// move to redo stack
undoActions.pop_back();
redoActions.push_back(action);
}
void ActionStack::redo() {
assert(canRedo());
Action* action = redoActions.back();
action->perform(false);
// move to undo stack
redoActions.pop_back();
undoActions.push_back(action);
}
bool ActionStack::canUndo() const {
return !undoActions.empty();
}
bool ActionStack::canRedo() const {
return !redoActions.empty();
}
String ActionStack::undoName() const {
if (canUndo()) {
return _("Undo ") + capitalize(undoActions.back()->getName(true));
} else {
return _("Undo");
}
}
String ActionStack::redoName() const {
if (canRedo()) {
return _("Redo ") + capitalize(redoActions.back()->getName(false));
} else {
return _("Redo");
}
}
bool ActionStack::atSavePoint() const {
return (undoActions.empty() && savePoint == nullptr)
|| (undoActions.back() == savePoint);
}
void ActionStack::setSavePoint() {
if (undoActions.empty()) {
savePoint = nullptr;
} else {
savePoint = undoActions.back();
}
}
void ActionStack::addListener(ActionListener* listener) {
listeners.push_back(listener);
}
void ActionStack::removeListener(ActionListener* listener) {
listeners.erase(
std::remove(
listeners.begin(),
listeners.end(),
listener
),
listeners.end()
);
}
void ActionStack::tellListeners(const Action& action) {
FOR_EACH(l, listeners) l->onAction(action);
}
+136
View File
@@ -0,0 +1,136 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_ACTION_STACK
#define HEADER_UTIL_ACTION_STACK
// ----------------------------------------------------------------------------- : Includes
#include <vector>
#include "string.hpp"
// ----------------------------------------------------------------------------- : Action
/// Base class for actions that can be stored in an ActionStack.
/** An action is something that can be done to modify an object.
* It must store the necessary information to also undo the action.
*/
class Action {
public:
virtual ~Action() {};
/// Name of the action, for use in strings like "Undo <name>"
virtual String getName(bool toUndo) const = 0;
/// Perform the action
/// Must be implemented in derived class
/** Perform will only ever be called alternatingly with toUndo = true/false,
* the first time with toUndo = false
*/
/// @param toUndo if true, undo the action instead of doing it
virtual void perform(bool toUndo) = 0;
/// Try to merge another action to the end of this action.
/// Either: return false and do nothing
/// Or: return true and change this action to incorporate both actions
virtual bool merge(const Action* action) { return false; }
};
// ----------------------------------------------------------------------------- : Action listeners
/// Base class/interface for objects that listen to actions
class ActionListener {
public:
virtual void onAction(const Action& a) = 0;
};
// ----------------------------------------------------------------------------- : Action stack
/// A stack of actions that can be done and undone.
/** This class handles the undo and redo functionality of a particular object.
*
* This class also takes on the role of Observable, ActionListeners can register themselfs.
* They will be notified when an action is added.
*/
class ActionStack {
public:
ActionStack();
~ActionStack();
/// Add an action to the stack, and perform that action.
/// Tells all listeners about the action.
/// The ActionStack takes ownership of the action
void add(Action* action, bool allowMerge = true);
/// Undoes the last action that was (re)done
/// @pre canUndo()
void undo();
/// Redoes the last action that was undone
/// @pre canRedo()
void redo();
/// Is undoing possible?
bool canUndo() const;
/// Is redoing possible?
bool canRedo() const;
/// Name of the action that will be undone next, in the form "Undo <Action>"
/// If there is no action to undo returns "Undo"
String undoName() const;
/// Name of the action that will be redone next "Redo <Action>"
/// If there is no action to undo returns "Redo"
String redoName() const;
/// Is the file currently at a 'savepoint'?
/// This is the last point at which the file was saved
bool atSavePoint() const;
/// Indicate that the file is at a savepoint.
void setSavePoint();
/// Add an action listener
void addListener(ActionListener* listener);
/// Remove an action listener
void removeListener(ActionListener* listener);
/// Tell all listeners about an action
void tellListeners(const Action&);
private:
/// Actions to be undone
/// Owns the action objects!
vector<Action*> undoActions;
/// Actions to be redone
/// Owns the action objects!
vector<Action*> redoActions;
/// Point at which the file was saved, corresponds to the top of the undo stack at that point
Action* savePoint;
/// Objects that are listening to actions
vector<ActionListener*> listeners;
};
// ----------------------------------------------------------------------------- : Utilities
/// Tests if variable has the type Type
/** Uses dynamic cast, so Type must have a virtual function.
*/
#define TYPE_CASE_(variable, Type) \
if (dynamic_cast<const Type*>(&variable))
/// Tests if variable has the type Type. If this is the case, makes
/// variable have type Type inside the statement
/** Uses dynamic cast, so Type must have a virtual function.
*/
#define TYPE_CASE(variable, Type) \
pair<const Type*,bool> Type##variable \
(dynamic_cast<const Type*>(&variable), true); \
if (Type##variable.first) \
for (const Type& variable = *Type##variable.first ; \
Type##variable.second ; \
Type##variable.second = false)
// ----------------------------------------------------------------------------- : EOF
#endif
+21
View File
@@ -0,0 +1,21 @@
//+----------------------------------------------------------------------------+
//| 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/error.hpp>
// ----------------------------------------------------------------------------- : Error types
Error::Error(const String& message)
: message(message)
{}
Error::~Error() {}
String Error::what() const {
return message;
}
+60
View File
@@ -0,0 +1,60 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_ERROR
#define HEADER_UTIL_ERROR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
/** @file util/error.hpp
*
* Classes and functions for handling errors/exceptions
*/
// ----------------------------------------------------------------------------- : Error types
/// Our own exception class
class Error {
public:
Error(const String& message);
virtual ~Error();
/// Return the error message
virtual String what() const;
private:
String message; //^ The error message
};
/// Internal errors
class InternalError : public Error {
public:
inline InternalError(const String& str)
: Error(_("An internal error occured, please contact the author:\n") + str)
{}
};
// ----------------------------------------------------------------------------- : File errors
// Errors related to packages
class PackageError : public Error {
public:
inline PackageError(const String& str) : Error(str) {}
};
// ----------------------------------------------------------------------------- : Parse errors
// Parse errors
class ParseError : public Error {
public:
inline ParseError(const String& str) : Error(str) {}
};
// ----------------------------------------------------------------------------- : EOF
#endif
+169
View File
@@ -0,0 +1,169 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_FOR_EACH
#define HEADER_UTIL_FOR_EACH
/** @file util/for_each.hpp
*
* Macros to simplify looping over collections.
* This header contains some evil template and macro hackery.
*/
// ----------------------------------------------------------------------------- : Includes
// ----------------------------------------------------------------------------- : Typeof magic
#ifdef __GNUC__
// GCC has a buildin typeof function, so it doesn't need (as much) hacks
#define DECLARE_TYPEOF(T)
#define DECLARE_TYPEOF_COLLECTION(T)
#define TYPEOF(Value) __typeof(Value)
#define TYPEOF_IT(Value) __typeof(Value.begin())
#define TYPEOF_CIT(Value) __typeof(Value.begin())
#define TYPEOF_RIT(Value) __typeof(Value.rbegin())
#define TYPEOF_REF(Value) __typeof(*Value.begin())&
#else
/// Helper for typeof tricks
template<const type_info &ref_type_info> struct TypeOf {};
/// The type of a value
#define TYPEOF(Value) TypeOf<typeid(Value)>::type
/// The type of an iterator
#define TYPEOF_IT(Value) TypeOf<typeid(Value)>::iterator
/// The type of a const iterator
#define TYPEOF_CIT(Value) TypeOf<typeid(Value)>::const_iterator
/// The type of a reverse iterator
#define TYPEOF_RIT(Value) TypeOf<typeid(Value)>::reverse_iterator
/// The type of a value
#define TYPEOF_REF(Value) TypeOf<typeid(Value)>::reference
/// Declare typeof magic for a specific type
#define DECLARE_TYPEOF(T) \
template<> struct TypeOf<typeid(T)> { \
typedef T type; \
typedef T::iterator iterator; \
typedef T::const_iterator const_iterator; \
typedef T::reverse_iterator reverse_iterator; \
typedef T::reference reference; \
}
/// Declare typeof magic for a specific type that doesn't support reverse iterators
#define DECLARE_TYPEOF_NO_REV(T) \
template<> struct TypeOf<typeid(T)> { \
typedef T type; \
typedef T::iterator iterator; \
typedef T::const_iterator const_iterator; \
typedef T::reference reference; \
}
/// Declare typeof magic for a specific type, using const iterators
#define DECLARE_TYPEOF_CONST(T) \
template<> struct TypeOf<typeid(T)> { \
typedef T type; \
typedef T::const_iterator iterator; \
typedef T::const_iterator const_iterator; \
typedef T::const_reverse_iterator reverse_iterator; \
typedef T::const_reference reference; \
}
/// Declare typeof magic for a specific std::vector type
#define DECLARE_TYPEOF_COLLECTION(T) DECLARE_TYPEOF(vector<T>); \
DECLARE_TYPEOF_CONST(set<T>)
#endif
// ----------------------------------------------------------------------------- : Looping macros with iterators
/// Iterate over a collection, using an iterator it of type Type
/// Usage: FOR_EACH_IT_T(Type,it,collect) { body-of-loop }
#define FOR_EACH_IT_T(Type,Iterator,Collection) \
for(Type Iterator = Collection.begin() ; \
Iterator != Collection.end() ; \
++Iterator)
/// Iterate over a collection whos type must be declared with DECLARE_TYPEOF
/// Usage: FOR_EACH_IT(it,collect) { body-of-loop }
#define FOR_EACH_IT(Iterator,Collection) \
FOR_EACH_IT_T(TYPEOF_IT(Collection), Iterator, Collection)
/// Iterate over a collection whos type must be declared with DECLARE_TYPEOF
/// Uses a const_iterator
/// Usage: FOR_EACH_IT(it,collect) { body-of-loop }
#define FOR_EACH_CONST_IT(Iterator,Collection) \
FOR_EACH_IT_T(TYPEOF_CIT(Collection), Iterator, Collection)
/// Iterate over a collection in whos type must be declared with DECLARE_TYPEOF
/// Iterates using a reverse_iterator
/// Usage: FOR_EACH_REVERSE_IT(it,collect) { body-of-loop }
#define FOR_EACH_REVERSE_IT(Iterator,Collection) \
for(TYPEOF_RIT(Collection) \
Iterator = Collection.rbegin() ; \
Iterator != Collection.rend() ; \
++Iterator)
// ----------------------------------------------------------------------------- : Looping macros
/// Iterate over a collection, with an iterator of type TypeIt, and elements of type TypeElem
/// Usage: FOR_EACH_T(TypeIt,TypeElem,e,collect) { body-of-loop }
/** We need a hack to be able to declare a local variable without needing braces.
* To do this we use a nested for loop that is only executed once, and which is optimized away.
* To terminate this loop we need an extra bool, which we set to false after the first iteration.
*/
#define FOR_EACH_T(TypeIt,TypeElem,Elem,Collection) \
for(std::pair<TypeIt,bool> Elem##_IT(Collection.begin(), true) ; \
Elem##_IT.first != Collection.end() ; \
++Elem##_IT.first, Elem##_IT.second = true) \
for(TypeElem Elem = *Elem##_IT.first ; \
Elem##_IT.second ; \
Elem##_IT.second = false)
/// Iterate over a collection whos type must be declared with DECLARE_TYPEOF
/// Usage: FOR_EACH(e,collect) { body-of-loop }
#define FOR_EACH(Elem,Collection) \
FOR_EACH_T(TYPEOF_IT(Collection), TYPEOF_REF(Collection), Elem, Collection)
/// Iterate over a collection whos type must be declared with DECLARE_TYPEOF
/// Iterates using a reverse_iterator
/// Usage: FOR_EACH_REVERSE(e,collect) { body-of-loop }
#define FOR_EACH_REVERSE(Elem,Collection) \
for(std::pair<TYPEOF_RIT(Collection),bool> Elem##_IT(Collection.rbegin(), true) ; \
Elem##_IT.first != Collection.rend() ; \
++Elem##_IT.first, Elem##_IT.second = true) \
for(TYPEOF_REF(Collection) Elem = *Elem##_IT.first ; \
Elem##_IT.second ; \
Elem##_IT.second = false)
/// Iterate over two collection in parallel
/// Usage: FOR_EACH_2_T(TypeIt1,TypeElem1,e1,collect1,TypeIt2,TypeElem2,e2,collect2) { body-of-loop }
/** Note: This has got to be one of the craziest pieces of code I have ever written :)
* It is just an extension of the idea of FOR_EACH_T.
*/
#define FOR_EACH_2_T(TypeIt1,TypeElem1,Elem1,Coll1,TypeIt2,TypeElem2,Elem2,Coll2) \
for(std::pair<std::pair<TypeIt1,TypeIt2>, bool> \
Elem1##_IT(make_pair(Coll1.begin(), Coll2.begin()), true) ; \
Elem1##_IT.first.first != Coll1.end() && \
Elem1##_IT.first.second != Coll2.end() ; \
++Elem1##_IT.first.first, ++Elem1##_IT.first.second, \
Elem1##_IT.second = true) \
for(TypeElem1 Elem1 = *Elem1##_IT.first.first ; \
Elem1##_IT.second ; \
Elem1##_IT.second = false) \
for(TypeElem2 Elem2 = *Elem1##_IT.first.second ; \
Elem1##_IT.second ; \
Elem1##_IT.second = false)
/// Iterate over two collections in parallel,
/// their type must be declared with DECLARE_TYPEOF.
/// Usage: FOR_EACH_2(e1,collect1, e2,collect2) { body-of-loop }
#define FOR_EACH_2(Elem1,Collection1, Elem2,Collection2) \
FOR_EACH_2_T(TYPEOF_IT(Collection1), TYPEOF_REF(Collection1), Elem1, Collection1, \
TYPEOF_IT(Collection2), TYPEOF_REF(Collection2), Elem2, Collection2)
// ----------------------------------------------------------------------------- : EOF
#endif
+73
View File
@@ -0,0 +1,73 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_INDEX_MAP
#define HEADER_UTIL_INDEX_MAP
// ----------------------------------------------------------------------------- : Includes
#include <vector>
// ----------------------------------------------------------------------------- : IndexMap
/// A kind of map of K->V, with the following properties:
/** - K must have a unique member ->index of type UInt
* - There must exist a function initObject(K, V&)
* that stores a new V object for a given key in v
* - O(1) inserts and lookups
*/
template <typename Key, typename Value>
class IndexMap : private vector<Value> {
public:
using vector<Value>::empty;
using vector<Value>::size;
using vector<Value>::iterator;
using vector<Value>::begin;
using vector<Value>::end;
/// Initialize this map with default values given a list of keys, has no effect if !empty()
/** Requires a function
* void initObject(Key, Value&)
*/
void init(const vector<Key>& keys) {
if (!this->empty()) return;
this->reserve(keys.size());
FOR_EACH(it, keys) {
Key& k = *it;
if (k->index >= this->size()) this->resize(k->index + 1);
initObject(k, (*this)[k->index]);
}
}
/// Retrieve a value given its key
inline Value operator [] (const Key& key) {
assert(key);
assert(this->size() > key->index);
return at(key->index);
}
/// Is a value contained in this index map?
/// requires a function Key Value::getKey()
inline bool contains(const Value& value) const {
assert(value);
size_t index = value->getKey()->index;
return index < this.size() && (*this)[index] == value
}
/// Is a key in the domain of this index map?
/// requires a function Key Value::getKey()
inline bool containsKey(const Key& key) const {
assert(key);
return key->index < this.size() && (*this)[key->index]->getKey() == key
}
private:
using vector<Value>::operator [];
};
// ----------------------------------------------------------------------------- : EOF
#endif
+127
View File
@@ -0,0 +1,127 @@
//+----------------------------------------------------------------------------+
//| 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 "reader.hpp"
#include <util/vector2d.hpp>
#include <util/error.hpp>
// ----------------------------------------------------------------------------- : Reader
Reader::Reader(const InputStreamP& input, String filename)
: input(input), filename(filename), lineNumber(0)
, indent(0), expectedIndent(0), justOpened(false)
, stream(*input)
{
moveNext();
}
bool Reader::enterBlock(const Char* name) {
if (justOpened) moveNext(); // on the key of the parent block, first move inside it
if (indent != expectedIndent) return false; // not enough indentation
if (name == key) {
justOpened = true;
expectedIndent += 1; // the indent inside the block must be at least this much
return true;
} else {
return false;
}
}
void Reader::exitBlock() {
assert(expectedIndent > 0);
expectedIndent -= 1;
multiLineStr.clear();
if (justOpened) moveNext(); // leave this key
// Dump the remainder of the block
// TODO: issue warnings?
while (indent > expectedIndent) {
moveNext();
}
}
void Reader::moveNext() {
justOpened = false;
key.clear();
multiLineStr.clear();
indent = -1; // if no line is read it never has the expected indentation
// repeat until we have a good line
while (key.empty() && !input->Eof()) {
readLine();
}
// did we reach the end of the file?
if (key.empty() && input->Eof()) {
lineNumber += 1;
indent = -1;
}
}
void Reader::readLine() {
// fix UTF8 in ascii builds; skip BOM
line = decodeUTF8BOM(stream.ReadLine());
// read indentation
indent = 0;
while ((UInt)indent < line.size() && line.GetChar(indent) == _('\t')) {
indent += 1;
}
// read key / value
size_t pos = line.find_first_of(_(':'), indent);
key = trim(line.substr(indent, pos - indent));
value = pos == String::npos ? _("") : trimLeft(line.substr(pos+1));
// we read a line
lineNumber += 1;
// was it a comment?
if (!key.empty() && key.GetChar(0) == _('#')) {
key.clear();
}
}
// ----------------------------------------------------------------------------- : Handling basic types
template <> void Reader::handle(String& s) {
if (!multiLineStr.empty()) {
s = multiLineStr;
} else if (value.empty()) {
// a multiline string
bool first = true;
// read all lines that are indented enough
readLine();
while (indent >= expectedIndent) {
if (!first) value += '\n';
first = false;
multiLineStr += line.substr(expectedIndent); // strip expected indent
readLine();
}
// moveNext(), but without emptying multiLineStr
justOpened = false;
while (key.empty() && !input->Eof()) {
readLine();
}
s = multiLineStr;
} else {
s = value;
}
}
template <> void Reader::handle(int& i) {
long l = 0;
value.ToLong(&l);
i = l;
}
template <> void Reader::handle(double& d) {
value.ToDouble(&d);
}
template <> void Reader::handle(bool& b) {
b = (value==_("true") || value==_("1") || value==_("yes"));
}
// ----------------------------------------------------------------------------- : Handling less basic util types
template <> void Reader::handle(Vector2D& vec) {
if (!wxSscanf(value.c_str(), _("(%lf,%lf)"), &vec.x, &vec.y)) {
throw ParseError(_("Expected (x,y)"));
}
}
+165
View File
@@ -0,0 +1,165 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_IO_READER
#define HEADER_UTIL_IO_READER
// ----------------------------------------------------------------------------- : Includes
#include "../prec.hpp"
#include <wx/txtstrm.h>
// ----------------------------------------------------------------------------- : Reader
typedef wxInputStream InputStream;
typedef shared_ptr<wxInputStream> InputStreamP;
/// The Reader can be used for reading (deserializing) objects
/** This class makes use of the reflection functionality, in effect
* an object tells the Reader what fields it would like to read.
* The reader then sees if the requested field is currently available.
*
* The handle functions ensure that afterwards the reader is at the line after the
* object that was just read.
*/
class Reader {
public:
/// Construct a reader that reads from the given input stream
/** filename is used only for error messages
*/
Reader(const InputStreamP& input, String filename = _(""));
/// Construct a reader that reads a file in a package
Reader(String filename);
/// Tell the reflection code we are reading
inline bool reading() const { return true; }
// --------------------------------------------------- : Handling objects
/// Handle an object: read it if it's name matches
template <typename T>
void handle(const Char* name, T& object) {
if (enterBlock(name)) {
handle(object);
exitBlock();
}
}
/// Reads an object of type T from the input stream
template <typename T> void handle(T& object);
/// Reads a vector from the input stream
template <typename T> void handle(vector<T>& vector);
/// Reads a shared_ptr from the input stream
template <typename T> void handle(shared_ptr<T>& pointer);
/// Reads a map from the input stream
//template <typename K, typename V> void handle(map<K,V>& map);
private:
// --------------------------------------------------- : Data
/// The line we read
String line;
/// The key and value of the last line we read
String key, value;
/// A string spanning multiple lines
String multiLineStr;
/// Indentation of the last line we read
int indent;
/// Indentation of the block we are in
int expectedIndent;
/// Did we just open a block (i.e. not read any more lines of it)?
bool justOpened;
/// Filename for error messages
String filename;
/// Line number for error messages
UInt lineNumber;
/// Input stream we are reading from
InputStreamP input;
/// Text stream wrapping the input stream
wxTextInputStream stream;
// --------------------------------------------------- : Reading the stream
/// Is there a block with the given key under the current cursor?
bool enterBlock(const Char* name);
/// Leave the block we are in
void exitBlock();
/// Move to the next non empty line
void moveNext();
/// Reads the next line from the input, and stores it in line/key/value/indent
void readLine();
/// Issue a warning: "Unexpected key: $key"
void unexpected();
};
// ----------------------------------------------------------------------------- : Container types
template <typename T>
void Reader::handle(vector<T>& vector) {
String vectorKey = key;
while (key == vectorKey) { // TODO : check indent
moveNext(); // skip key
vector.resize(vector.size() + 1);
handle(vector.back());
}
}
template <typename T>
void Reader::handle(shared_ptr<T>& pointer) {
if (!pointer) pointer.reset(new T);
handle(*pointer);
}
// ----------------------------------------------------------------------------- : Reflection
/// Implement reflection as used by Reader
#define REFLECT_OBJECT_READER(Cls) \
template<> void Reader::handle<Cls>(Cls& object) { \
object.reflect(*this); \
}
// ----------------------------------------------------------------------------- : Reflection for enumerations
/// Implement enum reflection as used by Reader
#define REFLECT_ENUM_READER(Enum) \
template<> void Reader::handle<Enum>(Enum& enum_) { \
EnumReader reader(value); \
reflect_ ## Enum(enum_, reader); \
if (!reader.isDone()) { \
/* warning message */ \
} \
}
/// 'Tag' to be used when reflecting enumerations for Reader
class EnumReader {
public:
inline EnumReader(String read)
: read(read), first(true), done(false) {}
/// Handle a possible value for the enum, if the name matches the name in the input
template <typename Enum>
inline void handle(const Char* name, Enum value, Enum& enum_) {
if (!done && read == name) {
first = true;
enum_ = value;
} else if (first) {
first = false;
enum_ = value;
}
}
inline bool isDone() const { return done; }
private:
String read; //^ The string to match to a value name
bool first; //^ Has the first (default) value been matched?
bool done; //^ Was anything matched?
};
// ----------------------------------------------------------------------------- : EOF
#endif
+108
View File
@@ -0,0 +1,108 @@
//+----------------------------------------------------------------------------+
//| 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 "writer.hpp"
#include <util/vector2d.hpp>
#include <util/error.hpp>
// ----------------------------------------------------------------------------- : Writer
Writer::Writer(const OutputStreamP& output)
: output(output)
, stream(*output)
, indentation(0)
, justOpened(false)
{
stream.WriteString(BYTE_ORDER_MARK);
}
void Writer::enterBlock(const Char* name) {
// indenting into a sub-block?
if (justOpened) {
writeKey();
stream.WriteString(_(":\n"));
}
// don't write the key yet
indentation += 1;
openedKey = name;
justOpened = true;
}
void Writer::exitBlock() {
assert(indentation > 0);
indentation -= 1;
justOpened = false;
}
void Writer::writeKey() {
writeIndentation();
writeUTF8(stream, openedKey);
}
void Writer::writeIndentation() {
for(int i = 1 ; i < indentation ; ++i) {
stream.PutChar(_('\t'));
}
}
// ----------------------------------------------------------------------------- : Handling basic types
void Writer::handle(const String& value) {
if (!justOpened) {
throw InternalError(_("Can only write a value in a key that was just opened"));
}
// write indentation and key
writeKey();
writeUTF8(stream, _(": "));
if (value.find_first_of(_('\n')) != String::npos) {
// multiline string
stream.PutChar(_('\n'));
indentation += 1;
// split lines, and write each line
size_t start = 0, end, size = value.size();
while (start < size) {
end = value.find_first_of(_("\n\r"), start); // until end of line
// write the line
writeIndentation();
writeUTF8(stream, value.substr(start, end - start));
stream.PutChar(_('\n'));
// Skip \r and \n
if (end == String::npos) break;
start = end + 1;
if (start < size) {
Char c1 = value.GetChar(start - 1);
Char c2 = value.GetChar(start);
// skip second character of \r\n or \n\r
if (c1 != c2 && (c2 == _('\r') || c2 == _('\n'))) start += 1;
}
}
indentation -= 1;
} else {
writeUTF8(stream, value);
}
stream.PutChar(_('\n'));
justOpened = false;
}
template <> void Writer::handle(const int& value) {
handle(String() << value);
}
template <> void Writer::handle(const double& value) {
handle(String() << value);
}
template <> void Writer::handle(const bool& value) {
handle(value ? _("true") : _("false"));
}
// ----------------------------------------------------------------------------- : Handling less basic util types
template <> void Writer::handle(const Vector2D& vec) {
String formated;
formated.Printf(_("(%.10lf,%.10lf)"), vec.x, vec.y);
handle(formated);
}
+131
View File
@@ -0,0 +1,131 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_IO_WRITER
#define HEADER_UTIL_IO_WRITER
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <wx/txtstrm.h>
// ----------------------------------------------------------------------------- : Writer
typedef wxOutputStream OutputStream;
typedef shared_ptr<wxOutputStream> OutputStreamP;
/// The Writer can be used for writing (serializing) objects
class Writer {
public:
/// Construct a writer that writes to the given output stream
Writer(const OutputStreamP& output);
/// Tell the reflection code we are not reading
inline bool reading() const { return false; }
// --------------------------------------------------- : Handling objects
/// Handle an object: write it under the given name
template <typename T>
void handle(const Char* name, const T& object) {
enterBlock(name);
handle(object);
exitBlock();
}
/// Write a string to the output stream
void handle(const String& str);
void handle(const Char* str) { handle(String(str)); }
/// Write an object of type T to the output stream
template <typename T> void handle(const T& object);
/// Write a vector to the output stream
template <typename T> void handle(const vector<T>& vector);
/// Write a shared_ptr to the output stream
template <typename T> void handle(const shared_ptr<T>& pointer);
/// Write a map to the output stream
//template <typename K, typename V> void handle(map<K,V>& map);
private:
// --------------------------------------------------- : Data
/// Indentation of the current block
int indentation;
/// Did we just open a block (i.e. not written any lines of it)?
bool justOpened;
/// Last key opened
String openedKey;
/// Output stream we are writing to
OutputStreamP output;
/// Text stream wrapping the output stream
wxTextOutputStream stream;
// --------------------------------------------------- : Writing to the stream
/// Start a new block with the given name
void enterBlock(const Char* name);
/// Leave the block we are in
void exitBlock();
/// Write the openedKey and the required indentation
void writeKey();
/// Output some taps to represent the indentation level
void writeIndentation();
};
// ----------------------------------------------------------------------------- : Container types
template <typename T>
void Writer::handle(const vector<T>& vector) {
/*String vectorKey = key;
while (key == vectorKey) { // TODO : check indent
moveNext(); // skip key
vector.resize(vector.size() + 1);
handle(vector.back());
}*/
}
template <typename T>
void Writer::handle(const shared_ptr<T>& pointer) {
if (pointer) handle(*pointer);
}
// ----------------------------------------------------------------------------- : Reflection
/// Implement reflection as used by Writer
#define REFLECT_OBJECT_WRITER(Cls) \
template<> void Writer::handle<Cls>(const Cls& object) { \
const_cast<Cls&>(object).reflect(*this); \
}
// ----------------------------------------------------------------------------- : Reflection for enumerations
/// Implement enum reflection as used by Writer
#define REFLECT_ENUM_WRITER(Enum) \
template<> void Writer::handle<Enum>(const Enum& enum_) { \
EnumWriter writer(*this); \
reflect_ ## Enum(const_cast<Enum&>(enum_), writer); \
}
/// 'Tag' to be used when reflecting enumerations for Writer
class EnumWriter {
public:
inline EnumWriter(Writer& writer)
: writer(writer) {}
/// Handle a possible value for the enum, if the name matches the name in the input
template <typename Enum>
inline void handle(const Char* name, Enum value, Enum enum_) {
if (enum_ == value) {
writer.handle(name);
}
}
private:
Writer& writer; //^ The writer to write output to
};
// ----------------------------------------------------------------------------- : EOF
#endif
+68
View File
@@ -0,0 +1,68 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_PREC
#define HEADER_UTIL_PREC
/** @file util/prec.hpp
*
* Precompiled header, and aliasses for common types
*/
// ----------------------------------------------------------------------------- : Compiler specific
#ifdef _MSC_VER
# pragma warning (disable: 4100) // unreferenced formal parameter
# pragma warning (disable: 4800) // 'int' : forcing value to bool 'true' or 'false' (performance warning)
#endif
// ----------------------------------------------------------------------------- : Includes
// Wx headers
#include <wx/setup.h>
#include <wx/wxprec.h>
#include <wx/image.h>
#include <wx/datetime.h>
// Std headers
#include <vector>
#include <map>
#include <set>
using namespace std;
// MSE utility headers (ones unlikely to change and used everywhere)
#include "for_each.hpp"
#include "string.hpp"
#include "smart_ptr.hpp"
#include "index_map.hpp"
// ----------------------------------------------------------------------------- : Wx Aliasses
// Remove some of the wxUglyness
typedef wxPanel Panel;
typedef wxWindow Window;
typedef wxFrame Frame;
typedef wxBitmap Bitmap;
typedef wxImage Image;
typedef wxColour Color;
typedef wxDC DC;
typedef wxDateTime DateTime;
typedef wxOutputStream OutputStream;
// ----------------------------------------------------------------------------- : Other aliasses
typedef unsigned char Byte;
typedef unsigned int UInt;
/// Null pointer
#define nullptr 0
// ----------------------------------------------------------------------------- : EOF
#endif
+116
View File
@@ -0,0 +1,116 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_REAL_POINT
#define HEADER_UTIL_REAL_POINT
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/vector2d.hpp>
// ----------------------------------------------------------------------------- : Point using doubles
/// A point using real (double) coordinates
typedef Vector2D RealPoint;
// ----------------------------------------------------------------------------- : Size using doubles
/// A size (width,height) using real (double) coordinates
class RealSize {
public:
double width;
double height;
inline RealSize()
: width(0), height(0)
{}
inline RealSize(double w, double h)
: width(w), height(h)
{}
inline RealSize(wxSize s)
: width(s.GetWidth()), height(s.GetHeight())
{}
/// Addition of two sizes
inline void operator += (const RealSize& s2) {
width += s2.width;
height += s2.height;
}
/// Addition of two sizes
inline RealSize operator + (const RealSize& s2) const {
return RealSize(width + s2.width, height + s2.height);
}
/// Difference of two sizes
inline void operator -= (const RealSize& s2){
width -= s2.width;
height -= s2.height;
}
/// Difference of two sizes
inline RealSize operator - (const RealSize& s2) const {
return RealSize(width - s2.width, height - s2.height);
}
/// Inversion of a size, inverts both components
inline RealSize operator - () const {
return RealSize(-width, -height);
}
/// Multiplying a size by a scalar r, multiplies both components
inline void operator *= (double r) {
width *= r;
height *= r;
}
/// Multiplying a size by a scalar r, multiplies both components
inline RealSize operator * (double r) const {
return RealSize(width * r, height * r);
}
/// Dividing a size by a scalar r, divides both components
inline RealSize operator / (double r) const {
return RealSize(width / r, height / r);
}
/// Can be converted to a wxSize, with integer components
inline operator wxSize() {
return wxSize(realRound(width), realRound(height));
}
};
// ----------------------------------------------------------------------------- : Rectangle using doubles
/// A rectangle (postion and size) using real (double) coordinats
class RealRect {
public:
/// Position of the top left corner
RealPoint position;
/// Size of the rectangle
RealSize size;
inline RealRect(const RealPoint& position, const RealSize& size)
: position(position), size(size)
{}
inline RealRect(double x, double y, double w, double h)
: position(x,y), size(w,h)
{}
};
// ----------------------------------------------------------------------------- : Operators
inline RealPoint operator + (const RealSize& s, const RealPoint& p) {
return RealPoint(p.x + s.width, p.y + s.height);
}
inline RealPoint operator + (const RealPoint& p, const RealSize& s) {
return RealPoint(p.x + s.width, p.y + s.height);
}
inline RealPoint operator - (const RealPoint& p, const RealSize& s) {
return RealPoint(p.x - s.width, p.y - s.height);
}
inline void operator += (RealPoint& p, const RealSize& s) {
p.x += s.width;
p.y += s.height;
}
// ----------------------------------------------------------------------------- : EOF
#endif
+111
View File
@@ -0,0 +1,111 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_REFLECT
#define HEADER_UTIL_REFLECT
/** @file util/reflect.hpp
*
* Reflection of classes, currently reflection is used for (de)serialization.
*/
// ----------------------------------------------------------------------------- : Includes
#include "io/reader.hpp"
#include "io/writer.hpp"
// ----------------------------------------------------------------------------- : Declaring reflection
/// Declare that a class supports reflection
/// Reflection allows the member variables of a class to be inspected at runtime.
#define DECLARE_REFLECTION() \
protected: \
template<class Tag> void reflect_impl(Tag& tag); \
friend class Reader; \
friend class Writer; \
void reflect(Reader& reader); \
void reflect(Writer& writer)
/// Declare that a class supports reflection, which can be overridden in derived classes
#define DECLARE_REFLECTION_VIRTUAL() \
protected: \
template<class Tag> void reflect_impl(Tag& tag); \
friend class Reader; \
friend class Writer; \
virtual void reflect(Reader& reader); \
virtual void reflect(Writer& writer)
// ----------------------------------------------------------------------------- : Implementing reflection
/// Implement the refelection of a class type Cls
/// Reflection allows the member variables of a class to be inspected at runtime.
///
/// Currently creates the methods:
/// - Reader::handle(Cls&)
/// - Writer::handle(Cls&)
/** Usage:
* \begincode
* IMPLEMENT_REFLECTION(MyClass) {
* REFLECT(a_variable_in_my_class);
* REFLECT(another_variable_in_my_class);
* }
* \endcode
*/
#define IMPLEMENT_REFLECTION(Cls) \
REFLECT_OBJECT_READER(Cls) \
REFLECT_OBJECT_WRITER(Cls) \
/* Extra level, so it can be declared virtual */ \
void Cls::reflect(Reader& reader) { \
reflect_impl(reader); \
} \
void Cls::reflect(Writer& writer) { \
reflect_impl(writer); \
} \
template <class Tag> \
void Cls::reflect_impl(Tag& tag)
/// Reflect a variable
#define REFLECT(var) tag.handle(_(#var), var)
/// Reflect a variable under the given name
#define REFLECT_N(name, var) tag.handle(_(name), var)
/// Declare that the variables of a base class should also be reflected
#define REFLECT_BASE(Base) Base::reflect_impl(tag)
// ----------------------------------------------------------------------------- : Reflecting enums
/// Implement the refelection of a enumeration type Enum
/** Usage:
* \begincode
* IMPLEMENT_REFLECTION_ENUM(MyEnum) {
* VALUE(value_of_enum_1);
* VALUE(value_of_enum_2);
* }
* \endcode
*
* When reading the first value declared is the default value
*
* Currently creates the methods:
* - Reader::handle(Enum&
* - Writer::handle(const Enum&)
*/
#define IMPLEMENT_REFLECTION_ENUM(Enum) \
template <class Tag> \
void reflect_ ## Enum (Enum& enum_, Tag& tag); \
REFLECT_ENUM_READER(Enum) \
REFLECT_ENUM_WRITER(Enum) \
template <class Tag> \
void reflect_ ## Enum (Enum& enum_, Tag& tag)
/// Declare a possible value of an enum
#define VALUE(val) tag.handle(_(#val), val, enum_)
/// Declare a possible value of an enum under the given name
#define VALUE_N(name, val) tag.handle(_(name), val, enum_)
// ----------------------------------------------------------------------------- : EOF
#endif
+56
View File
@@ -0,0 +1,56 @@
//+----------------------------------------------------------------------------+
//| 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/rotation.hpp>
// ----------------------------------------------------------------------------- : Rotation
Rotation::Rotation(int angle, const RealRect& rect, double zoom)
: angle(angle)
, size(rect.size)
, origin(rect.position)
, zoom(zoom)
{
// set origin
if (revX()) origin.x += size.width;
if (revY()) origin.x += size.height;
}
RealPoint Rotation::tr(const RealPoint& p) const {
return tr(RealSize(p.x, p.y)) + origin; // TODO : optimize?
}
RealSize Rotation::tr(const RealSize& s) const {
if (sideways()) {
return RealSize(negX(s.height), negY(s.width)) * zoom;
} else {
return RealSize(negX(s.width), negY(s.height)) * zoom;
}
}
RealRect Rotation::tr(const RealRect& r) const {
return RealRect(tr(r.position), tr(r.size));
}
RealSize Rotation::trNoNeg(const RealSize& s) const {
if (sideways()) {
return RealSize(s.height, s.width) * zoom;
} else {
return RealSize(s.width, s.height) * zoom;
}
}
RealRect Rotation::trNoNeg(const RealRect& r) const {
throw "TODO";
}
RealPoint Rotation::trInv(const RealPoint& p) const {
RealPoint p2 = (p - origin) / zoom;
if (sideways()) {
return RealPoint(negY(p2.y), negX(p2.x));
} else {
return RealPoint(negX(p2.x), negY(p2.y));
}
}
+123
View File
@@ -0,0 +1,123 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_ROTATION
#define HEADER_UTIL_ROTATION
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/real_point.hpp>
#include <gfx/gfx.hpp>
// ----------------------------------------------------------------------------- : Rotation
/// An object that can rotate coordinates inside a specified rectangle
/** This class has lots of tr*** functions, they convert
* internal coordinates to external/screen coordinates.
* tr***inv do the opposite.
*/
class Rotation {
public:
/// Construct a rotation object with the given rectangle of external coordinates
/// and a given rotation angle and zoom factor
Rotation(int angle, const RealRect& rect, double zoom = 1.0);
/// Change the zoom factor
inline void setZoom(double z) { zoom = z; }
/// Change the angle
void setAngle(int a);
/// Translate a size or length
inline double trS(double s) const { return s * zoom; }
/// Translate a single point
RealPoint tr(const RealPoint& p) const;
/// Translate a single size, the result may be negative
RealSize tr(const RealSize& s) const;
/// Translate a rectangle, the size of the result may be negative
RealRect tr(const RealRect& r) const;
/// Translate a size, the result will never be negative
RealSize trNoNeg(const RealSize& s) const;
/// Translate a rectangle, the result will never have a negative size
RealRect trNoNeg(const RealRect& r) const;
/// Translate a size or length back to internal 'coordinates'
inline double trInvS(double s) const { return s / zoom; }
/// Translate a point back to internal coordinates
RealPoint trInv(const RealPoint& p) const;
private:
int angle; //^ The angle of rotation in degrees (counterclockwise)
RealSize size; //^ Size of the rectangle, in external coordinates
RealPoint origin; //^ tr(0,0)
double zoom; //^ Zoom factor, zoom = 2.0 means that 1 internal = 2 external
/// Is the rotation sideways (90 or 270 degrees)?
// Note: angle & 2 == 0 for angle in {0, 180} and != 0 for angle in {90, 270)
inline bool sideways() const { return (angle & 2) != 0; }
/// Is the x axis 'reversed' (after turning sideways)?
inline bool revX() const { return angle >= 180; }
/// Is the y axis 'reversed' (after turning sideways)?
inline bool revY() const { return angle == 90 || angle == 180; }
/// Negate if revX
inline double negX(double d) const { return revX() ? -d : d; }
/// Negate if revY
inline double negY(double d) const { return revY() ? -d : d; }
};
// ----------------------------------------------------------------------------- : Rotater
/// An object that changes a rotation RIIA style
/** Usage:
* \begincode
* Rotation a, b;
* Rotater(a,b);
* a.tr(x) // now acts as a.tr(b.tr(x))
* \endcode
*/
class Rotater {
/// Compose a rotation by onto the rotation rot
/** rot is restored when this object is destructed
*/
Rotater(Rotation& rot, const Rotation& by);
~Rotater();
};
// ----------------------------------------------------------------------------- : RotatedDC
/// A DC with rotation applied
/** All draw** functions take internal coordinates.
*/
class RotatedDC : public Rotation {
public:
RotatedDC(int angle, const RealRect& rect, double zoom = 1.0);
RotatedDC(const Rotation& rotation);
// ----------------------------- : Drawing
void DrawText (const String& text, const RealPoint& pos);
void DrawBitmap(const Bitmap& bitmap, const RealPoint& pos);
void DrawImage (const Image& image, const RealPoint& pos, ImageCombine combine = COMBINE_NORMAL);
void DrawLine (const RealPoint& p1, const RealPoint& p2);
void DrawRectangle(const RealRect& r);
void DrawRoundedRectangle(const RealRect& r, double radius);
// ----------------------------- : Forwarded properties
void SetPen(const wxPen&);
void SetBrush(const wxBrush&);
void SetTextForeground(const Color&);
void SetLogicalFunction(int function);
RealSize getTextExtent(const String& string);
};
// ----------------------------------------------------------------------------- : EOF
#endif
+62
View File
@@ -0,0 +1,62 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_SMART_PTR
#define HEADER_UTIL_SMART_PTR
/** @file util/shared_ptr.hpp
*
* Utilities related to boost smart pointers
*/
// ----------------------------------------------------------------------------- : Includes
#include <boost/shared_ptr.hpp>
using namespace boost;
// ----------------------------------------------------------------------------- : Declaring
/// Declares the type TypeP as a shared_ptr<Type>
#define DECLARE_POINTER_TYPE(Type) \
class Type; \
typedef shared_ptr<Type> Type##P
// ----------------------------------------------------------------------------- : Creating
/// Allocate a new shared-pointed object
template <typename T>
inline shared_ptr<T> new_shared() {
return shared_ptr<T>(new T());
}
/// Allocate a new shared-pointed object, given one argument to pass to the ctor of T
template <typename T, typename A0>
inline shared_ptr<T> new_shared1(const A0& a0) {
return shared_ptr<T>(new T(a0));
}
/// Allocate a new shared-pointed object, given two arguments to pass to the ctor of T
template <typename T, typename A0, typename A1>
inline shared_ptr<T> new_shared2(const A0& a0, const A1& a1) {
return shared_ptr<T>(new T(a0, a1));
}
/// Allocate a new shared-pointed object, given three arguments to pass to the ctor of T
template <typename T, typename A0, typename A1, typename A2>
inline shared_ptr<T> new_shared3(const A0& a0, const A1& a1, const A2& a2) {
return shared_ptr<T>(new T(a0, a1, a2));
}
/// Allocate a new shared-pointed object, given four arguments to pass to the ctor of T
template <typename T, typename A0, typename A1, typename A2, typename A3>
inline shared_ptr<T> new_shared4(const A0& a0, const A1& a1, const A2& a2, const A3& a3) {
return shared_ptr<T>(new T(a0, a1, a2, a3));
}
/// Allocate a new shared-pointed object, given seven arguments to pass to the ctor of T
template <typename T, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
inline shared_ptr<T> new_shared7(const A0& a0, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6) {
return shared_ptr<T>(new T(a0, a1, a2, a3, a4, a5, a6));
}
// ----------------------------------------------------------------------------- : EOF
#endif
+118
View File
@@ -0,0 +1,118 @@
//+----------------------------------------------------------------------------+
//| 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 "string.hpp"
#include "for_each.hpp"
#include <wx/txtstrm.h>
// ----------------------------------------------------------------------------- : Unicode
String decodeUTF8BOM(const String& s) {
#ifdef UNICODE
if (!s.empty() && s.GetChar(0) == L'\xFEFF') {
// skip byte-order-mark
return s.substr(1);
} else {
return s;
}
#else
wxWCharBuffer buf = s.wc_str(wxConvUTF8);
if (buf && buf[size_t(0)] == L'\xFEFF') {
// skip byte-order-mark
return String(buf + 1, *wxConvCurrent);
} else {
return String(buf, *wxConvCurrent);
}
#endif
}
void writeUTF8(wxTextOutputStream& stream, const String& str) {
#ifdef UNICODE
stream.WriteString(str);
#else
wxWCharBuffer buf = str.wc_str(*wxConvCurrent);
stream.WriteString(wxString(buf, wxConvUTF8));
#endif
}
// ----------------------------------------------------------------------------- : String utilities
String trim(const String& s){
size_t start = s.find_first_not_of(_(" \t"));
size_t end = s.find_last_not_of( _(" \t"));
if (start == String::npos) {
return String();
} else {
return s.substr(start, end - start + 1);
}
}
String trimLeft(const String& s) {
size_t start = s.find_first_not_of(_(' '));
if (start == String::npos) {
return String();
} else {
return s.substr(start);
}
}
// ----------------------------------------------------------------------------- : Words
String lastWord(const String& s) {
size_t endLastWord = s.find_last_not_of(_(' '));
size_t startLastWord = s.find_last_of( _(' '), endLastWord);
if (endLastWord == String::npos) {
return String(); // empty string
} else if (startLastWord == String::npos) {
return s.substr(0, endLastWord + 1);// first word
} else {
return s.substr(startLastWord + 1, endLastWord - startLastWord);
}
}
String stripLastWord(const String& s) {
size_t endLastWord = s.find_last_not_of(_(' '));
size_t startLastWord = s.find_last_of(_(' '), endLastWord);
if (endLastWord == String::npos || startLastWord == String::npos) {
return String(); // single word or empty string
} else {
return s.substr(0, startLastWord + 1);
}
}
// ----------------------------------------------------------------------------- : Caseing
/// Quick check to see if the substring starting at the given iterator is equal
/// to some given string
bool is_substr(const String& s, String::iterator it, const Char* cmp) {
while (it != s.end() && *cmp != 0) {
if (*it++ != *cmp++) return false;
}
return *cmp == 0;
}
String capitalize(const String& s) {
String result = s;
bool afterSpace = true;
FOR_EACH_IT(it, result) {
if (*it == ' ') {
afterSpace = true;
} else if (afterSpace) {
afterSpace = false;
if (it != s.begin() &&
(is_substr(result,it,_("is ")) || is_substr(result,it,_("the ")) ||
is_substr(result,it,_("in ")) || is_substr(result,it,_("of ")) ||
is_substr(result,it,_("to ")) || is_substr(result,it,_("at ")) ||
is_substr(result,it,_("a " )))) {
// Short words are not capitalized, keep lower case
} else {
*it = toUpper(*it);
}
}
}
return result;
}
+108
View File
@@ -0,0 +1,108 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_STRING
#define HEADER_UTIL_STRING
/** @file util/string.hpp
*
* String and character utility functions and macros
*/
// ----------------------------------------------------------------------------- : Includes
#include "prec.hpp"
#include "for_each.hpp"
#include <ctype.h>
#include <boost/preprocessor/cat.hpp>
class wxTextOutputStream;
// ----------------------------------------------------------------------------- : String type
/// The string type used throughout MSE
typedef wxString String;
DECLARE_TYPEOF_NO_REV(String); // iterating over characters in a string
// ----------------------------------------------------------------------------- : Unicode
/// u if UNICODE is defined, a otherwise
#ifdef UNICODE
# define IF_UNICODE(u,a) u
#else
# define IF_UNICODE(u,a) a
#endif
#undef _
/// A string/character constant, correctly handled in unicode builds
#define _(S) IF_UNICODE(BOOST_PP_CAT(L,S), S)
/// The character type used
typedef IF_UNICODE(wchar_t, char) Char;
/// Decode a UTF8 string
/** In non-unicode builds the input is considered to be an incorrectly encoded utf8 string.
* In unicode builds it is a normal string, utf8 already decoded.
* Also removes a byte-order-mark from the start of the string if it is pressent
*/
String decodeUTF8BOM(const String& s);
/// UTF8 Byte order mark for writing at the start of files
/** In non-unicode builds it is UTF8 encoded \xFEFF
* In unicode builds it is a normal \xFEFF
*/
const Char BYTE_ORDER_MARK[] = IF_UNICODE(L"\xFEFF", "\xEF\xBB\xBF");
/// Writes a string to an output stream, encoded as UTF8
void writeUTF8(wxTextOutputStream& stream, const String& str);
// ----------------------------------------------------------------------------- : Char functions
// Character set tests
inline bool isSpace(Char c) { return IF_UNICODE( iswspace(c) , isspace(c) ); }
inline bool isAlpha(Char c) { return IF_UNICODE( iswalpha(c) , isalpha(c) ); }
inline bool isDigit(Char c) { return IF_UNICODE( iswdigit(c) , isdigit(c) ); }
inline bool isAlnum(Char c) { return IF_UNICODE( iswalnum(c) , isalnum(c) ); }
inline bool isUpper(Char c) { return IF_UNICODE( iswupper(c) , isupper(c) ); }
inline bool isLower(Char c) { return IF_UNICODE( iswlower(c) , islower(c) ); }
// Character conversions
inline Char toUpper(Char c) { return IF_UNICODE( towupper(c) , toupper(c) ); }
inline Char toLower(Char c) { return IF_UNICODE( towlower(c) , tolower(c) ); }
// ----------------------------------------------------------------------------- : String utilities
/// Remove whitespace from both ends of a string
String trim(const String&);
/// Remove whitespace from the start of a string
String trimLeft(const String&);
// ----------------------------------------------------------------------------- : Words
/// Returns the last word in a string
String lastWord(const String&);
/// Remove the last word from a string, leaves whitespace before that word
String stripLastWord(const String&);
// ----------------------------------------------------------------------------- : Caseing
/// Make each word in a string start with an upper case character.
/// for use in menus
String capitalize(const String&);
/// Make the first word in a string start with an upper case character.
/// for use in dialogs
String capitalizeSentence(const String&);
/// Convert a field name to cannocial form: lower case and ' ' instead of '_'
/// non alphanumeric characters are ignored
/// "camalCase" is converted to words "camel case"
String cannocialNameForm(const String&);
// ----------------------------------------------------------------------------- : EOF
#endif
+141
View File
@@ -0,0 +1,141 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_VECTOR2D
#define HEADER_UTIL_VECTOR2D
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <limits>
// ----------------------------------------------------------------------------- : Rounding
// Rounding function for converting doubles to integers
inline int realRound(double d) {
return d > 0 ? d + 0.5 : d - 0.5;
}
// ----------------------------------------------------------------------------- : Vector2D
/// A simple 2d vector class
class Vector2D {
public:
/// Coordinates of this vector
double x, y;
/// Default contructor
inline Vector2D()
: x(0), y(0) {}
/// Contructor with given x and y values
inline Vector2D(double x, double y)
: x(x), y(y) {}
/// Addition of two vectors
inline Vector2D operator + (Vector2D p2) const {
return Vector2D(x + p2.x, y + p2.y);
}
/// Addition of two vectors
inline void operator += (Vector2D p2) {
x += p2.x; y += p2.y;
}
/// Subtract two vectors
inline Vector2D operator - (Vector2D p2) const {
return Vector2D(x - p2.x, y - p2.y);
}
/// Subtract two vectors
inline void operator -= (Vector2D p2) {
x -= p2.x; y -= p2.y;
}
/// Invert a vector
inline Vector2D operator - () const {
return Vector2D(-x, -y);
}
/// Multiply with a scalar
inline Vector2D operator * (double r) const {
return Vector2D(x * r, y * r);
}
/// Multiply with a scalar
inline void operator *= (double r) {
x *= r; y *= r;
}
/// Divide by a scalar
inline Vector2D operator / (double r) const {
return Vector2D(x / r, y / r);
}
/// Divide by a scalar
inline void operator /= (double r) {
x /= r; y /= r;
}
/// Inner product with another vector
inline double dot(Vector2D p2) const {
return (x * p2.x) + (y * p2.y);
}
/// Outer product with another vector
inline double cross(Vector2D p2) const {
return (x * p2.y) - (y * p2.x);
}
/// Piecewise multiplication
inline Vector2D mul(Vector2D p2) {
return Vector2D(x * p2.x, y * p2.y);
}
/// Piecewise division
inline Vector2D div(Vector2D p2) {
return Vector2D(x / p2.x, y / p2.y);
}
/// Apply a 'matrix' to this vector
inline Vector2D mul(Vector2D mx, Vector2D my) {
return Vector2D(dot(mx), dot(my));
}
/// Returns the square of the length of this vector
inline double lengthSqr() const {
return x*x + y*y;
}
/// Returns the length of this vector
inline double length() const {
return sqrt(lengthSqr());
}
/// Returns a normalized version of this vector
/// i.e. length() == 1
inline Vector2D normalized() const {
return *this / length();
}
inline operator wxPoint() const {
return wxPoint(realRound(x), realRound(y));
}
// Vector at infinity
static inline Vector2D infinity() {
double inf = numeric_limits<double>::infinity();
return Vector2D(inf, inf);
}
};
/// Piecewise minimum
inline Vector2D piecewise_min(Vector2D a, Vector2D b) {
return Vector2D(
a.x < b.x ? a.x : b.x,
a.y < b.y ? a.y : b.y
);
}
/// Piecewise maximum
inline Vector2D piecewise_max(Vector2D a, Vector2D b) {
return Vector2D(
a.x < b.x ? b.x : a.x,
a.y < b.y ? b.y : a.y
);
}
// ----------------------------------------------------------------------------- : EOF
#endif
+154
View File
@@ -0,0 +1,154 @@
//+----------------------------------------------------------------------------+
//| 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_UTIL_WINDOW_ID
#define HEADER_UTIL_WINDOW_ID
// ----------------------------------------------------------------------------- : Includes
// ----------------------------------------------------------------------------- : Menu ids
/// Window ids for menus and toolbars
enum MenuID {
ID_MENU_MIN = 0
, ID_MENU_MAX = 999
// File menu
, ID_FILE_NEW = wxID_NEW
, ID_FILE_OPEN = wxID_OPEN
, ID_FILE_SAVE = wxID_SAVE
, ID_FILE_SAVE_AS = wxID_SAVEAS
, ID_FILE_STORE = 1
, ID_FILE_EXIT = wxID_EXIT
, ID_FILE_EXPORT = 2
, ID_FILE_EXPORT_HTML = 3
, ID_FILE_EXPORT_IMAGE = 4
, ID_FILE_EXPORT_IMAGES = 5
, ID_FILE_EXPORT_APPR = 6
, ID_FILE_EXPORT_MWS = 7
, ID_FILE_PRINT = wxID_PRINT
, ID_FILE_PRINT_PREVIEW = wxID_PREVIEW
, ID_FILE_INSPECT = 8
, ID_FILE_RECENT = wxID_FILE1
, ID_FILE_RECENT_MAX = wxID_FILE9
// Edit menu
, ID_EDIT_UNDO = wxID_UNDO
, ID_EDIT_REDO = wxID_REDO
, ID_EDIT_CUT = wxID_CUT
, ID_EDIT_COPY = wxID_COPY
, ID_EDIT_PASTE = wxID_PASTE
, ID_EDIT_DELETE = 101
, ID_EDIT_DUPLICATE = 102
, ID_EDIT_FIND = wxID_FIND
, ID_EDIT_FIND_NEXT = 103
, ID_EDIT_REPLACE = wxID_REPLACE
, ID_EDIT_PREFERENCES = 104
// Window menu (MainWindow)
, ID_WINDOW_NEW = 201
, ID_WINDOW_MIN = 202
, ID_WINDOW_MAX = 220
// Help menu (MainWindow)
, ID_HELP_INDEX = 301
, ID_HELP_ABOUT
// Mode menu (SymbolWindow)
, ID_MODE_MIN = 401
, ID_MODE_SELECT = ID_MODE_MIN
, ID_MODE_ROTATE
, ID_MODE_POINTS
, ID_MODE_SHAPES
, ID_MODE_PAINT
, ID_MODE_MAX
};
// ----------------------------------------------------------------------------- : Child ids
/// Ids for menus on child panels (MainWindowPanel / SymbolEditorBase)
enum ChildMenuID {
ID_CHILD_MIN = 1000
, ID_CHILD_MAX = 2999
// Cards menu
, ID_CARD_ADD = 1001
, ID_CARD_ADD_MULT
, ID_CARD_REMOVE
, ID_CARD_PREV
, ID_CARD_NEXT
, ID_CARD_ROTATE
, ID_CARD_ROTATE_0
, ID_CARD_ROTATE_90
, ID_CARD_ROTATE_180
, ID_CARD_ROTATE_270
// Keyword menu
, ID_KEYWORD_ADD = 1101
, ID_KEYWORD_REMOVE
, ID_KEYWORD_PREV
, ID_KEYWORD_NEXT
// SymbolSelectEditor toolbar/menu
, ID_PART = 2001
, ID_PART_MERGE = ID_PART + PART_MERGE
, ID_PART_SUBTRACT = ID_PART + PART_SUBTRACT
, ID_PART_INTERSECTION = ID_PART + PART_INTERSECTION
, ID_PART_DIFFERENCE = ID_PART + PART_DIFFERENCE
, ID_PART_OVERLAP = ID_PART + PART_OVERLAP
, ID_PART_BORDER = ID_PART + PART_BORDER
, ID_PART_MAX
// SymbolPointEditor toolbar/menu
, ID_SEGMENT = 2101
, ID_SEGMENT_LINE = ID_SEGMENT + SEGMENT_LINE
, ID_SEGMENT_CURVE = ID_SEGMENT + SEGMENT_CURVE
, ID_SEGMENT_MAX
, ID_LOCK = 2151
, ID_LOCK_FREE = ID_LOCK + LOCK_FREE
, ID_LOCK_DIR = ID_LOCK + LOCK_DIR
, ID_LOCK_SIZE = ID_LOCK + LOCK_SIZE
, ID_LOCK_MAX
// SymbolBasicShapeEditor toolbar/menu
, ID_SHAPE = 2201
, ID_SHAPE_CIRCLE = ID_SHAPE
, ID_SHAPE_RECTANGLE
, ID_SHAPE_POLYGON
, ID_SHAPE_STAR
, ID_SHAPE_MAX
, ID_SIDES
};
// ----------------------------------------------------------------------------- : Control/window ids
/// Window ids for controls
enum ControlID {
ID_CONTROL_MIN = 6000
, ID_CONTROL_MAX = 6999
// Controls
, ID_VIEWER = 6001
, ID_EDITOR
, ID_CONTROL
, ID_CARD_LIST
, ID_PART_LIST
, ID_NOTES
, ID_KEYWORD
, ID_PARAMETER
, ID_SEPARATOR
, ID_REMINDER
, ID_RULES
};
// ----------------------------------------------------------------------------- : EOF
#endif