From b46d979f9eaab112937c191a7e244744f9c2118b Mon Sep 17 00:00:00 2001 From: twanvl Date: Sun, 8 Jul 2007 01:12:55 +0000 Subject: [PATCH] Added the necessery classes to handle symmetry objects/mirrors in symbols; What used to be SymbolPart is now SymbolShape, SymbolPart is a base class. This should also pave the way for grouping. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@526 0fc631ac-6414-0410-93d0-97cfa31319b6 --- data/en.mse-locale/locale | 41 ++++- src/data/action/symbol.cpp | 179 ++++++++++++-------- src/data/action/symbol.hpp | 31 ++-- src/data/action/symbol_part.cpp | 68 ++++---- src/data/action/symbol_part.hpp | 18 +- src/data/format/image_to_symbol.cpp | 152 +++++++++-------- src/data/format/image_to_symbol.hpp | 2 +- src/data/symbol.cpp | 98 +++++++++-- src/data/symbol.hpp | 90 ++++++++-- src/gfx/bezier.cpp | 12 +- src/gfx/bezier.hpp | 4 +- src/gui/symbol/basic_shape_editor.cpp | 4 +- src/gui/symbol/basic_shape_editor.hpp | 2 +- src/gui/symbol/control.cpp | 35 ++-- src/gui/symbol/control.hpp | 6 +- src/gui/symbol/part_list.cpp | 14 +- src/gui/symbol/part_list.hpp | 2 +- src/gui/symbol/point_editor.cpp | 6 +- src/gui/symbol/point_editor.hpp | 4 +- src/gui/symbol/select_editor.cpp | 59 ++++--- src/gui/symbol/window.cpp | 2 +- src/gui/value/symbol.cpp | 21 +-- src/render/symbol/viewer.cpp | 117 +++++++------ src/render/symbol/viewer.hpp | 6 +- src/resource/common/symmetry_reflection.png | Bin 0 -> 257 bytes src/resource/common/symmetry_rotation.png | Bin 0 -> 252 bytes src/resource/msw/mse.rc | 2 + src/resource/msw/tool/window_keywords.png | Bin 657 -> 551 bytes src/resource/msw/tool/window_set_info.png | Bin 354 -> 520 bytes src/resource/msw/tool/window_style.png | Bin 568 -> 380 bytes src/util/window_id.hpp | 16 +- 31 files changed, 616 insertions(+), 375 deletions(-) create mode 100644 src/resource/common/symmetry_reflection.png create mode 100644 src/resource/common/symmetry_rotation.png diff --git a/data/en.mse-locale/locale b/data/en.mse-locale/locale index fb1fcf56..e745bce9 100644 --- a/data/en.mse-locale/locale +++ b/data/en.mse-locale/locale @@ -413,6 +413,10 @@ label: ############################################################## Buttons/checkboxes/choices in the GUI button: + # Editor + edit symbol: Edit + symbol gallery: Gallery + # Style panel use for all cards: Use for &all cards use custom styling options: Options &specific to this card @@ -491,10 +495,35 @@ title: ############################################################## Action (undo/redo) names action: - typing: Typing - backspace: Backspace - delete: Delete - change: Change %s + # Text editor + typing: Typing + backspace: Backspace + delete: Delete + # Choice/color editors + change: Change %s + + # Symbol Actions + move: Move %s + rotate: Rotate %s + shear: Shear %s + scale: Resize %s + add part: Add %s + remove parts: Remove %s + duplicate: Duplicate %s + reorder parts: Reorder + change combine mode:Change combine mode + change shape name: Change shape name + + # Symbol Part Actions + convert to line: Convert to line + convert to curve: Convert to curve + lock point: Lock point + move handle: Move handle + move curve: Move curve + add control point: Add control point + delete point: Delete point + delete points: Delete points + ############################################################## Error messages error: @@ -579,6 +608,10 @@ type: polygon: polygon star: star + point: point + points: points + handle: handle + ############################################################## Magic game: diff --git a/src/data/action/symbol.cpp b/src/data/action/symbol.cpp index 3cce7b13..c12b37f6 100644 --- a/src/data/action/symbol.cpp +++ b/src/data/action/symbol.cpp @@ -8,12 +8,19 @@ #include #include +#include -DECLARE_TYPEOF_COLLECTION(pair); -DECLARE_TYPEOF_COLLECTION(pair); +DECLARE_TYPEOF_COLLECTION(pair); +DECLARE_TYPEOF_COLLECTION(pair); DECLARE_TYPEOF_COLLECTION(SymbolPartP); DECLARE_TYPEOF_COLLECTION(ControlPointP); +// ----------------------------------------------------------------------------- : Utility + +String action_name_for(const set& parts, const String& action) { + return format_string(action, parts.size() == 1 ? _TYPE_("shape") : _TYPE_("shapes")); +} + // ----------------------------------------------------------------------------- : Moving symbol parts SymbolPartMoveAction::SymbolPartMoveAction(const set& parts) @@ -23,23 +30,31 @@ SymbolPartMoveAction::SymbolPartMoveAction(const set& parts) , snap(0) { // Determine min/max_pos - FOR_EACH(p,parts) { - min_pos = piecewise_min(min_pos, p->min_pos); - max_pos = piecewise_max(max_pos, p->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); + } } } String SymbolPartMoveAction::getName(bool to_undo) const { - return parts.size() == 1 ? _("Move shape") : _("Move shapes"); + return action_name_for(parts, _ACTION_("move")); } void SymbolPartMoveAction::perform(bool to_undo) { // move the points back FOR_EACH(p, parts) { - p->min_pos -= moved; - p->max_pos -= moved; - FOR_EACH(pnt, p->points) { - pnt->pos -= moved; + 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")); } } moved = -moved; @@ -49,15 +64,10 @@ void SymbolPartMoveAction::move(const Vector2D& deltaDelta) { delta += deltaDelta; // Determine actual delta, possibly constrained and snapped Vector2D d = constrain_snap_vector_offset(min_pos, max_pos, delta, constrain, snap); - Vector2D dd = d - moved; + Vector2D dd = d - moved; // move this much more // Move each point by d - FOR_EACH(p, parts) { - p->min_pos += dd; - p->max_pos += dd; - FOR_EACH(pnt, p->points) { - pnt->pos += dd; - } - } + moved = -dd; + perform(false); // (ab)use perform to move by +dd moved = d; } @@ -71,13 +81,19 @@ SymbolPartMatrixAction::SymbolPartMatrixAction(const set& parts, co 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->delta_before = pnt->delta_before.mul(mx,my); - pnt->delta_after = pnt->delta_after .mul(mx,my); + 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")); } - // bounds change after transforming - p->calculateBounds(); } } @@ -89,7 +105,7 @@ SymbolPartRotateAction::SymbolPartRotateAction(const set& parts, co {} String SymbolPartRotateAction::getName(bool to_undo) const { - return parts.size() == 1 ? _("Rotate shape") : _("Rotate shapes"); + return action_name_for(parts, _ACTION_("rotate")); } void SymbolPartRotateAction::perform(bool to_undo) { @@ -128,7 +144,7 @@ SymbolPartShearAction::SymbolPartShearAction(const set& parts, cons {} String SymbolPartShearAction::getName(bool to_undo) const { - return parts.size() == 1 ? _("Shear shape") : _("Shear shapes"); + return action_name_for(parts, _ACTION_("shear")); } void SymbolPartShearAction::perform(bool to_undo) { @@ -169,89 +185,104 @@ SymbolPartScaleAction::SymbolPartScaleAction(const set& parts, int , snap(0) { // Find min and max coordinates - oldMin = Vector2D::infinity(); - Vector2D oldMax = -Vector2D::infinity(); + old_min = Vector2D( 1e6, 1e6); + Vector2D old_max (-1e6,-1e6); FOR_EACH(p, parts) { - oldMin = piecewise_min(oldMin, p->min_pos); - oldMax = piecewise_max(oldMax, p->max_pos); + if (SymbolShape* s = p->isSymbolShape()) { + old_min = piecewise_min(old_min, s->min_pos); + old_max = piecewise_max(old_max, s->max_pos); + } } // new == old - newMin = newRealMin = oldMin; - newSize = newRealSize = oldSize = oldMax - oldMin; + new_min = new_real_min = old_min; + new_size = new_real_size = old_size = old_max - old_min; } String SymbolPartScaleAction::getName(bool to_undo) const { - return parts.size() == 1 ? _("Scale shape") : _("Scale shapes"); + return action_name_for(parts, _ACTION_("scale")); } void SymbolPartScaleAction::perform(bool to_undo) { - swap(oldMin, newMin); - swap(oldSize, newSize); + swap(old_min, new_min); + swap(old_size, new_size); transformAll(); } -void SymbolPartScaleAction::move(const Vector2D& deltaMin, const Vector2D& deltaMax) { - newRealMin += deltaMin; - newRealSize += deltaMax - deltaMin; +void SymbolPartScaleAction::move(const Vector2D& delta_min, const Vector2D& delta_max) { + new_real_min += delta_min; + new_real_size += delta_max - delta_min; update(); } void SymbolPartScaleAction::update() { - // Move each point so the range maps to - // we have already moved to the current - Vector2D tmpMin = oldMin, tmpSize = oldSize; // the size before any scaling - oldMin = newMin; oldSize = newSize; // the size before this move + // Move each point so the range [old_min...old_max] maps to [new_min...new_max] + // we have already moved to the current [new_min...new_max] + Vector2D tmp_min = old_min, tmp_size = old_size; // the size before any scaling + old_min = new_min; old_size = new_size; // the size before this move // the size after the move - newMin = newRealMin; newSize = newRealSize; + new_min = new_real_min; new_size = new_real_size; if (constrain && scaleX != 0 && scaleY != 0) { - Vector2D scale = newSize.div(tmpSize); + Vector2D scale = new_size.div(tmp_size); scale = constrain_vector(scale, true, true); - newSize = tmpSize.mul(scale); - newMin += (newRealSize - newSize).mul(Vector2D(scaleX == -1 ? 1 : 0, scaleY == -1 ? 1 : 0)); + new_size = tmp_size.mul(scale); + new_min += (new_real_size - new_size).mul(Vector2D(scaleX == -1 ? 1 : 0, scaleY == -1 ? 1 : 0)); // TODO : snapping } else if (snap >= 0) { if (scaleX + scaleY < 0) { - newMin = snap_vector(newMin, snap); - newSize += newRealMin - newMin; + new_min = snap_vector(new_min, snap); + new_size += new_real_min - new_min; } else { - Vector2D newMax = snap_vector(newMin + newSize, snap); - newSize = newMax - newMin; + Vector2D new_max = snap_vector(new_min + new_size, snap); + new_size = new_max - new_min; } } // now move all points transformAll(); - // restore oldMin/Size - oldMin = tmpMin; oldSize = tmpSize; + // restore old_min/size + old_min = tmp_min; old_size = tmp_size; } void SymbolPartScaleAction::transformAll() { - Vector2D scale = newSize.div(oldSize); + Vector2D scale = new_size.div(old_size); FOR_EACH(p, parts) { - p->min_pos = transform(p->min_pos); - p->max_pos = transform(p->max_pos); - // make sure that max >= min - if (p->min_pos.x > p->max_pos.x) swap(p->min_pos.x, p->max_pos.x); - if (p->min_pos.y > p->max_pos.y) swap(p->min_pos.y, p->max_pos.y); - // scale all points - FOR_EACH(pnt, p->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); + 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")); } } } +Vector2D SymbolPartScaleAction::transform(const Vector2D& v) { + // TODO: prevent div by 0 + return (v - old_min).div(old_size).mul(new_size) + new_min; +} + // ----------------------------------------------------------------------------- : Change combine mode -CombiningModeAction::CombiningModeAction(const set& parts, SymbolPartCombine mode) { +CombiningModeAction::CombiningModeAction(const set& parts, SymbolShapeCombine mode) { FOR_EACH(p, parts) { - this->parts.push_back(make_pair(p,mode)); + if (p->isSymbolShape()) { + this->parts.push_back(make_pair(static_pointer_cast(p),mode)); + } } } String CombiningModeAction::getName(bool to_undo) const { - return _("Change combine mode"); + return _ACTION_("change combine mode"); } void CombiningModeAction::perform(bool to_undo) { @@ -263,15 +294,15 @@ void CombiningModeAction::perform(bool to_undo) { // ----------------------------------------------------------------------------- : Change name SymbolPartNameAction::SymbolPartNameAction(const SymbolPartP& part, const String& name) - : part(part), partName(name) + : part(part), part_name(name) {} String SymbolPartNameAction::getName(bool to_undo) const { - return _("Change shape name"); + return _ACTION_("change shape name"); } void SymbolPartNameAction::perform(bool to_undo) { - swap(part->name, partName); + swap(part->name, part_name); } // ----------------------------------------------------------------------------- : Add symbol part @@ -281,7 +312,7 @@ AddSymbolPartAction::AddSymbolPartAction(Symbol& symbol, const SymbolPartP& part {} String AddSymbolPartAction::getName(bool to_undo) const { - return _("Add ") + part->name; + return format_string(_ACTION_("add"), part->name); } void AddSymbolPartAction::perform(bool to_undo) { @@ -308,7 +339,7 @@ RemoveSymbolPartsAction::RemoveSymbolPartsAction(Symbol& symbol, const set parts; ///< Parts to transform + set parts; ///< Parts to transform Vector2D center; ///< Center to transform around }; @@ -122,22 +122,20 @@ class SymbolPartScaleAction : public SymbolPartAction { virtual void perform(bool to_undo); /// Change min and max coordinates - void move(const Vector2D& deltaMin, const Vector2D& deltaMax); + void move(const Vector2D& delta_min, const Vector2D& delta_max); /// Update the action's effect void update(); private: - set 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? + set 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(); /// Transform a single vector - inline Vector2D transform(const Vector2D& v) { - return (v - oldMin).div(oldSize).mul(newSize) + newMin; - } + inline Vector2D transform(const Vector2D& v); public: bool constrain; ///< Constrain movement? int snap; ///< Snap to grid? @@ -148,13 +146,14 @@ class SymbolPartScaleAction : public SymbolPartAction { /// Change the name of a symbol part class CombiningModeAction : public SymbolPartListAction { public: - CombiningModeAction(const set& parts, SymbolPartCombine mode); + // All parts must be SymbolParts + CombiningModeAction(const set& parts, SymbolShapeCombine mode); virtual String getName(bool to_undo) const; virtual void perform(bool to_undo); private: - vector > parts; ///< Affected parts with new combining modes + vector > parts; ///< Affected parts with new combining modes }; // ----------------------------------------------------------------------------- : Change name @@ -168,8 +167,8 @@ class SymbolPartNameAction : public SymbolPartListAction { virtual void perform(bool to_undo); private: - SymbolPartP part; ///< Affected part - String partName; ///< New name + SymbolPartP part; ///< Affected part + String part_name; ///< New name }; // ----------------------------------------------------------------------------- : Add symbol part @@ -183,8 +182,8 @@ class AddSymbolPartAction : public SymbolPartListAction { virtual void perform(bool to_undo); private: - Symbol& symbol; ///< Symbol to add the part to - SymbolPartP part; ///< Part to add + Symbol& symbol; ///< Symbol to add the part to + SymbolPartP part; ///< Part to add }; // ----------------------------------------------------------------------------- : Remove symbol part diff --git a/src/data/action/symbol_part.cpp b/src/data/action/symbol_part.cpp index e612dc66..c10d4b45 100644 --- a/src/data/action/symbol_part.cpp +++ b/src/data/action/symbol_part.cpp @@ -80,6 +80,10 @@ Vector2D constrain_snap_vector_offset(const Vector2D& off1, const Vector2D& off2 return dd; } +String action_name_for(const set& points, const String& action) { + return format_string(action, points.size() == 1 ? _TYPE_("point") : _TYPE_("points")); +} + // ----------------------------------------------------------------------------- : Move control point @@ -95,7 +99,7 @@ ControlPointMoveAction::ControlPointMoveAction(const set& points) } String ControlPointMoveAction::getName(bool to_undo) const { - return points.size() == 1 ? _("Move point") : _("Move points"); + return action_name_for(points, _ACTION_("move")); } void ControlPointMoveAction::perform(bool to_undo) { @@ -126,7 +130,7 @@ HandleMoveAction::HandleMoveAction(const SelectedHandle& handle) {} String HandleMoveAction::getName(bool to_undo) const { - return _("Move handle"); + return _ACTION_("move handle"); } void HandleMoveAction::perform(bool to_undo) { @@ -171,8 +175,8 @@ SegmentModeAction::SegmentModeAction(const ControlPointP& p1, const ControlPoint } String SegmentModeAction::getName(bool to_undo) const { SegmentMode mode = to_undo ? point1.point->segment_after : point1.other.segment_after; - if (mode == SEGMENT_LINE) return _("Convert to line"); - else return _("Convert to curve"); + if (mode == SEGMENT_LINE) return _ACTION_("convert to line"); + else return _ACTION_("convert to curve"); } void SegmentModeAction::perform(bool to_undo) { @@ -191,7 +195,7 @@ LockModeAction::LockModeAction(const ControlPointP& p, LockMode lock) } String LockModeAction::getName(bool to_undo) const { - return _("Lock point"); + return _ACTION_("lock point"); } void LockModeAction::perform(bool to_undo) { @@ -206,7 +210,7 @@ CurveDragAction::CurveDragAction(const ControlPointP& point1, const ControlPoint {} String CurveDragAction::getName(bool to_undo) const { - return _("Move curve"); + return _ACTION_("move curve"); } void CurveDragAction::perform(bool to_undo) { @@ -239,12 +243,12 @@ void CurveDragAction::move(const Vector2D& delta, double t) { // ----------------------------------------------------------------------------- : Add control point -ControlPointAddAction::ControlPointAddAction(const SymbolPartP& part, UInt insert_after, double t) - : part(part) +ControlPointAddAction::ControlPointAddAction(const SymbolShapeP& shape, UInt insert_after, double t) + : shape(shape) , new_point(new ControlPoint()) , insert_after(insert_after) - , point1(part->getPoint(insert_after)) - , point2(part->getPoint(insert_after + 1)) + , point1(shape->getPoint(insert_after)) + , point2(shape->getPoint(insert_after + 1)) { // calculate new point if (point1.other.segment_after == SEGMENT_CURVE) { @@ -265,14 +269,14 @@ ControlPointAddAction::ControlPointAddAction(const SymbolPartP& part, UInt inser } String ControlPointAddAction::getName(bool to_undo) const { - return _("Add control point"); + return _ACTION_("add control point"); } void ControlPointAddAction::perform(bool to_undo) { if (to_undo) { // remove the point - part->points.erase( part->points.begin() + insert_after + 1); + shape->points.erase( shape->points.begin() + insert_after + 1); } else { - part->points.insert(part->points.begin() + insert_after + 1, new_point); + shape->points.insert(shape->points.begin() + insert_after + 1, new_point); } // update points before/after point1.perform(); @@ -291,24 +295,24 @@ double ssqrt(double x) { // Remove a single control point class SinglePointRemoveAction : public Action, public IntrusivePtrBase { public: - SinglePointRemoveAction(const SymbolPartP& part, UInt position); + SinglePointRemoveAction(const SymbolShapeP& shape, UInt position); virtual String getName(bool to_undo) const { return _("Delete point"); } virtual void perform(bool to_undo); private: - SymbolPartP part; + SymbolShapeP shape; UInt position; ControlPointP point; ///< Removed point ControlPointUpdate point1, point2; ///< Points before/after }; -SinglePointRemoveAction::SinglePointRemoveAction(const SymbolPartP& part, UInt position) - : part(part) +SinglePointRemoveAction::SinglePointRemoveAction(const SymbolShapeP& shape, UInt position) + : shape(shape) , position(position) - , point (part->getPoint(position)) - , point1(part->getPoint(position - 1)) - , point2(part->getPoint(position + 1)) + , point (shape->getPoint(position)) + , point1(shape->getPoint(position - 1)) + , point2(shape->getPoint(position + 1)) { if (point1.other.segment_after == SEGMENT_CURVE || point2.other.segment_before == SEGMENT_CURVE) { // try to preserve curve @@ -359,10 +363,10 @@ SinglePointRemoveAction::SinglePointRemoveAction(const SymbolPartP& part, UInt p void SinglePointRemoveAction::perform(bool to_undo) { if (to_undo) { // reinsert the point - part->points.insert(part->points.begin() + position, point); + shape->points.insert(shape->points.begin() + position, point); } else { // remove the point - part->points.erase( part->points.begin() + position); + shape->points.erase( shape->points.begin() + position); } // update points around removed point point1.perform(); @@ -373,12 +377,12 @@ DECLARE_POINTER_TYPE(SinglePointRemoveAction); DECLARE_TYPEOF_COLLECTION(SinglePointRemoveActionP); -// Remove a set of points from a symbol part. +// Remove a set of points from a symbol shape. // 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& toDelete); + ControlPointRemoveAction(const SymbolShapeP& shape, const set& to_delete); virtual String getName(bool to_undo) const; virtual void perform(bool to_undo); @@ -387,20 +391,20 @@ class ControlPointRemoveAction : public Action { vector removals; }; -ControlPointRemoveAction::ControlPointRemoveAction(const SymbolPartP& part, const set& toDelete) { +ControlPointRemoveAction::ControlPointRemoveAction(const SymbolShapeP& shape, const set& to_delete) { int index = 0; // find points to remove, in reverse order - FOR_EACH(point, part->points) { - if (toDelete.find(point) != toDelete.end()) { + FOR_EACH(point, shape->points) { + if (to_delete.find(point) != to_delete.end()) { // remove this point - removals.push_back(new_intrusive2(part, index)); + removals.push_back(new_intrusive2(shape, index)); } ++index; } } String ControlPointRemoveAction::getName(bool to_undo) const { - return removals.size() == 1 ? _("Delete point") : _("Delete points"); + return removals.size() == 1 ? _ACTION_("delete point") : _ACTION_("delete points"); } void ControlPointRemoveAction::perform(bool to_undo) { @@ -414,12 +418,12 @@ void ControlPointRemoveAction::perform(bool to_undo) { } -Action* controlPointRemoveAction(const SymbolPartP& part, const set& toDelete) { - if (part->points.size() - toDelete.size() < 2) { +Action* control_point_remove_action(const SymbolShapeP& shape, const set& to_delete) { + if (shape->points.size() - to_delete.size() < 2) { // TODO : remove part? //new_intrusive(part); return 0; // no action } else { - return new ControlPointRemoveAction(part, toDelete); + return new ControlPointRemoveAction(shape, to_delete); } } diff --git a/src/data/action/symbol_part.hpp b/src/data/action/symbol_part.hpp index 1f29927c..0202a253 100644 --- a/src/data/action/symbol_part.hpp +++ b/src/data/action/symbol_part.hpp @@ -9,7 +9,7 @@ /** @file data/action/symbol_part.hpp * - * Actions operating on the insides of SymbolParts (ControlPoints and the like). + * Actions operating on the insides of SymbolParts/SymbolShapes (ControlPoints and the like). */ // ----------------------------------------------------------------------------- : Includes @@ -147,11 +147,11 @@ class CurveDragAction : public SegmentModeAction { // ----------------------------------------------------------------------------- : Add control point -/// Insert a new point in a symbol part +/// Insert a new point in a symbol shape 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 insert_after, double t); + /// Insert a new point in shape, after position insertAfter_, at the time t on the segment + ControlPointAddAction(const SymbolShapeP& shape, UInt insert_after, double t); virtual String getName(bool to_undo) const; virtual void perform(bool to_undo); @@ -159,17 +159,17 @@ class ControlPointAddAction : public Action { inline ControlPointP getNewPoint() const { return new_point; } private: - SymbolPartP part; ///< SymbolPart we are in - ControlPointP new_point; ///< The point to insert + SymbolShapeP shape; ///< SymbolShape we are in + ControlPointP new_point; ///< The point to insert UInt insert_after; ///< 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& toDelete); +/// Action that removes any number of points from a symbol shape +/// TODO: If less then 3 points are left removes the entire shape? +Action* control_point_remove_action(const SymbolShapeP& shape, const set& to_delete); // ----------------------------------------------------------------------------- : EOF diff --git a/src/data/format/image_to_symbol.cpp b/src/data/format/image_to_symbol.cpp index 0e9a86a2..ceb4b773 100644 --- a/src/data/format/image_to_symbol.cpp +++ b/src/data/format/image_to_symbol.cpp @@ -121,7 +121,7 @@ struct ImageData { } }; -bool find_symbol_part_start(const ImageData& data, int& x_out, int& y_out) { +bool find_symbol_shape_start(const ImageData& data, int& x_out, int& y_out) { for (int x = 0 ; x < data.width ; ++x) { for (int y = 0 ; y < data.height ; ++y) { if (data(x, y) == FULL && data(x, y-1) == EMPTY) { @@ -136,13 +136,13 @@ bool find_symbol_part_start(const ImageData& data, int& x_out, int& y_out) { return false; } -SymbolPartP read_symbol_part(const ImageData& data) { +SymbolShapeP read_symbol_shape(const ImageData& data) { // find start point int xs, ys; - if (!find_symbol_part_start(data, xs, ys)) return SymbolPartP(); + if (!find_symbol_shape_start(data, xs, ys)) return SymbolShapeP(); data(xs, ys) |= MARKED; - SymbolPartP part(new SymbolPart); + SymbolShapeP shape(new SymbolShape); // walk around, clockwise xs += 1; // start right of the found point, otherwise last_move might think we came from above @@ -178,12 +178,12 @@ SymbolPartP read_symbol_part(const ImageData& data) { throw InternalError(_("in the ground/air")); } - // add to part and place a mark - part->points.push_back(new_intrusive2( + // add to shape and place a mark + shape->points.push_back(new_intrusive2( double(x) / data.width, double(y) / data.height )); - if (x > old_x) data(old_x, y) |= MARKED; // mark when moving right -> only mark the top of the part + if (x > old_x) data(old_x, y) |= MARKED; // mark when moving right -> only mark the top of the shape last_move = (x + y) - (old_x + old_y); old_x = x; old_y = y; @@ -191,11 +191,11 @@ SymbolPartP read_symbol_part(const ImageData& data) { // are we on the inside or the outside? if (data(x-2,y-1) & FULL) { - part->combine = PART_SUBTRACT; + shape->combine = SYMBOL_COMBINE_SUBTRACT; } else { - part->combine = PART_MERGE; + shape->combine = SYMBOL_COMBINE_MERGE; } - return part; + return shape; } @@ -204,13 +204,13 @@ SymbolP image_to_symbol(Image& img) { // 1. threshold the image greyscale(img); threshold(img.GetData(), w, h); - // 2. read as many symbol parts as we can + // 2. read as many symbol shapes as we can ImageData data = {w,h,img.GetData()}; SymbolP symbol(new Symbol); while (true) { - SymbolPartP part = read_symbol_part(data); - if (!part) break; - symbol->parts.push_back(part); + SymbolShapeP shape = read_symbol_shape(data); + if (!shape) break; + symbol->parts.push_back(shape); } reverse(symbol->parts.begin(), symbol->parts.end()); return symbol; @@ -238,11 +238,11 @@ SymbolP import_symbol(Image& img) { /// Finds corners, marks corners as LOCK_FREE, non-corners as LOCK_DIR /** A corner is a point that has an angle between tangent greater then a treshold */ -void mark_corners(SymbolPart& part) { - for (int i = 0 ; (size_t)i < part.points.size() ; ++i) { - ControlPoint& current = *part.getPoint(i); - Vector2D before = .6 * part.getPoint(i-1)->pos + .2 * part.getPoint(i-2)->pos + .1 * part.getPoint(i-3)->pos + .1 * part.getPoint(i-4)->pos; - Vector2D after = .6 * part.getPoint(i+1)->pos + .2 * part.getPoint(i+2)->pos + .1 * part.getPoint(i+3)->pos + .1 * part.getPoint(i+4)->pos; +void mark_corners(SymbolShape& shape) { + for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) { + ControlPoint& current = *shape.getPoint(i); + Vector2D before = .6 * shape.getPoint(i-1)->pos + .2 * shape.getPoint(i-2)->pos + .1 * shape.getPoint(i-3)->pos + .1 * shape.getPoint(i-4)->pos; + 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) { @@ -271,10 +271,10 @@ void mark_corners(SymbolPart& part) { * Where these two lines (one for each corner) intersect, * is the merged corner. If it is too far away, don't merge */ -void merge_corners(SymbolPart& part) { - for (int i = 0 ; (size_t)i < part.points.size() ; ++i) { - ControlPoint& cur = *part.getPoint(i); - ControlPoint& prev = *part.getPoint(i - 1); +void merge_corners(SymbolShape& shape) { + for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) { + ControlPoint& cur = *shape.getPoint(i); + ControlPoint& prev = *shape.getPoint(i - 1); if (prev.lock != LOCK_FREE || cur.lock != LOCK_FREE) continue; // step 1. find tangent lines: try tangent lines to the first point, the second, etc. // and take the one that has the largest angle with ab, i.e. the smallest dot, @@ -283,8 +283,8 @@ void merge_corners(SymbolPart& part) { double min_a_dot = 1e100, min_b_dot = 1e100; Vector2D a, b; for (int j = 0 ; j < 4 ; ++j) { - Vector2D a_ = (part.getPoint(i-j-1)->pos - prev.pos).normalized(); - Vector2D b_ = (part.getPoint(i+j)->pos - cur.pos).normalized(); + 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); if (a_dot < min_a_dot) { @@ -306,22 +306,22 @@ void merge_corners(SymbolPart& part) { // if so, then the intersection point is the merged point if (t >= 0 && t < 20.0) { prev.pos += a * -t; - part.points.erase(part.points.begin() + i); + shape.points.erase(shape.points.begin() + i); i -= 1; } } } -/// Avarage/'blur' a symbol part -void avarage(SymbolPart& part) { +/// Avarage/'blur' a symbol shape +void avarage(SymbolShape& shape) { // create a copy of the points vector old_points; - FOR_EACH(p, part.points) { + FOR_EACH(p, shape.points) { old_points.push_back(p->pos); } // avarage points - for (int i = 0 ; (size_t)i < part.points.size() ; ++i) { - ControlPoint& p = *part.getPoint(i); + for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) { + ControlPoint& p = *shape.getPoint(i); if (p.lock == LOCK_DIR) { p.pos = .25 * old_points[mod(i-1, old_points.size())] + .50 * p.pos @@ -330,29 +330,29 @@ void avarage(SymbolPart& part) { } } -/// Convert a symbol part to curves -void convert_to_curves(SymbolPart& part) { +/// Convert a symbol shape to curves +void convert_to_curves(SymbolShape& shape) { // mark all segments as curves - for (int i = 0 ; (size_t)i < part.points.size() ; ++i) { - ControlPoint& cur = *part.getPoint(i); - ControlPoint& next = *part.getPoint(i + 1); + for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) { + ControlPoint& cur = *shape.getPoint(i); + ControlPoint& next = *shape.getPoint(i + 1); cur.segment_after = SEGMENT_CURVE; cur.segment_before = SEGMENT_CURVE; cur.delta_after = (next.pos - cur.pos) / 3.0; next.delta_before = (cur.pos - next.pos) / 3.0; } // make the curves smooth by enforcing direction constraints - FOR_EACH(p, part.points) { + FOR_EACH(p, shape.points) { p->onUpdateLock(); } } -/// Convert almost straight curves in a symbol part to lines -void straighten(SymbolPart& part) { +/// Convert almost straight curves in a symbol shape to lines +void straighten(SymbolShape& shape) { const double treshold = 0.2; - for (int i = 0 ; (size_t)i < part.points.size() ; ++i) { - ControlPoint& cur = *part.getPoint(i); - ControlPoint& next = *part.getPoint(i + 1); + for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) { + ControlPoint& cur = *shape.getPoint(i); + ControlPoint& next = *shape.getPoint(i + 1); Vector2D ab = (next.pos - cur.pos).normalized(); Vector2D aa = cur.delta_after.normalized(); Vector2D bb = next.delta_before.normalized(); @@ -368,37 +368,37 @@ void straighten(SymbolPart& part) { } /// Remove unneeded points between straight lines -void merge_lines(SymbolPart& part) { - for (int i = 0 ; (size_t)i < part.points.size() ; ++i) { - ControlPoint& cur = *part.getPoint(i); +void merge_lines(SymbolShape& shape) { + for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) { + ControlPoint& cur = *shape.getPoint(i); if (cur.segment_before != cur.segment_after) continue; - Vector2D a = part.getPoint(i-1)->pos, b = cur.pos, c = part.getPoint(i+1)->pos; + Vector2D a = shape.getPoint(i-1)->pos, b = cur.pos, c = shape.getPoint(i+1)->pos; Vector2D ab = (a-b).normalized(); Vector2D bc = (b-c).normalized(); double angle_len = abs( atan2(ab.x,ab.y) - atan2(bc.x,bc.y)) * (a-c).lengthSqr(); bool keep = angle_len >= .0001; if (!keep) { - part.points.erase(part.points.begin() + i); + shape.points.erase(shape.points.begin() + i); i -= 1; } } } -double cost_of_point_removal(SymbolPart& part, int i); -void remove_point(SymbolPart& part, int i); +double cost_of_point_removal(SymbolShape& shape, int i); +void remove_point(SymbolShape& shape, int i); -/// Simplify a symbol part by removing points +/// Simplify a symbol shape by removing points /** Always remove the point with the lowest cost, * stop when the cost becomes too high */ -void remove_points(SymbolPart& part) { +void remove_points(SymbolShape& shape) { const double treshold = 0.0002; // maximum cost while (true) { // Find the point with the lowest cost of removal int best = -1; double best_cost = 1e100; - for (int i = 0 ; (size_t)i < part.points.size() ; ++i) { - double cost = cost_of_point_removal(part, i); + for (int i = 0 ; (size_t)i < shape.points.size() ; ++i) { + double cost = cost_of_point_removal(shape, i); if (cost < best_cost) { best_cost = cost; best = i; @@ -406,14 +406,14 @@ void remove_points(SymbolPart& part) { } if (best_cost > treshold) break; // ... and remove it - remove_point(part, best); + remove_point(shape, best); } } -/// Cost of removing point i from a symbol part -double cost_of_point_removal(SymbolPart& part, int i) { - ControlPoint& cur = *part.getPoint(i); - ControlPoint& prev = *part.getPoint(i-1); - ControlPoint& next = *part.getPoint(i+1); +/// Cost of removing point i from a symbol shape +double cost_of_point_removal(SymbolShape& shape, int i) { + ControlPoint& cur = *shape.getPoint(i); + ControlPoint& prev = *shape.getPoint(i-1); + ControlPoint& next = *shape.getPoint(i+1); if (cur.lock != LOCK_DIR) return 1e100; // don't remove corners Vector2D before = cur.delta_before; @@ -430,15 +430,15 @@ double cost_of_point_removal(SymbolPart& part, int i) { BezierCurve c(prev.pos, prev.pos + after0, next.pos + before2, next.pos); double t = bl/totl; Vector2D np = cur.pos - c.pointAt(t); - // cost is distance to new point * length of line ~= area added/removed from part + // cost is distance to new point * length of line ~= area added/removed from shape return np.length() * ac.length(); } /// Remove a point from a bezier curve /** See SinglePointRemoveAction for algorithm */ -void remove_point(SymbolPart& part, int i) { - ControlPoint& cur = *part.getPoint(i); - ControlPoint& prev = *part.getPoint(i-1); - ControlPoint& next = *part.getPoint(i+1); +void remove_point(SymbolShape& shape, int i) { + ControlPoint& cur = *shape.getPoint(i); + ControlPoint& prev = *shape.getPoint(i-1); + ControlPoint& next = *shape.getPoint(i+1); Vector2D before = cur.delta_before; Vector2D after = cur.delta_after; // Based on SinglePointRemoveAction @@ -449,24 +449,26 @@ void remove_point(SymbolPart& part, int i) { prev.delta_after *= totl / bl; next.delta_before *= totl / al; // remove - part.points.erase(part.points.begin() + i); + shape.points.erase(shape.points.begin() + i); } -void simplify_symbol_part(SymbolPart& part) { - mark_corners(part); - merge_corners(part); +void simplify_symbol_shape(SymbolShape& shape) { + mark_corners(shape); + merge_corners(shape); for (int i = 0 ; i < 3 ; ++i) { - avarage(part); + avarage(shape); } - convert_to_curves(part); - remove_points(part); - straighten(part); - merge_lines(part); + convert_to_curves(shape); + remove_points(shape); + straighten(shape); + merge_lines(shape); } void simplify_symbol(Symbol& symbol) { - FOR_EACH(p, symbol.parts) { - simplify_symbol_part(*p); + FOR_EACH(pb, symbol.parts) { + if (SymbolShape* p = pb->isSymbolShape()) { + simplify_symbol_shape(*p); + } } } diff --git a/src/data/format/image_to_symbol.hpp b/src/data/format/image_to_symbol.hpp index f47e3179..970d9869 100644 --- a/src/data/format/image_to_symbol.hpp +++ b/src/data/format/image_to_symbol.hpp @@ -31,7 +31,7 @@ SymbolP image_to_symbol(Image& img); void simplify_symbol(Symbol&); /// Simplify a symbol parts, i.e. use bezier curves instead of lots of lines -void simplify_symbol_part(SymbolPart&); +void simplify_symbol_shape(SymbolShape&); // ----------------------------------------------------------------------------- : EOF #endif diff --git a/src/data/symbol.cpp b/src/data/symbol.cpp index fc7a7e77..49f99fca 100644 --- a/src/data/symbol.cpp +++ b/src/data/symbol.cpp @@ -10,7 +10,6 @@ #include