New symbol part list control that shows previews and has a built in editor

git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@529 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
twanvl
2007-07-08 22:26:39 +00:00
parent c709760837
commit d00b09eb60
28 changed files with 856 additions and 343 deletions
+118 -70
View File
@@ -18,13 +18,17 @@ DECLARE_TYPEOF_COLLECTION(ControlPointP);
// ----------------------------------------------------------------------------- : Utility
String action_name_for(const set<SymbolPartP>& parts, const String& action) {
return format_string(action, parts.size() == 1 ? _TYPE_("shape") : _TYPE_("shapes"));
return format_string(action, parts.size() == 1 ? (*parts.begin())->name : _TYPE_("shapes"));
}
SymbolPartsAction::SymbolPartsAction(const set<SymbolPartP>& parts)
: parts(parts)
{}
// ----------------------------------------------------------------------------- : Moving symbol parts
SymbolPartMoveAction::SymbolPartMoveAction(const set<SymbolPartP>& parts, const Vector2D& delta)
: parts(parts)
: SymbolPartsAction(parts)
, delta(delta), moved(-delta)
, min_pos(Vector2D::infinity()), max_pos(-Vector2D::infinity())
, constrain(false)
@@ -32,10 +36,8 @@ SymbolPartMoveAction::SymbolPartMoveAction(const set<SymbolPartP>& parts, const
{
// Determine min/max_pos
FOR_EACH(p, parts) {
if (SymbolShape* s = p->isSymbolShape()) {
min_pos = piecewise_min(min_pos, s->min_pos);
max_pos = piecewise_max(max_pos, s->max_pos);
}
min_pos = piecewise_min(min_pos, p->min_pos);
max_pos = piecewise_max(max_pos, p->max_pos);
}
}
@@ -46,20 +48,28 @@ String SymbolPartMoveAction::getName(bool to_undo) const {
void SymbolPartMoveAction::perform(bool to_undo) {
// move the points back
FOR_EACH(p, parts) {
if (SymbolShape* s = p->isSymbolShape()) {
s->min_pos -= moved;
s->max_pos -= moved;
FOR_EACH(pnt, s->points) {
pnt->pos -= moved;
}
} else if (SymbolSymmetry* s = p->isSymbolSymmetry()) {
s->center -= moved;
} else {
throw InternalError(_("Invalid symbol part type"));
}
movePart(*p);
}
moved = -moved;
}
void SymbolPartMoveAction::movePart(SymbolPart& part) {
if (SymbolShape* s = part.isSymbolShape()) {
s->min_pos -= moved;
s->max_pos -= moved;
FOR_EACH(pnt, s->points) {
pnt->pos -= moved;
}
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
s->center -= moved;
} else if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
movePart(*p);
}
g->calculateBoundsNonRec();
} else {
throw InternalError(_("Invalid symbol part type"));
}
}
void SymbolPartMoveAction::move(const Vector2D& deltaDelta) {
delta += deltaDelta;
@@ -75,26 +85,34 @@ void SymbolPartMoveAction::move(const Vector2D& deltaDelta) {
// ----------------------------------------------------------------------------- : Rotating symbol parts
SymbolPartMatrixAction::SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center)
: parts(parts)
: SymbolPartsAction(parts)
, center(center)
{}
void SymbolPartMatrixAction::transform(const Vector2D& mx, const Vector2D& my) {
// Transform each point
void SymbolPartMatrixAction::transform(const Matrix2D& m) {
// Transform each part
FOR_EACH(p, parts) {
if (SymbolShape* s = p->isSymbolShape()) {
FOR_EACH(pnt, s->points) {
pnt->pos = (pnt->pos - center).mul(mx,my) + center;
pnt->delta_before = pnt->delta_before.mul(mx,my);
pnt->delta_after = pnt->delta_after .mul(mx,my);
}
// bounds change after transforming
s->calculateBounds();
} else if (SymbolSymmetry* s = p->isSymbolSymmetry()) {
s->handle = s->handle.mul(mx,my);
} else {
throw InternalError(_("Invalid symbol part type"));
transform(*p, m);
}
}
void SymbolPartMatrixAction::transform(SymbolPart& part, const Matrix2D& m) {
if (SymbolShape* s = part.isSymbolShape()) {
FOR_EACH(pnt, s->points) {
pnt->pos = ((pnt->pos - center) * m) + center;
pnt->delta_before = pnt->delta_before * m;
pnt->delta_after = pnt->delta_after * m;
}
// bounds change after transforming
s->calculateBounds();
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
s->handle = s->handle * m;
} else if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
transform(*p, m);
}
g->calculateBoundsNonRec();
} else {
throw InternalError(_("Invalid symbol part type"));
}
}
@@ -130,8 +148,8 @@ void SymbolPartRotateAction::rotateTo(double newAngle) {
void SymbolPartRotateAction::rotateBy(double deltaAngle) {
// Rotation 'matrix'
transform(
Vector2D(cos(deltaAngle), -sin(deltaAngle)),
Vector2D(sin(deltaAngle), cos(deltaAngle))
Matrix2D(cos(deltaAngle), -sin(deltaAngle)
,sin(deltaAngle), cos(deltaAngle))
);
}
@@ -170,8 +188,8 @@ void SymbolPartShearAction::move(const Vector2D& deltaShear) {
void SymbolPartShearAction::shearBy(const Vector2D& shear) {
// Shear 'matrix'
transform(
Vector2D(1, shear.x),
Vector2D(shear.y, 1)
Matrix2D(1, shear.x
,shear.y, 1)
);
}
@@ -180,7 +198,7 @@ void SymbolPartShearAction::shearBy(const Vector2D& shear) {
SymbolPartScaleAction::SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY)
: parts(parts)
: SymbolPartsAction(parts)
, scaleX(scaleX), scaleY(scaleY)
, constrain(false)
, snap(0)
@@ -189,10 +207,8 @@ SymbolPartScaleAction::SymbolPartScaleAction(const set<SymbolPartP>& parts, int
old_min = Vector2D( 1e6, 1e6);
Vector2D old_max (-1e6,-1e6);
FOR_EACH(p, parts) {
if (SymbolShape* s = p->isSymbolShape()) {
old_min = piecewise_min(old_min, s->min_pos);
old_max = piecewise_max(old_max, s->max_pos);
}
old_min = piecewise_min(old_min, p->min_pos);
old_max = piecewise_max(old_max, p->max_pos);
}
// new == old
new_min = new_real_min = old_min;
@@ -244,26 +260,34 @@ void SymbolPartScaleAction::update() {
}
void SymbolPartScaleAction::transformAll() {
Vector2D scale = new_size.div(old_size);
FOR_EACH(p, parts) {
if (SymbolShape* s = p->isSymbolShape()) {
s->min_pos = transform(s->min_pos);
s->max_pos = transform(s->max_pos);
// make sure that max >= min
if (s->min_pos.x > s->max_pos.x) swap(s->min_pos.x, s->max_pos.x);
if (s->min_pos.y > s->max_pos.y) swap(s->min_pos.y, s->max_pos.y);
// scale all points
FOR_EACH(pnt, s->points) {
pnt->pos = transform(pnt->pos);
// also scale handles
pnt->delta_before = pnt->delta_before.mul(scale);
pnt->delta_after = pnt->delta_after .mul(scale);
}
} else if (SymbolSymmetry* s = p->isSymbolSymmetry()) {
throw "TODO";
} else {
throw InternalError(_("Invalid symbol part type"));
transformPart(*p);
}
}
void SymbolPartScaleAction::transformPart(SymbolPart& part) {
if (SymbolShape* s = part.isSymbolShape()) {
Vector2D scale = new_size.div(old_size);
s->min_pos = transform(s->min_pos);
s->max_pos = transform(s->max_pos);
// make sure that max >= min
if (s->min_pos.x > s->max_pos.x) swap(s->min_pos.x, s->max_pos.x);
if (s->min_pos.y > s->max_pos.y) swap(s->min_pos.y, s->max_pos.y);
// scale all points
FOR_EACH(pnt, s->points) {
pnt->pos = transform(pnt->pos);
// also scale handles
pnt->delta_before = pnt->delta_before.mul(scale);
pnt->delta_after = pnt->delta_after .mul(scale);
}
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
throw "TODO";
} else if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
transformPart(*p);
}
g->calculateBoundsNonRec();
} else {
throw InternalError(_("Invalid symbol part type"));
}
}
@@ -274,11 +298,18 @@ Vector2D SymbolPartScaleAction::transform(const Vector2D& v) {
// ----------------------------------------------------------------------------- : Change combine mode
CombiningModeAction::CombiningModeAction(const set<SymbolPartP>& parts, SymbolShapeCombine mode) {
CombiningModeAction::CombiningModeAction(const set<SymbolPartP>& parts, SymbolShapeCombine mode)
: SymbolPartsAction(parts)
{
FOR_EACH(p, parts) {
if (p->isSymbolShape()) {
this->parts.push_back(make_pair(static_pointer_cast<SymbolShape>(p),mode));
}
add(p,mode);
}
}
void CombiningModeAction::add(const SymbolPartP& part, SymbolShapeCombine mode) {
if (part->isSymbolShape()) {
this->parts.push_back(make_pair(static_pointer_cast<SymbolShape>(part),mode));
} else if (SymbolGroup* g = part->isSymbolGroup()) {
FOR_EACH(p, g->parts) add(p, mode);
}
}
@@ -294,16 +325,28 @@ void CombiningModeAction::perform(bool to_undo) {
// ----------------------------------------------------------------------------- : Change name
SymbolPartNameAction::SymbolPartNameAction(const SymbolPartP& part, const String& name)
SymbolPartNameAction::SymbolPartNameAction(const SymbolPartP& part, const String& name, size_t old_cursor, size_t new_cursor)
: part(part), part_name(name)
, new_cursor(old_cursor), old_cursor(new_cursor) // will be swapped
{}
String SymbolPartNameAction::getName(bool to_undo) const {
return _ACTION_("change shape name");
}
bool SymbolPartNameAction::merge(const Action& action) {
TYPE_CASE(action, SymbolPartNameAction) {
if (action.part == part) {
// adjacent actions on the same part, discard the other one,
// because it only keeps an intermediate value
return true;
}
}
return false;
}
void SymbolPartNameAction::perform(bool to_undo) {
swap(part->name, part_name);
swap(old_cursor, new_cursor);
}
// ----------------------------------------------------------------------------- : Add symbol part
@@ -313,7 +356,7 @@ AddSymbolPartAction::AddSymbolPartAction(Symbol& symbol, const SymbolPartP& part
{}
String AddSymbolPartAction::getName(bool to_undo) const {
return format_string(_ACTION_("add"), part->name);
return format_string(_ACTION_("add part"), part->name);
}
void AddSymbolPartAction::perform(bool to_undo) {
@@ -407,8 +450,8 @@ void DuplicateSymbolPartsAction::getParts(set<SymbolPartP>& parts) {
// ----------------------------------------------------------------------------- : Reorder symbol parts
ReorderSymbolPartsAction::ReorderSymbolPartsAction(Symbol& symbol, size_t part_id1, size_t part_id2)
: symbol(symbol), part_id1(part_id1), part_id2(part_id2)
ReorderSymbolPartsAction::ReorderSymbolPartsAction(Symbol& symbol, size_t old_position, size_t new_position)
: symbol(symbol), old_position(old_position), new_position(new_position)
{}
String ReorderSymbolPartsAction::getName(bool to_undo) const {
@@ -416,9 +459,12 @@ String ReorderSymbolPartsAction::getName(bool to_undo) const {
}
void ReorderSymbolPartsAction::perform(bool to_undo) {
assert(part_id1 < symbol.parts.size());
assert(part_id2 < symbol.parts.size());
swap(symbol.parts[part_id1], symbol.parts[part_id2]);
assert(old_position < symbol.parts.size());
assert(new_position < symbol.parts.size());
SymbolPartP part = symbol.parts.at(old_position);
symbol.parts.erase( symbol.parts.begin() + old_position);
symbol.parts.insert(symbol.parts.begin() + new_position, part);
swap(old_position, new_position);
}
// ----------------------------------------------------------------------------- : Group symbol parts
@@ -439,6 +485,7 @@ GroupSymbolPartsAction::GroupSymbolPartsAction(Symbol& symbol, const set<SymbolP
group->name = _("Group");
FOR_EACH(p, symbol.parts) {
if (parts.find(p) != parts.end()) {
// add to group instead
group->parts.push_back(p);
if (!done) {
done = true;
@@ -449,6 +496,7 @@ GroupSymbolPartsAction::GroupSymbolPartsAction(Symbol& symbol, const set<SymbolP
old_part_list.push_back(p);
}
}
group->calculateBounds();
}
String GroupSymbolPartsAction::getName(bool to_undo) const {
return _ACTION_("group parts");
+30 -17
View File
@@ -20,16 +20,24 @@
// ----------------------------------------------------------------------------- : Transform symbol part
/// Anything that changes a part
/// Anything that changes one or more parts
class SymbolPartAction : public Action {};
/// Anything that changes a set of parts
class SymbolPartsAction : public SymbolPartAction {
public:
SymbolPartsAction(const set<SymbolPartP>& parts);
const set<SymbolPartP> parts; ///< Affected parts
};
/// 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 {
class SymbolPartMoveAction : public SymbolPartsAction {
public:
SymbolPartMoveAction(const set<SymbolPartP>& parts, const Vector2D& delta = Vector2D());
@@ -40,10 +48,11 @@ class SymbolPartMoveAction : public SymbolPartAction {
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
Vector2D min_pos, max_pos; ///< Bounding box of the thing we are moving
void movePart(SymbolPart& part); ///< Move a single part
public:
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
@@ -52,7 +61,7 @@ class SymbolPartMoveAction : public SymbolPartAction {
// ----------------------------------------------------------------------------- : Rotating symbol parts
/// Transforming symbol parts using a matrix
class SymbolPartMatrixAction : public SymbolPartAction {
class SymbolPartMatrixAction : public SymbolPartsAction {
public:
SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center);
@@ -61,10 +70,10 @@ class SymbolPartMatrixAction : public SymbolPartAction {
protected:
/// Perform the transformation using the given matrix
void transform(const Vector2D& mx, const Vector2D& my);
void transform(const Matrix2D& m);
void transform(SymbolPart& part, const Matrix2D& m);
set<SymbolPartP> parts; ///< Parts to transform
Vector2D center; ///< Center to transform around
Vector2D center; ///< Center to transform around
};
/// Rotate some symbol parts
@@ -114,7 +123,7 @@ class SymbolPartShearAction : public SymbolPartMatrixAction {
// ----------------------------------------------------------------------------- : Scaling symbol parts
/// Scale some symbol parts
class SymbolPartScaleAction : public SymbolPartAction {
class SymbolPartScaleAction : public SymbolPartsAction {
public:
SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY);
@@ -127,13 +136,13 @@ class SymbolPartScaleAction : public SymbolPartAction {
void update();
private:
set<SymbolPartP> parts; ///< Parts to scale
Vector2D old_min, old_size; ///< the original pos/size
Vector2D new_real_min, new_real_size; ///< the target pos/sizevoid shearBy(const Vector2D& shear)
Vector2D new_min, new_size; ///< the target pos/size after applying constrains
int scaleX, scaleY; ///< to what corner are we attached?
/// Transform everything in the parts
void transformAll();
void transformPart(SymbolPart&);
/// Transform a single vector
inline Vector2D transform(const Vector2D& v);
public:
@@ -144,7 +153,7 @@ class SymbolPartScaleAction : public SymbolPartAction {
// ----------------------------------------------------------------------------- : Change combine mode
/// Change the name of a symbol part
class CombiningModeAction : public SymbolPartListAction {
class CombiningModeAction : public SymbolPartsAction {
public:
// All parts must be SymbolParts
CombiningModeAction(const set<SymbolPartP>& parts, SymbolShapeCombine mode);
@@ -153,22 +162,26 @@ class CombiningModeAction : public SymbolPartListAction {
virtual void perform(bool to_undo);
private:
void add(const SymbolPartP&, SymbolShapeCombine mode);
vector<pair<SymbolShapeP,SymbolShapeCombine> > parts; ///< Affected parts with new combining modes
};
// ----------------------------------------------------------------------------- : Change name
/// Change the name of a symbol part
class SymbolPartNameAction : public SymbolPartListAction {
class SymbolPartNameAction : public SymbolPartAction {
public:
SymbolPartNameAction(const SymbolPartP& part, const String& name);
SymbolPartNameAction(const SymbolPartP& part, const String& name, size_t old_cursor, size_t new_cursor);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
virtual bool merge(const Action& action);
private:
public:
SymbolPartP part; ///< Affected part
String part_name; ///< New name
size_t old_cursor; ///< Cursor position
size_t new_cursor; ///< Cursor position
};
// ----------------------------------------------------------------------------- : Add symbol part
@@ -224,18 +237,18 @@ class DuplicateSymbolPartsAction : public SymbolPartListAction {
// ----------------------------------------------------------------------------- : Reorder symbol parts
/// Change the position of a part in a symbol, by swapping two parts.
/// Change the position of a part in a symbol, by moving a part.
class ReorderSymbolPartsAction : public SymbolPartListAction {
public:
ReorderSymbolPartsAction(Symbol& symbol, size_t part_id1, size_t part_id2);
ReorderSymbolPartsAction(Symbol& symbol, size_t old_position, size_t new_position);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
Symbol& symbol; ///< Symbol to swap the parts in
Symbol& symbol; ///< Symbol to swap the parts in
public:
size_t part_id1, part_id2; ///< Indices of parts to swap
size_t old_position, new_position; ///< Positions to move from and to
};
+2 -2
View File
@@ -349,8 +349,8 @@ SinglePointRemoveAction::SinglePointRemoveAction(const SymbolShapeP& shape, UInt
Vector2D p = c.pointAt(t);
Vector2D distP = point->pos - p;
// adjust handle sizes
point1.other.delta_after *= ssqrt(distP.dot(point1.other.delta_after) /point1.other.delta_after.lengthSqr()) + 1;
point2.other.delta_before *= ssqrt(distP.dot(point2.other.delta_before)/point2.other.delta_before.lengthSqr()) + 1;
point1.other.delta_after *= ssqrt(dot(distP, point1.other.delta_after) /point1.other.delta_after.lengthSqr()) + 1;
point2.other.delta_before *= ssqrt(dot(distP, point2.other.delta_before)/point2.other.delta_before.lengthSqr()) + 1;
// unlock if needed
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
+6 -6
View File
@@ -245,7 +245,7 @@ void mark_corners(SymbolShape& shape) {
Vector2D after = .6 * shape.getPoint(i+1)->pos + .2 * shape.getPoint(i+2)->pos + .1 * shape.getPoint(i+3)->pos + .1 * shape.getPoint(i+4)->pos;
before = (before - current.pos).normalized();
after = (after - current.pos).normalized();
if (before.dot(after) >= -0.25f) {
if (dot(before,after) >= -0.25f) {
// corner
current.lock = LOCK_FREE;
} else {
@@ -285,8 +285,8 @@ void merge_corners(SymbolShape& shape) {
for (int j = 0 ; j < 4 ; ++j) {
Vector2D a_ = (shape.getPoint(i-j-1)->pos - prev.pos).normalized();
Vector2D b_ = (shape.getPoint(i+j)->pos - cur.pos).normalized();
double a_dot = a_.dot(ab);
double b_dot = -b_.dot(ab);
double a_dot = dot(a_, ab);
double b_dot = -dot(b_, ab);
if (a_dot < min_a_dot) {
min_a_dot = a_dot;
a = a_;
@@ -300,8 +300,8 @@ void merge_corners(SymbolShape& shape) {
// t a + ab = u b, solve for t,u
// Gives us:
// t = ab cross b / b cross a
double tden = max(0.00000001, b.cross(a));
double t = ab.cross(b) / tden;
double tden = max(0.00000001, cross(b,a));
double t = cross(ab,b) / tden;
// do these tangent lines intersect, and not too far away?
// if so, then the intersection point is the merged point
if (t >= 0 && t < 20.0) {
@@ -358,7 +358,7 @@ void straighten(SymbolShape& shape) {
Vector2D bb = next.delta_before.normalized();
// if the area beneath the polygon formed by the handles is small
// then it is a straight line
double cpDot = abs(aa.cross(ab)) + abs(bb.cross(ab));
double cpDot = abs(cross(aa,ab)) + abs(cross(bb,ab));
if (cpDot < treshold) {
cur.segment_after = next.segment_before = SEGMENT_LINE;
cur.delta_after = next.delta_before = Vector2D();
+18
View File
@@ -94,6 +94,11 @@ Vector2D& ControlPoint::getOther(WhichHandle wh) {
// ----------------------------------------------------------------------------- : SymbolPart
void SymbolPart::calculateBounds() {
min_pos = Vector2D::infinity();
max_pos = -Vector2D::infinity();
}
IMPLEMENT_REFLECTION(SymbolPart) {
REFLECT_IF_NOT_READING {
String type = typeName();
@@ -235,6 +240,19 @@ SymbolPartP SymbolGroup::clone() const {
return part;
}
void SymbolGroup::calculateBounds() {
FOR_EACH(p, parts) p->calculateBounds();
calculateBoundsNonRec();
}
void SymbolGroup::calculateBoundsNonRec() {
min_pos = Vector2D::infinity();
max_pos = -Vector2D::infinity();
FOR_EACH(p, parts) {
min_pos = piecewise_min(min_pos, p->min_pos);
max_pos = piecewise_max(max_pos, p->max_pos);
}
}
IMPLEMENT_REFLECTION(SymbolGroup) {
REFLECT_BASE(SymbolPart);
REFLECT(parts);
+13 -8
View File
@@ -112,6 +112,9 @@ class SymbolPart : public IntrusivePtrVirtualBase {
public:
/// Name/label for this part
String name;
/// Position and size of the part.
/** this is the smallest axis aligned bounding box that fits around the part */
Vector2D min_pos, max_pos;
/// Type of this part
virtual String typeName() const = 0;
@@ -130,6 +133,9 @@ class SymbolPart : public IntrusivePtrVirtualBase {
virtual SymbolGroup* isSymbolGroup() { return nullptr; }
virtual const SymbolGroup* isSymbolGroup() const { return nullptr; }
/// Calculate the position and size of the part (min_pos and max_pos)
virtual void calculateBounds();
DECLARE_REFLECTION_VIRTUAL();
};
@@ -162,9 +168,6 @@ class SymbolShape : public SymbolPart {
SymbolShapeCombine combine;
// Center of rotation, relative to the part, when the part is scaled to [0..1]
Vector2D rotation_center;
/// Position and size of the part
/// this is the smallest axis aligned bounding box that fits around the part
Vector2D min_pos, max_pos;
SymbolShape();
@@ -183,7 +186,7 @@ class SymbolShape : public SymbolPart {
void enforceConstraints();
/// Calculate the position and size of the part
void calculateBounds();
virtual void calculateBounds();
DECLARE_REFLECTION();
};
@@ -221,7 +224,7 @@ class SymbolSymmetry : public SymbolPart {
/// A group of symbol parts
class SymbolGroup : public SymbolPart {
public:
vector<SymbolPartP> parts; ///< The parts in this group
vector<SymbolPartP> parts; ///< The parts in this group, first item is on top
virtual String typeName() const;
virtual SymbolPartP clone() const;
@@ -229,16 +232,18 @@ class SymbolGroup : public SymbolPart {
virtual SymbolGroup* isSymbolGroup() { return this; }
virtual const SymbolGroup* isSymbolGroup() const { return this; }
virtual void calculateBounds();
/// re-calculate the bounds, but not of the contained parts
void calculateBoundsNonRec();
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Symbol
/// An editable symbol, consists of any number of SymbolParts
class Symbol : public IntrusivePtrBase<Symbol> {
class Symbol : public SymbolGroup {
public:
/// The parts of this symbol, first item is on top
vector<SymbolPartP> parts;
/// Actions performed on this symbol and the parts in it
ActionStack actions;