Commit 3ed3d772 authored by twanvl's avatar twanvl

initial checkin of C++ port (in progress)

parents
All or most of the source files in this distribution refer to this
file for copyright and warranty information. This file should be
included whenever those files are redistributed.
This software is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation. That license is reproduced
below.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
//+----------------------------------------------------------------------------+
//| 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]);
}
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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);
}
}
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_DATA_ACTION_SYMBOL_PART
#define HEADER_DATA_ACTION_SYMBOL_PART
/** @file data/action/symbol_part.hpp
*
* Actions operating on the insides of SymbolParts (ControlPoints and the like).
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/action_stack.hpp>
#include <data/symbol.hpp>
// ----------------------------------------------------------------------------- : Utility
/// Constrain a vector to be horizontal, vertical or diagonal
/// If constraint==false does nothing
Vector2D constrainVector(const Vector2D& v, bool constrain = true, bool onlyDiagonal = false);
// ----------------------------------------------------------------------------- : Move control point
/// Moving a control point in a symbol
class ControlPointMoveAction : public Action {
public:
ControlPointMoveAction(const set<ControlPointP>& points);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
set<ControlPointP> points; //^ Points to move
vector<Vector2D> oldValues; //^ Their old positions
Vector2D delta; //^ Amount we moved
public:
bool constrain; //^ Constrain movement?
};
// ----------------------------------------------------------------------------- : Move handle
/// Moving a handle(before/after) of a control point in a symbol
class HandleMoveAction : public Action {
public:
HandleMoveAction(const SelectedHandle& handle);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
SelectedHandle handle; //^ The handle to move
Vector2D oldHandle; //^ Old value of this handle
Vector2D oldOther; //^ Old value of other handle, needed for contraints
Vector2D delta; //^ Amount we moved
public:
bool constrain; //^ Constrain movement?
};
// ----------------------------------------------------------------------------- : Segment mode
/// Utility class to update a control point
class ControlPointUpdate {
public:
ControlPointUpdate(const ControlPointP& pnt);
/// Perform or undo an update on this control point
void perform();
/// Other value that is swapped with the current one.
/// Should be changed to make perform have an effect
ControlPoint other;
/// The point that is to be changed, should not be updated before perform()
ControlPointP point;
};
/// Changing a line to a curve and vice versa
class SegmentModeAction : public Action {
public:
SegmentModeAction(const ControlPointP& p1, const ControlPointP& p2, SegmentMode mode);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
protected:
ControlPointUpdate point1, point2;
};
// ----------------------------------------------------------------------------- : Locking mode
/// Locking a control point
class LockModeAction : public Action {
public:
LockModeAction(const ControlPointP& p, LockMode mode);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
private:
ControlPointUpdate point; //^ The affected point
};
// ----------------------------------------------------------------------------- : Move curve
/// Dragging a curve; also coverts lines to curves
/** Inherits from SegmentModeAction because it also has that effect
*/
class CurveDragAction : public SegmentModeAction {
public:
CurveDragAction(const ControlPointP& point1, const ControlPointP& point2);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
// Move the curve by this much, it is grabbed at time t
void move(const Vector2D& delta, double t);
};
// ----------------------------------------------------------------------------- : Add control point
/// Insert a new point in a symbol part
class ControlPointAddAction : public Action {
public:
/// Insert a new point in part, after position insertAfter_, at the time t on the segment
ControlPointAddAction(const SymbolPartP& part, UInt insertAfter, double t);
virtual String getName(bool toUndo) const;
virtual void perform(bool toUndo);
inline ControlPointP getNewPoint() const { return newPoint; }
private:
SymbolPartP part; //^ SymbolPart we are in
ControlPointP newPoint; //^ The point to insert
UInt insertAfter; //^ Insert after index .. in the array
ControlPointUpdate point1, point2; //^ Update the points around the new point
};
// ----------------------------------------------------------------------------- : Remove control point
/// Action that removes any number of points from a symbol part
/// TODO: If less then 3 points are left removes the entire part!
Action* controlPointRemoveAction(const SymbolPartP& part, const set<ControlPointP>& toDelete);
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <data/symbol.hpp>
#include <gfx/bezier.hpp>
// ----------------------------------------------------------------------------- : ControlPoint
IMPLEMENT_REFLECTION_ENUM(LockMode) {
VALUE_N("free", LOCK_FREE);
VALUE_N("direction", LOCK_DIR);
VALUE_N("size", LOCK_SIZE);
}
IMPLEMENT_REFLECTION_ENUM(SegmentMode) {
VALUE_N("line", SEGMENT_LINE);
VALUE_N("curve", SEGMENT_CURVE);
}
IMPLEMENT_REFLECTION(ControlPoint) {
REFLECT_N("position", pos);
REFLECT_N("lock", lock);
REFLECT_N("line after", segmentAfter);
if (tag.reading() || segmentBefore == SEGMENT_CURVE) {
REFLECT_N("handle before", deltaBefore);
}
if (tag.reading() || segmentAfter == SEGMENT_CURVE) {
REFLECT_N("handle after", deltaAfter);
}
}
ControlPoint::ControlPoint()
: segmentBefore(SEGMENT_LINE), segmentAfter(SEGMENT_LINE)
, lock(LOCK_FREE)
{}
ControlPoint::ControlPoint(double x, double y)
: segmentBefore(SEGMENT_LINE), segmentAfter(SEGMENT_LINE)
, lock(LOCK_FREE)
, pos(x,y)
{}
ControlPoint::ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock)
: segmentBefore(SEGMENT_CURVE), segmentAfter(SEGMENT_CURVE)
, lock(lock)
, pos(x,y)
, deltaBefore(xb,yb)
, deltaAfter(xa,ya)
{}
void ControlPoint::onUpdateHandle(WhichHandle wh) {
// One handle has changed, update only the other one
if (lock == LOCK_DIR) {
getOther(wh) = -getHandle(wh) * getOther(wh).length() / getHandle(wh).length();
} else if (lock == LOCK_SIZE) {
getOther(wh) = -getHandle(wh);
}
}
void ControlPoint::onUpdateLock() {
// The lock has changed, avarage the handle values
if (lock == LOCK_DIR) {
// deltaBefore = x * deltaAfter
Vector2D dir = (deltaBefore - deltaAfter).normalized();
deltaBefore = dir * deltaBefore.length();
deltaAfter = dir * -deltaAfter.length();
} else if (lock == LOCK_SIZE) {
// deltaBefore = -deltaAfter
deltaBefore = (deltaBefore - deltaAfter) * 0.5;
deltaAfter = -deltaBefore;
}
}
Vector2D& ControlPoint::getHandle(WhichHandle wh) {
if (wh == HANDLE_BEFORE) {
return deltaBefore;
} else {
assert(wh == HANDLE_AFTER);
return deltaAfter;
}
}
Vector2D& ControlPoint::getOther(WhichHandle wh) {
if (wh == HANDLE_BEFORE) {
return deltaAfter;
} else {
assert(wh == HANDLE_AFTER);
return deltaBefore;
}
}
// ----------------------------------------------------------------------------- : SymbolPart
IMPLEMENT_REFLECTION_ENUM(SymbolPartCombine) {
VALUE_N("overlap", PART_OVERLAP);
VALUE_N("merge", PART_MERGE);
VALUE_N("subtract", PART_SUBTRACT);
VALUE_N("intersection", PART_INTERSECTION);
VALUE_N("difference", PART_DIFFERENCE);
VALUE_N("border", PART_BORDER);
}
IMPLEMENT_REFLECTION(SymbolPart) {
REFLECT(name);
REFLECT(combine);
REFLECT_N("point", points);
// Fixes after reading
if (tag.reading()) {
// enforce constraints
enforceConstraints();
calculateBounds();
if (maxPos.x > 100 && maxPos.y > 100) {
// this is a <= 0.1.2 symbol, points range [0...500] instead of [0...1]
// adjust it
FOR_EACH(p, points) {
p->pos /= 500.0;
p->deltaBefore /= 500.0;
p->deltaAfter /= 500.0;
}
if (name.empty()) name = _("Shape");
calculateBounds();
}
}
}
SymbolPart::SymbolPart()
: combine(PART_OVERLAP), rotationCenter(.5, .5)
{}
SymbolPartP SymbolPart::clone() const {
SymbolPartP part = new_shared1<SymbolPart>(*this);
// also clone the control points
FOR_EACH(p, part->points) {
p = new_shared1<ControlPoint>(*p);
}
return part;
}
void SymbolPart::enforceConstraints() {
for (int i = 0 ; i < (int)points.size() ; ++i) {
ControlPointP p1 = getPoint(i);
ControlPointP p2 = getPoint(i + 1);
p2->segmentBefore = p1->segmentAfter;
p1->onUpdateLock();
}
}
void SymbolPart::calculateBounds() {
minPos = Vector2D::infinity();
maxPos = -Vector2D::infinity();
for (int i = 0 ; i < (int)points.size() ; ++i) {
segmentBounds(*getPoint(i), *getPoint(i + 1), minPos, maxPos);
}
}
// ----------------------------------------------------------------------------- : Symbol
IMPLEMENT_REFLECTION(Symbol) {
//%% version?
REFLECT_N("part", parts);
}
// ----------------------------------------------------------------------------- : SymbolView
SymbolView::SymbolView() {}
SymbolView::SymbolView(SymbolP symbol)
: symbol(symbol)
{
if (symbol) symbol->actions.addListener(this);
}
SymbolView::~SymbolView() {
if (symbol) symbol->actions.removeListener(this);
}
void SymbolView::setSymbol(SymbolP newSymbol) {
// no longer listening to old symbol
if (symbol) symbol->actions.removeListener(this);
symbol = newSymbol;
// start listening to new symbol
if (symbol) symbol->actions.addListener(this);
onSymbolChange();
}
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_DATA_SYMBOL
#define HEADER_DATA_SYMBOL
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/reflect.hpp>
#include <util/action_stack.hpp>
#include <util/vector2d.hpp>
DECLARE_POINTER_TYPE(ControlPoint);
DECLARE_POINTER_TYPE(SymbolPart);
DECLARE_POINTER_TYPE(Symbol);
DECLARE_TYPEOF_COLLECTION(ControlPointP);
DECLARE_TYPEOF_COLLECTION(SymbolPartP);
// ----------------------------------------------------------------------------- : ControlPoint
/// Mode of locking for control points in a bezier curve
/** Specificly: the relation between deltaBefore and deltaAfter
*/
enum LockMode
{ LOCK_FREE //^ no locking
, LOCK_DIR //^ deltaBefore = x * deltaAfter
, LOCK_SIZE //^ deltaBefore = -deltaAfter
};
/// Is the segment between two ControlPoints a line or a curve?
enum SegmentMode
{ SEGMENT_LINE
, SEGMENT_CURVE
};
/// To refer to a specific handle of a control point
enum WhichHandle
{ HANDLE_NONE = 0 //^ point is not selected
, HANDLE_MAIN
, HANDLE_BEFORE
, HANDLE_AFTER
};
/// A control point (corner) of a SymbolPart (polygon/bezier-gon)
class ControlPoint {
public:
Vector2D pos; //^ position of the control point itself
Vector2D deltaBefore; //^ delta to bezier control point, for curve before point
Vector2D deltaAfter; //^ delta to bezier control point, for curve after point
SegmentMode segmentBefore, segmentAfter;
LockMode lock;
/// Default constructor
ControlPoint();
/// Constructor for straight lines, takes only the position
ControlPoint(double x, double y);
/// Constructor for curves lines, takes postions, deltaBefore, deltaAfter and lock mode
ControlPoint(double x, double y, double xb, double yb, double xa, double ya, LockMode lock = LOCK_FREE);
/// Must be called after deltaBefore/deltaAfter has changed, enforces lock constraints
void onUpdateHandle(WhichHandle wh);
/// Must be called after lock has changed, enforces lock constraints
void onUpdateLock();
/// Get a handle of this control point
Vector2D& getHandle(WhichHandle wh);
/// Get a handle of this control point that is oposite wh
Vector2D& getOther(WhichHandle wh);
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Selected handles
/// A specific handle of a ControlPoint
class SelectedHandle {
public:
ControlPointP point; //^ the selected point
WhichHandle handle; //^ the selected handle of the point
// SelectedHandle
SelectedHandle() : handle(HANDLE_NONE) {}
SelectedHandle(const WhichHandle& handle) : handle(handle) { assert (handle == HANDLE_NONE); }
SelectedHandle(const ControlPointP& point, const WhichHandle& handle) : point(point), handle(handle) {}
inline Vector2D& getHandle() const { return point->getHandle(handle); }
inline Vector2D& getOther() const { return point->getOther (handle); }
inline void onUpdateHandle() const { return point->onUpdateHandle(handle); }
/*
bool operator == (const ControlPointP pnt) const;
bool SelectedHandle::operator == (const ControlPointP pnt) const { return point == pnt; }
bool operator == (const WhichHandle& wh) const;
bool SelectedHandle::operator == (const WhichHandle& wh) const { return handle == wh; }
bool operator ! () const;
bool SelectedHandle::operator ! () const { return handle == handleNone; }
*/
};
// ----------------------------------------------------------------------------- : SymbolPart
/// How are symbol parts combined with parts below it?
enum SymbolPartCombine
{ PART_MERGE
, PART_SUBTRACT
, PART_INTERSECTION
, PART_DIFFERENCE
, PART_OVERLAP
, PART_BORDER
};
/// A single part (polygon/bezier-gon) in a Symbol
class SymbolPart {
public:
/// The points of this polygon
vector<ControlPointP> points;
/// Name/label for this part
String name;
/// How is this part combined with parts below it?
SymbolPartCombine combine;
// Center of rotation, relative to the part, when the part is scaled to [0..1]
Vector2D rotationCenter;
/// Position and size of the part
/// this is the smallest axis aligned bounding box that fits around the part
Vector2D minPos, maxPos;
SymbolPart();
/// Create a clone of this symbol part
SymbolPartP clone() const;
/// Get a control point, wraps around
inline ControlPointP getPoint(int id) const {
return points[id >= 0 ? id % points.size() : id + points.size()];
}
/// Enforce lock constraints
void enforceConstraints();
/// Calculate the position and size of the part
void calculateBounds();
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Symbol
/// An editable symbol, consists of any number of SymbolParts
class Symbol {
public:
/// The parts of this symbol
vector<SymbolPartP> parts;
/// Actions performed on this symbol and the parts in it
ActionStack actions;
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : SymbolView
/// A 'view' of a symbol, is notified when the symbol is updated
class SymbolView : public ActionListener {
public:
SymbolView();
SymbolView(SymbolP symbol);
~SymbolView();
/// Get the symbol that is currently being viewed
inline SymbolP getSymbol() { return symbol; }
/// Change the symbol that is being viewed
void setSymbol(SymbolP symbol);
protected:
/// The symbol that is currently being viewed, should not be modified directly!
SymbolP symbol;
/// Called when the associated symbol is changed, but not when it is initially set!
virtual void onSymbolChange() {}
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gfx/bezier.hpp>
#include <gfx/polynomial.hpp>
// ----------------------------------------------------------------------------- : Evaluation
BezierCurve::BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) {
// calculate coefficients
c = (p1 - p0) * 3.0;
b = (p2 - p1) * 3.0 - c;
a = (p3 - p0) - c - b;
d = p0;
}
BezierCurve::BezierCurve(const ControlPoint& p0, const ControlPoint& p3) {
// calculate coefficients
c = p0.deltaAfter * 3.0;
b = (p3.pos + p3.deltaBefore - p0.pos - p0.deltaAfter) * 3.0 - c;
a = (p3.pos - p0.pos) - c - b;
d = p0.pos;
}
void deCasteljau(Vector2D a1, Vector2D a2, Vector2D a3, Vector2D a4,
Vector2D& b2, Vector2D& b3, Vector2D& b4c1,
Vector2D& c2, Vector2D& c3, double t)
{
b2 = a1 + (a2 - a1) * t;
Vector2D mid23 = a2 + (a3 - a2) * t;
c3 = a3 + (a4 - a3) * t;
b3 = b2 + (mid23 - b2) * t;
c2 = mid23 + (c3 - mid23) * t;
b4c1 = b3 + (c2 - b3) * t;
}
void deCasteljau(ControlPoint& a, ControlPoint& b, double t, ControlPoint& mid) {
deCasteljau(a.pos, a.deltaAfter, b.deltaBefore, b.pos, t, mid);
}
void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2D& a4, double t, ControlPoint& out) {
Vector2D half21 = a21 * t;
Vector2D half34 = a34 * (1-t);
Vector2D mid23 = (a1 + a21) * (1-t) + (a34 + a4) * t;
Vector2D mid23h21 = (a1 + half21) * (1-t) + mid23 * t;
Vector2D mid23h34 = (a4 + half34) * t + mid23 * (1-t);
out.pos = mid23h21 * (1-t) + mid23h34 * t;
out.deltaBefore = mid23h21 - out.pos;
out.deltaAfter = mid23h34 - out.pos;
a21 = half21;
a34 = half34;
}
// ----------------------------------------------------------------------------- : Drawing
void curveSubdivide(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, const Rotation& rot, vector<wxPoint>& out, UInt level) {
if (level <= 0) return;
double midtime = (t0+t1) * 0.5f;
Vector2D midpoint = c.pointAt(midtime);
Vector2D d0 = p0 - midpoint;
Vector2D d1 = midpoint - p1;
// Determine treshold for subdivision, greater angle -> subdivide
// greater size -> subdivide
double treshold = fabs( atan2(d0.x,d0.y) - atan2(d1.x,d1.y)) * (p0-p1).lengthSqr();
bool subdivide = treshold >= .0001;
// subdivide left
curveSubdivide(c, p0, midpoint, t0, midtime, rot, out, level - 1);
// add midpoint
if (subdivide) {
out.push_back(rot.tr(midpoint));
}
// subdivide right
curveSubdivide(c, midpoint, p1, midtime, t1, rot, out, level - 1);
}
void segmentSubdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out) {
assert(p0.segmentAfter == p1.segmentBefore);
// always the start
out.push_back(rot.tr(p0.pos));
if (p0.segmentAfter == SEGMENT_CURVE) {
// need more points?
BezierCurve curve(p0,p1);
curveSubdivide(curve, p0.pos, p1.pos, 0, 1, rot, out, 5);
}
}
// ----------------------------------------------------------------------------- : Bounds
void segmentBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max) {
assert(p1.segmentAfter == p2.segmentBefore);
if (p1.segmentAfter == SEGMENT_LINE) {
lineBounds (p1.pos, p2.pos, min, max);
} else {
bezierBounds(p1, p2, min, max);
}
}
void bezierBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max) {
assert(p1.segmentAfter == SEGMENT_CURVE);
// First of all, the corners should be in the bounding box
pointBounds(p1.pos, min, max);
pointBounds(p2.pos, min, max);
// Solve the derivative of the bezier curve to find its extremes
// It's only a quadtratic equation :)
BezierCurve curve(p1,p2);
double roots[4];
UInt count;
count = solveQuadratic(3*curve.a.x, 2*curve.b.x, curve.c.x, roots);
count += solveQuadratic(3*curve.a.y, 2*curve.b.y, curve.c.y, roots + count);
// now check them for min/max
for (UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >=0 && t <= 1) {
pointBounds(curve.pointAt(t), min, max);
}
}
}
void lineBounds(const Vector2D& p1, const Vector2D& p2, Vector2D& min, Vector2D& max) {
pointBounds(p1, min, max);
pointBounds(p2, min, max);
}
void pointBounds(const Vector2D& p, Vector2D& min, Vector2D& max) {
min = piecewise_min(min, p);
max = piecewise_max(max, p);
}
// Is a point inside the bounds <min...max>?
bool pointInBounds(const Vector2D& p, const Vector2D& min, const Vector2D& max) {
return p.x >= min.x && p.y >= min.y &&
p.x <= max.x && p.y <= max.y;
}
// ----------------------------------------------------------------------------- : Point tests
// As a point inside a symbol part?
bool pointInPart(const Vector2D& pos, const SymbolPart& part) {
// Step 1. compare bounding box of the part
if (!pointInBounds(pos, part.minPos, part.maxPos)) return false;
// Step 2. trace ray outward, count intersections
int count = 0;
size_t size = part.points.size();
for(size_t i = 0 ; i < size ; ++i) {
ControlPointP p1 = part.getPoint((int) i);
ControlPointP p2 = part.getPoint((int) i + 1);
if (p1->segmentAfter == SEGMENT_LINE) {
count += intersectLineRay (p1->pos, p2->pos, pos);
} else {
count += intersectBezierRay(*p1, *p2, pos);
}
}
return count & 1; // odd number of intersections
}
// ----------------------------------------------------------------------------- : Finding points
bool posOnSegment(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
if (p1.segmentAfter == SEGMENT_CURVE) {
return posOnBezier(pos, range, p1, p2, pOut, tOut);
} else {
return posOnLine (pos, range, p1.pos, p2.pos, pOut, tOut);
}
}
bool posOnBezier(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut) {
assert(p1.segmentAfter == SEGMENT_CURVE);
// Find intersections with the horizontal and vertical lines through p0
// theoretically we would need to check in all directions, but this covers enough
BezierCurve curve(p1, p2);
double roots[6];
UInt count;
count = solveCubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
count += solveCubic(curve.a.x, curve.b.x, curve.c.x, curve.d.x - pos.x, roots + count); // append intersections
// take the best intersection point
double bestDistSqr = std::numeric_limits<double>::max(); //infinity
for(UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >= 0 && t < 1) {
Vector2D pnt = curve.pointAt(t);
double distSqr = (pnt - pos).lengthSqr();
if (distSqr < bestDistSqr) {
bestDistSqr = distSqr;
pOut = pnt;
tOut = t;
}
}
}
return bestDistSqr <= range * range;
}
bool posOnLine(const Vector2D& pos, double range, const Vector2D& p1, const Vector2D& p2, Vector2D& pOut, double& t) {
Vector2D p21 = p2 - p1;
double p21len = p21.lengthSqr();
if (p21len < 0.00001) return false; // line is too short
t = p21.dot(pos - p1) / p21len; // 'time' on line p1->p2
if (t < 0 || t > 1) return false; // outside segment
pOut = p1 + p21 * t; // point on line
Vector2D dist = pOut - pos; // distance to line
return dist.lengthSqr() <= range * range; // in range?
}
// ----------------------------------------------------------------------------- : Intersection
UInt intersectBezierRay(const ControlPoint& p1, const ControlPoint& p2, const Vector2D& pos) {
// Looking only at the y coordinate
// we can use the cubic formula to find roots, points where the horizontal line
// through pos intersects the (extended) curve
BezierCurve curve(p1,p2);
double roots[3];
UInt count = solveCubic(curve.a.y, curve.b.y, curve.c.y, curve.d.y - pos.y, roots);
// now check if the solutions are left of pos.x
UInt solsInRange = 0;
for(UInt i = 0 ; i < count ; ++i) {
double t = roots[i];
if (t >= 0 && t < 1 && curve.pointAt(t).x < pos.x) {
solsInRange += 1;
}
}
return solsInRange;
}
bool intersectLineRay(const Vector2D& p1, const Vector2D& p2, const Vector2D& pos) {
// Vector2D intersection = p1 + t * (p2 - p1)
// intersection.y == pos.y
// == p1.y + t * (p2.y - p1.y)
// => t == (pos.y - p1.y) / (p2.y - p1.y)
// intersection.x == p1.x + t * (p2.x - p1.x)
// == p1.x + (pos.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y)
double dy = p2.y - p1.y;
if (fabs(dy) < 0.0000000001) {
// horizontal line
return (p1.x > pos.x || p2.x > pos.x) && // starts to the left of pos
fabs(p1.y - pos.y) < 0.0000000001; // same y as pos
} else {
double dx = p2.x - p1.x;
double t = (pos.y - p1.y) / dy;
if (t < 0.0 || t >= 1.0) return false;
double intersectX = p1.x + t * dx;
return intersectX <= pos.x; // intersection is left of pos
}
}
\ No newline at end of file
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GFX_BEZIER
#define HEADER_GFX_BEZIER
/** @file gfx/bezier.hpp
*
* Bezier curve and line related functions
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/rotation.hpp>
#include <data/symbol.hpp>
// ----------------------------------------------------------------------------- : Evaluation
/// A bezier curve for evaluation
class BezierCurve {
public:
/// coefficients of the equation (x,y) = at^3 + bt^2 + ct + d
Vector2D a, b, c, d;
/// Construct a bezier curve evaluator given the 4 handles
BezierCurve(const Vector2D& p0, const Vector2D& p1, const Vector2D& p2, const Vector2D& p3);
/// Construct a bezier curve evaluator given two ControlPoints at the ends
BezierCurve(const ControlPoint& p0, const ControlPoint& p3);
/// Return the point on this curve at time t in [0...1)
inline Vector2D pointAt(double t) const {
return d + (c + (b + a * t) * t) * t;
}
/// Return the tangent on this curve at time t in [0...1)
inline Vector2D tangentAt(double t) const {
return c + ((b * 2) + (a * 3) * t) * t;
}
};
/// Subdivide a curve from a to b, store the result in a control point
/** Also modifies the handles of the points to accomodate the inserted point
* Direct version, using input curve a1,a2,a3,a4 and output curves a1,b2,b3,b4 and c1,c2,c3,a4
*/
void deCasteljau(Vector2D a1, Vector2D a2, Vector2D a3, Vector2D a4,
Vector2D& b2, Vector2D& b3, Vector2D& b4c1,
Vector2D& c2, Vector2D& c3, double t);
/// Subdivide a curve from a to b at time t
/** Stores the point at time t in mid, updates the handles of a and b!
*/
void deCasteljau(ControlPoint& a, ControlPoint& b, double t, ControlPoint& mid);
/// Subdivide a curve from a to b, store the result in a control point
/** Also modifies the handles of the points to accomodate the inserted point!
*/
void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2D& a4, double t, ControlPoint& out);
// ----------------------------------------------------------------------------- : Drawing
/// Devide a segment into a number of straight lines for display purposes
/** Adds the resulting corner points of those lines to out, the last point is not added.
* All points are converted to display coordinates using rot.tr
*/
void segmentSubdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out);
// ----------------------------------------------------------------------------- : Bounds
/// Find a bounding box that fits a segment (either a line or a bezier curve) between p1 and p2.
/** stores the results in min and max.
* min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger.
*/
void segmentBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max);
/// Find a bounding box that fits a curve between p1 and p2, stores the results in min and max.
/** min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger
*/
void bezierBounds(const ControlPoint& p1, const ControlPoint& p2, Vector2D& min, Vector2D& max);
/// Find a bounding box that fits around p1 and p2, stores the result in min and max
/** min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger
*/
void lineBounds(const Vector2D& p1, const Vector2D& p2, Vector2D& min, Vector2D& max);
/// Find a bounding 'box' that fits around a single point
/** min is only changed if the minimum is smaller then the current value in min,
* max only if the maximum is larger
*/
void pointBounds(const Vector2D& p, Vector2D& min, Vector2D& max);
// ----------------------------------------------------------------------------- : Point tests
/// Is a point inside the given symbol part?
bool pointInPart(const Vector2D& p, const SymbolPart& part);
// ----------------------------------------------------------------------------- : Finding points
/// Finds the position of p0 on the line between p1 and p2, returns true if the point is on (or near) the line
/// the line between p1 and p2 can also be a bezier curve
/** Returns the time on the segment in tOut, and the point on the segment in pOut
*/
bool posOnSegment(const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut);
/// Finds the position of p0 on the line between p1 and p2, returns true if the point is on (or near)
/// the bezier curve between p1 and p2
bool posOnBezier (const Vector2D& pos, double range, const ControlPoint& p1, const ControlPoint& p2, Vector2D& pOut, double& tOut);
/// Finds the position of p0 on the line p1-p2, returns true if the point is withing range of the line
/// if that is the case then (x,y) = p1 + (p2-p1) * out
bool posOnLine (const Vector2D& pos, double range, const Vector2D& p1, const Vector2D& p2, Vector2D& pOut, double& tOut);
// ----------------------------------------------------------------------------- : Intersection
/// Counts the number of intersections between the ray/halfline from (-inf, pos.y) to pos
/// and the bezier curve between p1 and p2.
UInt intersectBezierRay(const ControlPoint& p1, const ControlPoint& p2, const Vector2D& pos);
// Does the line between p1 and p2 intersect the ray (half line) from (-inf, pos.y) to pos?
bool intersectLineRay(const Vector2D& p1, const Vector2D& p2, const Vector2D& pos);
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include "../util/prec.hpp"
#include "../util/reflect.hpp"
#include "gfx.hpp"
#include <algorithm>
using namespace std;
// ----------------------------------------------------------------------------- : Reflection for combining modes
IMPLEMENT_REFLECTION_ENUM(ImageCombine) {
VALUE_N("normal", COMBINE_NORMAL);
VALUE_N("add", COMBINE_ADD);
VALUE_N("subtract", COMBINE_SUBTRACT);
VALUE_N("stamp", COMBINE_STAMP);
VALUE_N("difference", COMBINE_DIFFERENCE);
VALUE_N("negation", COMBINE_NEGATION);
VALUE_N("multiply", COMBINE_MULTIPLY);
VALUE_N("darken", COMBINE_DARKEN);
VALUE_N("lighten", COMBINE_LIGHTEN);
VALUE_N("color dodge", COMBINE_COLOR_DODGE);
VALUE_N("color burn", COMBINE_COLOR_BURN);
VALUE_N("screen", COMBINE_SCREEN);
VALUE_N("overlay", COMBINE_OVERLAY);
VALUE_N("hard light", COMBINE_HARD_LIGHT);
VALUE_N("soft light", COMBINE_SOFT_LIGHT);
VALUE_N("reflect", COMBINE_REFLECT);
VALUE_N("glow", COMBINE_GLOW);
VALUE_N("freeze", COMBINE_FREEZE);
VALUE_N("heat", COMBINE_HEAT);
VALUE_N("and", COMBINE_AND);
VALUE_N("or", COMBINE_OR);
VALUE_N("xor", COMBINE_XOR);
VALUE_N("shadow", COMBINE_SHADOW);
}
// ----------------------------------------------------------------------------- : Combining functions
// Functor for combining functions for a given combining type
template <ImageCombine combine> struct Combine {
static inline int f(int a, int b);
};
// Give a combining function for enum value 'combine'
#define COMBINE_FUN(combine,fun) \
template <> int Combine<combine>::f(int a, int b) { return fun; }
// Based on
// http://www.pegtop.net/delphi/articles/blendmodes/
COMBINE_FUN(COMBINE_NORMAL, b )
COMBINE_FUN(COMBINE_ADD, top(a + b) )
COMBINE_FUN(COMBINE_SUBTRACT, bot(a - b) )
COMBINE_FUN(COMBINE_STAMP, col(a - 2 * b + 256) )
COMBINE_FUN(COMBINE_DIFFERENCE, abs(a - b) )
COMBINE_FUN(COMBINE_NEGATION, 255 - abs(255 - a - b) )
COMBINE_FUN(COMBINE_MULTIPLY, (a * b) / 255 )
COMBINE_FUN(COMBINE_DARKEN, min(a, b) )
COMBINE_FUN(COMBINE_LIGHTEN, max(a, b) )
COMBINE_FUN(COMBINE_COLOR_DODGE,b == 255 ? 255 : top(a * 255 / (255 - b)) )
COMBINE_FUN(COMBINE_COLOR_BURN, b == 0 ? 0 : bot(255 - (255-a) * 255 / b) )
COMBINE_FUN(COMBINE_SCREEN, 255 - (((255 - a) * (255 - b)) / 255) )
COMBINE_FUN(COMBINE_OVERLAY, a < 128
? (a * b) >> 7
: 255 - (((255 - a) * (255 - b)) >> 7) )
COMBINE_FUN(COMBINE_HARD_LIGHT, b < 128
? (a * b) >> 7
: 255 - (((255 - a) * (255 - b)) >> 7) )
COMBINE_FUN(COMBINE_SOFT_LIGHT, b)
COMBINE_FUN(COMBINE_REFLECT, b == 255 ? 255 : top(a * a / (255 - b)) )
COMBINE_FUN(COMBINE_GLOW, a == 255 ? 255 : top(b * b / (255 - a)) )
COMBINE_FUN(COMBINE_FREEZE, b == 0 ? 0 : bot(255 - (255 - a) * (255 - a) / b) )
COMBINE_FUN(COMBINE_HEAT, a == 0 ? 0 : bot(255 - (255 - b) * (255 - b) / a) )
COMBINE_FUN(COMBINE_AND, a & b )
COMBINE_FUN(COMBINE_OR, a | b )
COMBINE_FUN(COMBINE_XOR, a ^ b )
COMBINE_FUN(COMBINE_SHADOW, (b * a * a) / (255 * 255) )
// ----------------------------------------------------------------------------- : Combining
/// Combine image b onto image a using some combining mode.
/// The results are stored in the image A.
template <ImageCombine combine>
void combineImageDo(Image& a, Image b) {
UInt size = a.GetWidth() * a.GetHeight() * 3;
Byte *dataA = a.GetData(), *dataB = b.GetData();
// for each pixel: apply function
for (UInt i = 0 ; i < size ; ++i) {
dataA[i] = Combine<combine>::f(dataA[i], dataB[i]);
}
}
void combineImage(Image& a, const Image& b, ImageCombine combine) {
// Images must have same size
assert(a.GetWidth() == b.GetWidth());
assert(a.GetHeight() == b.GetHeight());
// Copy alpha channel?
if (b.HasAlpha()) {
if (!a.HasAlpha()) a.InitAlpha();
memcpy(a.GetAlpha(), b.GetAlpha(), a.GetWidth() * a.GetHeight());
}
// Combine image data, by dispatching to combineImageDo
switch(combine) {
#define DISPATCH(comb) case comb: combineImageDo<comb>(a,b); return
DISPATCH(COMBINE_NORMAL);
DISPATCH(COMBINE_ADD);
DISPATCH(COMBINE_SUBTRACT);
DISPATCH(COMBINE_STAMP);
DISPATCH(COMBINE_DIFFERENCE);
DISPATCH(COMBINE_NEGATION);
DISPATCH(COMBINE_MULTIPLY);
DISPATCH(COMBINE_DARKEN);
DISPATCH(COMBINE_LIGHTEN);
DISPATCH(COMBINE_COLOR_DODGE);
DISPATCH(COMBINE_COLOR_BURN);
DISPATCH(COMBINE_SCREEN);
DISPATCH(COMBINE_OVERLAY);
DISPATCH(COMBINE_HARD_LIGHT);
DISPATCH(COMBINE_SOFT_LIGHT);
DISPATCH(COMBINE_REFLECT);
DISPATCH(COMBINE_GLOW);
DISPATCH(COMBINE_FREEZE);
DISPATCH(COMBINE_HEAT);
DISPATCH(COMBINE_AND);
DISPATCH(COMBINE_OR);
DISPATCH(COMBINE_XOR);
DISPATCH(COMBINE_SHADOW);
}
}
void drawCombineImage(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine) {
}
\ No newline at end of file
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GFX_GFX
#define HEADER_GFX_GFX
/** @file gfx/gfx.hpp
*
* Graphics/image processing functions.
*/
// ----------------------------------------------------------------------------- : Includes
#include "../util/prec.hpp"
// ----------------------------------------------------------------------------- : Resampling
/// Resample (resize) an image, uses bilenear filtering
/** The algorithm first resizes in horizontally, then vertically,
* the two passes are essentially the same:
* - for each row:
* - each input pixel becomes a fixed amount of output (in 1<<shift fixed point math)
* - for each output pixel:
* - 'eat' input pixels until the total is 1<<shift
* - write the total to the output pixel
* - to ensure the sum of all the pixel amounts is exacly width<<shift an extra rest amount
* is 'eaten' from the first pixel
*
* Uses fixed point numbers internally
*/
void resample(const Image& imgIn, Image& imgOut);
/// Resamples an image, first clips the input image to a specified rectangle,
/// that rectangle is resampledinto the entire output image
void resample_and_clip(const Image& imgIn, Image& imgOut, wxRect rect);
// ----------------------------------------------------------------------------- : Image rotation
/// Rotates an image counter clockwise
/// angle must be a multiple of 90, i.e. {0,90,180,270}
Image rotateImageBy(const Image& image, int angle);
// ----------------------------------------------------------------------------- : Blending
/// Blends two images together, using a horizontal gradient
/** The result is stored in img1
* To the left the color is that of img1, to the right of img2
*/
void hblend(Image& img1, const Image& img2);
/// Blends two images together, using a vertical gradient
void vblend(Image& img1, const Image& img2);
/// Blends two images together, using a third image as a mask
/** The result is stored in img1
* mask is used as a mask, white pixels are taken from img1, black pixels from img2
* color channels are blended separatly
*/
void maskBlend(Image& img1, const Image& img2, const Image& mask);
// ----------------------------------------------------------------------------- : Combining
/// Ways in which images can be combined, similair to what Photoshop supports
enum ImageCombine
{ COMBINE_NORMAL
, COMBINE_ADD
, COMBINE_SUBTRACT
, COMBINE_STAMP
, COMBINE_DIFFERENCE
, COMBINE_NEGATION
, COMBINE_MULTIPLY
, COMBINE_DARKEN
, COMBINE_LIGHTEN
, COMBINE_COLOR_DODGE
, COMBINE_COLOR_BURN
, COMBINE_SCREEN
, COMBINE_OVERLAY
, COMBINE_HARD_LIGHT
, COMBINE_SOFT_LIGHT
, COMBINE_REFLECT
, COMBINE_GLOW
, COMBINE_FREEZE
, COMBINE_HEAT
, COMBINE_AND
, COMBINE_OR
, COMBINE_XOR
, COMBINE_SHADOW
};
/// Combine image b onto image a using some combining function.
/// The results are stored in the image A.
/// This image gets the alpha channel from B, it should then be
/// drawn onto the area where A originated.
void combineImage(Image& a, const Image& b, ImageCombine combine);
/// Draw an image to a DC using a combining function
void drawCombineImage(DC& dc, UInt x, UInt y, const Image& img, ImageCombine combine);
// ----------------------------------------------------------------------------- : Utility
inline int bot(int x) { return max(0, x); } //^ bottom range check for color values
inline int top(int x) { return min(255, x); } //^ top range check for color values
inline int col(int x) { return top(bot(x)); } //^ top and bottom range check for color values
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gfx/polynomial.hpp>
#include <complex>
// ----------------------------------------------------------------------------- : Solving
UInt solveLinear(double a, double b, double* root) {
if (a == 0) {
if (b == 0) {
root[0] = 0;
return 1;
} else {
return 0;
}
} else {
root[0] = -b / a;
return 1;
}
}
UInt solveQuadratic(double a, double b, double c, double* roots) {
if (a == 0) {
return solveLinear(b, c, roots);
} else {
double d = b*b - 4*a*c;
if (d < 0) return 0;
roots[0] = (-b - sqrt(d)) / (2*a);
roots[1] = (-b + sqrt(d)) / (2*a);
return 2;
}
}
UInt solveCubic(double a, double b, double c, double d, double* roots) {
if (a == 0) {
return solveQuadratic(b, c, d, roots);
} else {
return solveCubic(b/a, c/a, d/a, roots);
}
}
// cubic root
template <typename T>
inline T curt(T x) { return pow(x, 1.0 / 3); }
UInt solveCubic(double a, double b, double c, double* roots) {
double p = b - a*a / 3;
double q = c + (2 * a*a*a - 9 * a * b) / 27;
if (p == 0 && q == 0) {
roots[0] = -a / 3;
return 1;
}
complex<double> u;
if (q > 0) {
u = curt(q/2 + sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
} else {
u = curt(q/2 - sqrt(complex<double>(q*q / 4 + p*p*p / 27)));
}
// now for the complex part
// rot1(1, 0)
complex<double> rot2(-0.5, sqrt(3.0) / 2);
complex<double> rot3(-0.5, -sqrt(3.0) / 2);
complex<double> x1 = p / (3.0 * u) - u - a / 3.0;
complex<double> x2 = p / (3.0 * u * rot2) - u * rot2 - a / 3.0;
complex<double> x3 = p / (3.0 * u * rot3) - u * rot3 - a / 3.0;
// check if the solutions are real
UInt count = 0;
if (abs(x1.imag()) < 0.00001) {
roots[count] = x1.real();
count += 1;
}
if (abs(x2.imag()) < 0.00001) {
roots[count] = x2.real();
count += 1;
}
if (abs(x3.imag()) < 0.00001) {
roots[count] = x3.real();
count += 1;
}
return count;
}
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GFX_POLYNOMIAL
#define HEADER_GFX_POLYNOMIAL
/** @file gfx/polynomial.hpp
*
* Solutions to polynomials, used by bezier curve algorithms
*/
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
// ----------------------------------------------------------------------------- : Solving
/// Solve a linear equation a x + b = 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
*/
UInt solveLinear(double a, double b, double* root);
/// Solve a quadratic equation a x^2 + b x + c == 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
*/
UInt solveQuadratic(double a, double b, double c, double* roots);
// Solve a cubic equation a x^3 + b x^2 + c x + d == 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
*/
UInt solveCubic(double a, double b, double c, double d, double* roots);
// Solve a cubic equation x^3 + a x^2 + b x + c == 0
/** Returns the number of real roots, and the roots themselfs in the output parameter.
* Based on http://en.wikipedia.org/wiki/Cubic_equation
*/
UInt solveCubic(double a, double b, double c, double* roots);
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include "../util/prec.hpp"
#include "gfx.hpp"
// ----------------------------------------------------------------------------- : Implementation
// Rotates an image
// 'Rotater' is a function object that knows how to 'rotate' a pixel coordinate
template <class Rotater>
Image rotateImageImpl(Image img) {
UInt width = img.GetWidth(), height = img.GetHeight();
// initialize the return image
Image ret;
Rotater::init(ret, width, height);
Byte* in = img.GetData(), *out = ret.GetData();
// rotate each pixel
for (UInt y = 0 ; y < height ; ++y) {
for (UInt x = 0 ; x < width ; ++x) {
memcpy(out + 3 * Rotater::offset(x, y, width, height), in, 3);
in += 3;
}
}
// don't forget alpha
if (img.HasAlpha()) {
ret.InitAlpha();
in = img.GetAlpha();
out = ret.GetAlpha();
for (UInt y = 0 ; y < height ; ++y) {
for (UInt x = 0 ; x < width ; ++x) {
out[Rotater::offset(x, y, width, height)] = *in;
in += 1;
}
}
}
// ret is rotated image
return ret;
}
// ----------------------------------------------------------------------------- : Rotations
// Function object to handle rotation
struct Rotate90 {
/// Init a rotated image, where the source is w * h pixels
inline static void init(Image& img, UInt w, UInt h) {
img.Create(h, w, false);
}
/// Offset in the target data, x, y, w, h are SOURCE coordintes
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
int mx = y;
int my = w - x - 1;
return h * my + mx; // note: h, since that is the width of the target image
}
};
struct Rotate180 {
inline static void init(Image& img, UInt w, UInt h) {
img.Create(w, h, false);
}
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
UInt mx = w - x - 1;
UInt my = h - y - 1;
return w * my + mx;
}
};
struct Rotate270 {
inline static void init(Image& img, UInt w, UInt h) {
img.Create(h, w, false);
}
inline static int offset(UInt x, UInt y, UInt w, UInt h) {
UInt mx = h - y - 1;
UInt my = x;
return h * my + mx;
}
};
// ----------------------------------------------------------------------------- : Interface
Image rotateImageBy(const Image& image, int angle) {
if (angle == 90) {
return rotateImageImpl<Rotate90>(image);
} else if (angle == 180){
return rotateImageImpl<Rotate180>(image);
} else if (angle == 270){
return rotateImageImpl<Rotate270>(image);
} else{
return image;
}
}
//+----------------------------------------------------------------------------+
//| 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;
}
}
}
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_BASIC_SHAPE_EDITOR
#define HEADER_GUI_SYMBOL_BASIC_SHAPE_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <gui/symbol/editor.hpp>
class wxSpinCtrl;
// ----------------------------------------------------------------------------- : SymbolBasicShapeEditor
/// Editor for drawing basic shapes such as rectangles and polygons
class SymbolBasicShapeEditor : public SymbolEditorBase {
public:
SymbolBasicShapeEditor(SymbolControl* control);
// --------------------------------------------------- : Drawing
virtual void draw(DC& dc);
// --------------------------------------------------- : UI
virtual void initUI (wxToolBar* tb, wxMenuBar* mb);
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb);
virtual void onUpdateUI(wxUpdateUIEvent& e);
virtual void onCommand(int id);
virtual int modeToolId();
// --------------------------------------------------- : Mouse events
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev);
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev);
virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
// --------------------------------------------------- : Other events
virtual void onKeyChange(wxKeyEvent& ev);
virtual bool isEditing();
// --------------------------------------------------- : Data
private:
int mode;
SymbolPartP shape;
Vector2D start;
Vector2D end;
bool drawing;
// controls
wxSpinCtrl* sides;
wxStaticText* sidesL;
/// Cancel the drawing
void stopActions();
/// Make the shape
/** when centered: a = center, b-a = radius
* otherwise: a = top left, b = bottom right
*/
void makeShape(const Vector2D& a, const Vector2D& b, bool constrained, bool centered);
/// Make the shape, centered in c, with radius r
void makeCenteredShape(const Vector2D& c, Vector2D r, bool constrained);
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/control.hpp>
#include <gui/symbol/window.hpp>
#include <gui/symbol/editor.hpp>
#include <gui/symbol/select_editor.hpp>
#include <gui/symbol/point_editor.hpp>
#include <gui/symbol/basic_shape_editor.hpp>
#include <gui/util.hpp>
#include <data/action/symbol.hpp>
#include <util/window_id.hpp>
#include <wx/dcbuffer.h>
// ----------------------------------------------------------------------------- : SymbolControl
SymbolControl::SymbolControl(SymbolWindow* parent, int id, const SymbolP& symbol)
: wxControl(parent, id)
, SymbolViewer(symbol)
, parent(parent)
{
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
}
void SymbolControl::switchEditor(const SymbolEditorBaseP& e) {
if (editor) editor->destroyUI(parent->GetToolBar(), parent->GetMenuBar());
editor = e;
if (editor) editor->initUI (parent->GetToolBar(), parent->GetMenuBar());
Refresh(false);
}
void SymbolControl::onSymbolChange() {
selectedParts.clear();
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
Refresh(false);
}
void SymbolControl::onModeChange(wxCommandEvent& ev) {
switch (ev.GetId()) {
case ID_MODE_SELECT:
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
break;
case ID_MODE_ROTATE:
switchEditor(new_shared2<SymbolSelectEditor>(this, true));
break;
case ID_MODE_POINTS:
if (selectedParts.size() == 1) {
singleSelection = *selectedParts.begin();
switchEditor(new_shared2<SymbolPointEditor>(this, singleSelection));
}
break;
case ID_MODE_SHAPES:
if (!selectedParts.empty()) {
selectedParts.clear();
signalSelectionChange();
}
switchEditor(new_shared1<SymbolBasicShapeEditor>(this));
break;
}
}
void SymbolControl::onExtraTool(wxCommandEvent& ev) {
if (editor) editor->onCommand(ev.GetId());
}
void SymbolControl::onAction(const Action& action) {
TYPE_CASE_(action, SymbolPartAction) {
Refresh(false);
}
}
void SymbolControl::onUpdateSelection() {
switch(editor->modeToolId()) {
case ID_MODE_POINTS:
// can only select a single part!
if (selectedParts.size() > 1) {
SymbolPartP part = *selectedParts.begin();
selectedParts.clear();
selectedParts.insert(part);
signalSelectionChange();
} else if (selectedParts.empty()) {
selectedParts.insert(singleSelection);
signalSelectionChange();
}
if (singleSelection != *selectedParts.begin()) {
// begin editing another part
singleSelection = *selectedParts.begin();
editor = new_shared2<SymbolPointEditor>(this, singleSelection);
Refresh(false);
}
break;
case ID_MODE_SHAPES:
if (!selectedParts.empty()) {
// there can't be a selection
selectedParts.clear();
signalSelectionChange();
}
break;
default:
Refresh(false);
break;
}
}
void SymbolControl::selectPart(const SymbolPartP& part) {
selectedParts.clear();
selectedParts.insert(part);
switchEditor(new_shared2<SymbolSelectEditor>(this, false));
signalSelectionChange();
}
void SymbolControl::activatePart(const SymbolPartP& part) {
selectedParts.clear();
selectedParts.insert(part);
switchEditor(new_shared2<SymbolPointEditor>(this, part));
}
void SymbolControl::signalSelectionChange() {
parent->onSelectFromControl();
}
bool SymbolControl::isEditing() {
return editor && editor->isEditing();
}
// ----------------------------------------------------------------------------- : Drawing
void SymbolControl::draw(DC& dc) {
// clear the background
clearDC(dc, Color(0, 128, 0));
// draw symbol iself
SymbolViewer::draw(dc);
// draw editing overlay
if (editor) {
editor->draw(dc);
}
}
void SymbolControl::onPaint(wxPaintEvent& e) {
wxBufferedPaintDC dc(this);
dc.BeginDrawing();
draw(dc);
dc.EndDrawing();
}
// ----------------------------------------------------------------------------- : Events
// Mouse events, convert position, forward event
void SymbolControl::onLeftDown(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onLeftDown(pos, ev);
lastPos = pos;
ev.Skip(); // for focus
}
void SymbolControl::onLeftUp(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onLeftUp(pos, ev);
lastPos = pos;
}
void SymbolControl::onLeftDClick(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onLeftDClick(pos, ev);
lastPos = pos;
}
void SymbolControl::onRightDown(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
if (editor) editor->onRightDown(pos, ev);
lastPos = pos;
}
void SymbolControl::onMotion(wxMouseEvent& ev) {
Vector2D pos = rotation.trInv(RealPoint(ev.GetX(), ev.GetY()));
// Dragging something?
if (ev.LeftIsDown()) {
if (editor) editor->onMouseDrag(lastPos, pos, ev);
} else {
if (editor) editor->onMouseMove(lastPos, pos, ev);
}
lastPos = pos;
}
// Key events, just forward
void SymbolControl::onKeyChange(wxKeyEvent& ev) {
if (editor) editor->onKeyChange(ev);
ev.Skip(); // so we get char events
}
void SymbolControl::onChar(wxKeyEvent& ev) {
if (editor) editor->onChar(ev);
else ev.Skip();
}
void SymbolControl::onSize(wxSizeEvent& ev) {
wxSize s = ev.GetSize();
rotation.setZoom(min(s.GetWidth(), s.GetHeight()));
Refresh(false);
}
void SymbolControl::onUpdateUI(wxUpdateUIEvent& ev) {
if (!editor) return;
switch (ev.GetId()) {
case ID_MODE_SELECT: case ID_MODE_ROTATE: case ID_MODE_POINTS: case ID_MODE_SHAPES: //case ID_MODE_PAINT:
ev.Check(editor->modeToolId() == ev.GetId());
if (ev.GetId() == ID_MODE_POINTS) {
// can only edit points when a single part is selected <TODO?>
ev.Enable(selectedParts.size() == 1);
}
break;
case ID_MODE_PAINT:
ev.Enable(false); // TODO
break;
default:
if (ev.GetId() >= ID_CHILD_MIN && ev.GetId() < ID_CHILD_MAX) {
editor->onUpdateUI(ev); // foward to editor
}
}
}
// ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(SymbolControl, wxControl)
EVT_PAINT (SymbolControl::onPaint)
EVT_SIZE (SymbolControl::onSize)
EVT_LEFT_UP (SymbolControl::onLeftUp)
EVT_LEFT_DOWN (SymbolControl::onLeftDown)
EVT_RIGHT_DOWN (SymbolControl::onRightDown)
EVT_LEFT_DCLICK (SymbolControl::onLeftDClick)
EVT_MOTION (SymbolControl::onMotion)
EVT_KEY_UP (SymbolControl::onKeyChange)
EVT_KEY_DOWN (SymbolControl::onKeyChange)
EVT_CHAR (SymbolControl::onChar)
END_EVENT_TABLE ()
\ No newline at end of file
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_CONTROL
#define HEADER_GUI_SYMBOL_CONTROL
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <gui/symbol/viewer.hpp>
class SymbolWindow;
DECLARE_POINTER_TYPE(SymbolEditorBase);
// ----------------------------------------------------------------------------- : SymbolControl
/// Control for editing symbols
/** What kind of editing is done is determined by the contained SymbolEditorBase object
* That object handles all events and the drawing. This class is mostly just a proxy.
*/
class SymbolControl : public wxControl, public SymbolViewer {
public:
SymbolControl(SymbolWindow* parent, int id, const SymbolP& symbol);
virtual void onSymbolChange();
virtual void onAction(const Action&);
// Forward command to editor
void onExtraTool(wxCommandEvent& ev);
// Switch to some editing mode
void onModeChange(wxCommandEvent& ev);
/// Handle UpdateUIEvents propagated from the SymbolWindow
/** Handles events for editing mode related stuff
*/
void onUpdateUI(wxUpdateUIEvent& ev);
/// The selection has changed, tell the part list
void signalSelectionChange();
/// Activate a part, open it in the point editor
void activatePart(const SymbolPartP& part);
/// Select a specific part from the symbol
/// The editor is switched to the select editor
void selectPart(const SymbolPartP& part);
/// Update the selection
void onUpdateSelection();
/// Are we editing?
bool isEditing();
private:
/// Switch the a different editor object
void switchEditor(const SymbolEditorBaseP& e);
/// Draw the editor
void draw(DC& dc);
private:
DECLARE_EVENT_TABLE();
// --------------------------------------------------- : Data
public:
/// What parts are selected
set<SymbolPartP> selectedParts;
SymbolPartP singleSelection;
/// Parent window
SymbolWindow* parent;
private:
/// The current editor
SymbolEditorBaseP editor;
/// Last mouse position
Vector2D lastPos;
// --------------------------------------------------- : Events
void onLeftDown (wxMouseEvent& ev);
void onLeftUp (wxMouseEvent& ev);
void onLeftDClick(wxMouseEvent& ev);
void onRightDown (wxMouseEvent& ev);
void onMotion (wxMouseEvent& ev);
void onPaint (wxPaintEvent& e);
void onKeyChange(wxKeyEvent& ev);
void onChar (wxKeyEvent& ev);
void onSize (wxSizeEvent& ev);
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/editor.hpp>
#include <gui/symbol/window.hpp>
// ----------------------------------------------------------------------------- : SymbolEditorBase
void SymbolEditorBase::SetStatusText(const String& text) {
control.parent->SetStatusText(text);
}
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_EDITOR
#define HEADER_GUI_SYMBOL_EDITOR
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <gui/symbol/control.hpp>
class SymbolControl;
// ----------------------------------------------------------------------------- : SymbolEditorBase
/// Base class for editors of symbols.
/** A symbol editor is like a FieldEditor, events are forwarded to it.
* Differrent SymbolEditors represent different tools.
* NOTE : Do not confuse with SymbolEditor (a FieldEditor)
*/
class SymbolEditorBase {
protected:
/// The control for which we are editing
SymbolControl& control;
inline SymbolP getSymbol() { return control.getSymbol(); }
void SetStatusText(const String& text);
public:
SymbolEditorBase(SymbolControl* control)
: control(*control)
{}
virtual ~SymbolEditorBase() {};
// --------------------------------------------------- : Drawing
/// Drawing for this control,
virtual void draw(DC& dc) = 0;
// --------------------------------------------------- : UI
/// Init extra toolbar items and menus needed for this panel
virtual void initUI(wxToolBar* tb, wxMenuBar* mb) {}
/// Destroy the extra items added by initUI
virtual void destroyUI(wxToolBar* tb, wxMenuBar* mb) {}
/// Update the UI by enabling/disabling items
virtual void onUpdateUI(wxUpdateUIEvent& ev) {}
/// Respond to one of the extra menu/tool items
virtual void onCommand(int id) {}
/// Tool id used in the symbol window
virtual int modeToolId() = 0;
// --------------------------------------------------- : Mouse events
/// The left mouse button has been pressed, at the given position (internal coordinates)
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {}
/// The left mouse button has been released, at the given position (internal coordinates)
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {}
/// The left mouse button has been double clicked, at the given position (internal coordinates)
virtual void onLeftDClick (const Vector2D& pos, wxMouseEvent& ev) {}
/// The right mouse button has been pressed, at the given position (internal coordinates)
virtual void onRightDown (const Vector2D& pos, wxMouseEvent& ev) {}
/// The mouse is being moved, no mouse buttons are pressed
virtual void onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {}
/// The mouse is being moved while mouse buttons are being pressed
virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {}
// --------------------------------------------------- : Keyboard events
/// A key is pressed or released, should be used for modifier keys (Shift/Ctrl/Alt)
virtual void onKeyChange (wxKeyEvent& ev) {}
/// A key is pressed/clicked
virtual void onChar (wxKeyEvent& ev) {}
// --------------------------------------------------- : Other events
/// A context menu is requested
virtual void onContextMenu(wxContextMenuEvent& ev) {}
/// Is the user currently editing, i.e. dragging the mouse?
/** This disables undo/redo, so the current action is not
* undone while it is in progress.
*/
virtual bool isEditing() { return false; }
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <gui/symbol/part_list.hpp>
#include <gui/util.hpp>
#include <data/action/symbol.hpp>
#include <wx/imaglist.h>
// ----------------------------------------------------------------------------- : Constructor
SymbolPartList::SymbolPartList(Window* parent, int id, SymbolP symbol)
: wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize,
wxLC_REPORT | wxLC_NO_HEADER | wxLC_VIRTUAL | wxLC_EDIT_LABELS)
, SymbolView(symbol)
{
// Create image list
wxImageList* images = new wxImageList(16,16);
// NOTE: this is based on the order of the SymbolPartCombine enum!
images->Add(loadResourceImage(_("COMBINE_OR")));
images->Add(loadResourceImage(_("COMBINE_SUB")));
images->Add(loadResourceImage(_("COMBINE_AND")));
images->Add(loadResourceImage(_("COMBINE_XOR")));
images->Add(loadResourceImage(_("COMBINE_OVER")));
images->Add(loadResourceImage(_("COMBINE_BORDER")));
AssignImageList(images, wxIMAGE_LIST_SMALL);
// create columns
InsertColumn(0, _("Name"));
update();
}
// ----------------------------------------------------------------------------- : View events
void SymbolPartList::onSymbolChange() {
update();
}
void SymbolPartList::onAction(const Action& action) {
TYPE_CASE(action, ReorderSymbolPartsAction) {
if (selected == (long) action.partId1) {
selectItem((long) action.partId2);
} else if (selected == (long) action.partId2) {
selectItem((long) action.partId1);
}
}
TYPE_CASE_(action, SymbolPartListAction) {
update();
}
}
// ----------------------------------------------------------------------------- : Other
String SymbolPartList::OnGetItemText(long item, long col) const {
assert(col == 0);
return getPart(item)->name;
}
int SymbolPartList::OnGetItemImage(long item) const {
return getPart(item)->combine;
}
SymbolPartP SymbolPartList::getPart(long item) const {
return symbol->parts.at(item);
}
void SymbolPartList::selectItem(long item) {
selected = (long)item;
long count = GetItemCount();
for (long i = 0 ; i < count ; ++i) {
SetItemState(i, i == selected ? wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED : 0,
wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
}
}
void SymbolPartList::getSelectedParts(set<SymbolPartP>& sel) {
sel.clear();
long count = GetItemCount();
for (long i = 0 ; i < count ; ++ i) {
bool selected = GetItemState(i, wxLIST_STATE_SELECTED);
if (selected) {
sel.insert(symbol->parts.at(i));
}
}
}
void SymbolPartList::selectParts(const set<SymbolPartP>& sel) {
long count = GetItemCount();
for (long i = 0 ; i < count ; ++ i) {
// is that part selected?
bool selected = sel.find(symbol->parts.at(i)) != sel.end();
SetItemState(i, selected ? wxLIST_STATE_SELECTED : 0,
wxLIST_STATE_SELECTED);
}
}
void SymbolPartList::update() {
if (symbol->parts.empty()) {
// deleting all items requires a full refresh on win32
SetItemCount(0);
Refresh(true);
} else {
SetItemCount((long) symbol->parts.size() );
Refresh(false);
}
}
// ----------------------------------------------------------------------------- : Event handling
void SymbolPartList::onSelect(wxListEvent& ev) {
selected = ev.GetIndex();
ev.Skip();
}
void SymbolPartList::onDeselect(wxListEvent& ev) {
selected = -1;
ev.Skip();
}
void SymbolPartList::onLabelEdit(wxListEvent& ev){
symbol->actions.add(
new SymbolPartNameAction(getPart(ev.GetIndex()), ev.GetLabel())
);
}
void SymbolPartList::onSize(wxSizeEvent& ev) {
wxSize s = GetClientSize();
SetColumnWidth(0, s.GetWidth() - 2);
}
void SymbolPartList::onDrag(wxMouseEvent& ev) {
if (!ev.Dragging() || selected == -1) return;
// reorder the list of parts
int flags;
long item = HitTest(ev.GetPosition(), flags);
if (flags & wxLIST_HITTEST_ONITEM) {
if (item > 0) EnsureVisible(item - 1);
if (item < GetItemCount() - 1) EnsureVisible(item + 1);
if (item != selected) {
// swap the two items
symbol->actions.add(new ReorderSymbolPartsAction(*symbol, item, selected));
selectItem(item); // deselect all other items, to prevent 'lassoing' them
}
}
}
// ----------------------------------------------------------------------------- : Event table
BEGIN_EVENT_TABLE(SymbolPartList, wxListCtrl)
EVT_LIST_ITEM_SELECTED (wxID_ANY, SymbolPartList::onSelect)
EVT_LIST_ITEM_DESELECTED (wxID_ANY, SymbolPartList::onDeselect)
EVT_LIST_END_LABEL_EDIT (wxID_ANY, SymbolPartList::onLabelEdit)
EVT_SIZE ( SymbolPartList::onSize)
EVT_MOTION ( SymbolPartList::onDrag)
END_EVENT_TABLE ()
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_PART_LIST
#define HEADER_GUI_SYMBOL_PART_LIST
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <wx/listctrl.h>
// ----------------------------------------------------------------------------- : SymbolPartList
// A list view of parts of a symbol
class SymbolPartList : public wxListCtrl, public SymbolView {
public:
SymbolPartList(Window* parent, int id, SymbolP symbol = SymbolP());
/// Update the list
void update();
/// Is there a selection?
inline bool hasSelection() const { return selected != -1; }
/// Return the last part that was selected
/** @pre hasSelection()
*/
inline SymbolPartP getSelection() const { return getPart(selected); }
/// Get a set of selected parts
void getSelectedParts(set<SymbolPartP>& sel);
/// Select the specified parts, and nothing else
void selectParts(const set<SymbolPartP>& sel);
/// Another symbol is being viewed
void onSymbolChange();
/// Event handler for changes to the symbol
virtual void onAction(const Action& a);
protected:
/// Get the text of an item
virtual String OnGetItemText(long item, long col) const;
/// Get the icon of an item
virtual int OnGetItemImage(long item) const;
private:
/// The selected item, or -1 if there is no selection
long selected;
/// Get a part from the symbol
SymbolPartP getPart(long item) const;
/// Select an item, also in the list control
/// Deselects all other items
void selectItem(long item);
// --------------------------------------------------- : Event handling
DECLARE_EVENT_TABLE();
void onSelect (wxListEvent& ev);
void onDeselect (wxListEvent& ev);
void onLabelEdit(wxListEvent& ev);
void onSize (wxSizeEvent& ev);
void onDrag (wxMouseEvent& ev);
};
// ----------------------------------------------------------------------------- : EOF
#endif
//+----------------------------------------------------------------------------+
//| 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;
}
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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;
}
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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);
}
*/
\ No newline at end of file
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_SYMBOL_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
//+----------------------------------------------------------------------------+
//| 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 ()
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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
}
\ No newline at end of file
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_GUI_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
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
<?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>
//+----------------------------------------------------------------------------+
//| 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);
}
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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;
}
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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)"));
}
}
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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);
}
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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));
}
}
\ No newline at end of file
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_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
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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;
}
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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
//+----------------------------------------------------------------------------+
//| 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
# Generate a header and source file
$file = $ARGV[0];
$macro = uc $file;
$macro =~ s@/@_@g;
# read templates
open F, "../src/code_template.hpp";
$hpp = join('',<F>);
close F;
open F, "../src/code_template.cpp";
$cpp = join('',<F>);
close F;
# insert stuff
$hpp =~ s/_\n/_$macro\n/g;
$cpp =~ s@<util/prec.hpp>@<$file.hpp>@g;
# write files
open F, "> ../src/$file.hpp";
print F $hpp;
close F;
open F, "> ../src/$file.cpp";
print F $cpp;
close F;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment