Commit 53ee9924 authored by twanvl's avatar twanvl

Rotation and reflection should now work correctly;

Finished the symmetry editor
parent bac273a8
...@@ -519,6 +519,7 @@ void GroupSymbolPartsActionBase::perform(bool to_undo) { ...@@ -519,6 +519,7 @@ void GroupSymbolPartsActionBase::perform(bool to_undo) {
GroupSymbolPartsAction::GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group) GroupSymbolPartsAction::GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group)
: GroupSymbolPartsActionBase(root) : GroupSymbolPartsActionBase(root)
, group(group)
{ {
// group parts in the old parts list // group parts in the old parts list
bool done = false; bool done = false;
...@@ -539,7 +540,7 @@ GroupSymbolPartsAction::GroupSymbolPartsAction(SymbolGroup& root, const set<Symb ...@@ -539,7 +540,7 @@ GroupSymbolPartsAction::GroupSymbolPartsAction(SymbolGroup& root, const set<Symb
group->calculateBounds(); group->calculateBounds();
} }
String GroupSymbolPartsAction::getName(bool to_undo) const { String GroupSymbolPartsAction::getName(bool to_undo) const {
return _ACTION_("group parts"); return group->isSymbolSymmetry() ? _ACTION_("add symmetry") : _ACTION_("group parts");
} }
UngroupSymbolPartsAction::UngroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts) UngroupSymbolPartsAction::UngroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts)
......
...@@ -301,6 +301,8 @@ class GroupSymbolPartsAction : public GroupSymbolPartsActionBase { ...@@ -301,6 +301,8 @@ class GroupSymbolPartsAction : public GroupSymbolPartsActionBase {
GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group); GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group);
virtual String getName(bool to_undo) const; virtual String getName(bool to_undo) const;
private:
SymbolGroupP group;
}; };
/// Break up one or more SymbolGroups /// Break up one or more SymbolGroups
......
...@@ -427,3 +427,90 @@ Action* control_point_remove_action(const SymbolShapeP& shape, const set<Control ...@@ -427,3 +427,90 @@ Action* control_point_remove_action(const SymbolShapeP& shape, const set<Control
return new ControlPointRemoveAction(shape, to_delete); return new ControlPointRemoveAction(shape, to_delete);
} }
} }
// ----------------------------------------------------------------------------- : Move symmetry center/handle
SymmetryMoveAction::SymmetryMoveAction(SymbolSymmetry& symmetry, bool is_handle)
: symmetry(symmetry)
, is_handle(is_handle)
, original(is_handle ? symmetry.handle : symmetry.center)
, constrain(false)
, snap(0)
{}
String SymmetryMoveAction::getName(bool to_undo) const {
return is_handle ? _ACTION_("move symmetry handle") : _ACTION_("move symmetry center");
}
void SymmetryMoveAction::perform(bool to_undo) {
if (is_handle) {
swap(symmetry.handle, original);
} else {
swap(symmetry.center, original);
}
}
void SymmetryMoveAction::move(const Vector2D& deltaDelta) {
delta += deltaDelta;
if (is_handle) {
symmetry.handle = snap_vector(symmetry.center + original + delta, snap) - symmetry.center;
if (constrain) {
// constrain to multiples of 2pi/24 i.e. 24 stops
double angle = atan2(symmetry.handle.y, symmetry.handle.x);
double mult = (2 * M_PI) / 24;
angle = floor(angle / mult + 0.5) * mult;
symmetry.handle = Vector2D(cos(angle), sin(angle)) * symmetry.handle.length();
}
} else {
// Determine actual delta, possibly constrained and snapped
symmetry.center = constrain_snap_vector(original, delta, constrain, snap);
}
}
// ----------------------------------------------------------------------------- : Change symmetry kind
SymmetryTypeAction::SymmetryTypeAction(SymbolSymmetry& symmetry, SymbolSymmetryType type)
: symmetry(symmetry), type(type)
, old_name(symmetry.name)
{
// update name?
if (old_name == symmetry.expectedName()) {
swap(symmetry.kind, type);
old_name = symmetry.expectedName();
swap(symmetry.kind, type);
}
}
String SymmetryTypeAction::getName(bool to_undo) const {
return _ACTION_("change symmetry type");
}
void SymmetryTypeAction::perform(bool to_undo) {
swap(symmetry.kind, type);
swap(symmetry.name, old_name);
}
// ----------------------------------------------------------------------------- : Change symmetry copies
SymmetryCopiesAction::SymmetryCopiesAction(SymbolSymmetry& symmetry, int copies)
: symmetry(symmetry), copies(copies)
, old_name(symmetry.name)
{
// update name?
if (old_name == symmetry.expectedName()) {
swap(symmetry.copies, copies);
old_name = symmetry.expectedName();
swap(symmetry.copies, copies);
}
}
String SymmetryCopiesAction::getName(bool to_undo) const {
return _ACTION_("change symmetry copies");
}
void SymmetryCopiesAction::perform(bool to_undo) {
swap(symmetry.copies, copies);
swap(symmetry.name, old_name);
}
...@@ -172,5 +172,60 @@ class ControlPointAddAction : public Action { ...@@ -172,5 +172,60 @@ class ControlPointAddAction : public Action {
Action* control_point_remove_action(const SymbolShapeP& shape, const set<ControlPointP>& to_delete); Action* control_point_remove_action(const SymbolShapeP& shape, const set<ControlPointP>& to_delete);
// ----------------------------------------------------------------------------- : Move symmetry center/handle
/// Moving the handle or the center of a symbol symmetry
class SymmetryMoveAction : public Action {
public:
SymmetryMoveAction(SymbolSymmetry& symmetry, bool is_handle);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
/// Update this action to move some more
void move(const Vector2D& delta);
private:
SymbolSymmetry& symmetry; ///< Affected part
bool is_handle; ///< Move the handle or the center?
Vector2D delta; ///< Amount we moved
Vector2D original; ///< Original value
public:
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
};
// ----------------------------------------------------------------------------- : Change symmetry kind
/// Change the type of symmetry
class SymmetryTypeAction : public Action {
public:
SymmetryTypeAction(SymbolSymmetry& symmetry, SymbolSymmetryType type);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
SymbolSymmetry& symmetry;
SymbolSymmetryType type;
String old_name;
};
// ----------------------------------------------------------------------------- : Change symmetry copies
/// Change the number of copies of a symmetry
class SymmetryCopiesAction : public Action {
public:
SymmetryCopiesAction(SymbolSymmetry& symmetry, int copies);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
private:
SymbolSymmetry& symmetry;
int copies;
String old_name;
};
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -212,6 +212,11 @@ SymbolPartP SymbolSymmetry::clone() const { ...@@ -212,6 +212,11 @@ SymbolPartP SymbolSymmetry::clone() const {
return part; return part;
} }
String SymbolSymmetry::expectedName() const {
return capitalize(kind == SYMMETRY_ROTATION ? _TYPE_("rotation") : _TYPE_("reflection"))
+ String::Format(_(" (%d)"), copies);
}
IMPLEMENT_REFLECTION(SymbolSymmetry) { IMPLEMENT_REFLECTION(SymbolSymmetry) {
REFLECT_BASE(SymbolPart); REFLECT_BASE(SymbolPart);
REFLECT(kind); REFLECT(kind);
......
...@@ -222,7 +222,7 @@ class SymbolGroup : public SymbolPart { ...@@ -222,7 +222,7 @@ class SymbolGroup : public SymbolPart {
// ----------------------------------------------------------------------------- : SymbolSymmetry // ----------------------------------------------------------------------------- : SymbolSymmetry
enum SymbolSymmetryType enum SymbolSymmetryType
{ SYMMETRY_ROTATION = SYMBOL_COMBINE_BORDER + 1 // for icons { SYMMETRY_ROTATION
, SYMMETRY_REFLECTION , SYMMETRY_REFLECTION
}; };
...@@ -240,10 +240,12 @@ class SymbolSymmetry : public SymbolGroup { ...@@ -240,10 +240,12 @@ class SymbolSymmetry : public SymbolGroup {
virtual String typeName() const; virtual String typeName() const;
virtual SymbolPartP clone() const; virtual SymbolPartP clone() const;
virtual int icon() const { return kind; } virtual int icon() const { return kind + SYMBOL_COMBINE_BORDER + 1; }
virtual SymbolSymmetry* isSymbolSymmetry() { return this; } virtual SymbolSymmetry* isSymbolSymmetry() { return this; }
virtual const SymbolSymmetry* isSymbolSymmetry() const { return this; } virtual const SymbolSymmetry* isSymbolSymmetry() const { return this; }
String expectedName() const;
DECLARE_REFLECTION(); DECLARE_REFLECTION();
}; };
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
SymbolControl::SymbolControl(SymbolWindow* parent, int id, const SymbolP& symbol) SymbolControl::SymbolControl(SymbolWindow* parent, int id, const SymbolP& symbol)
: wxControl(parent, id) : wxControl(parent, id)
, SymbolViewer(symbol) , SymbolViewer(symbol, true)
, parent(parent) , parent(parent)
{ {
onChangeSymbol(); onChangeSymbol();
...@@ -66,7 +66,7 @@ void SymbolControl::onModeChange(wxCommandEvent& ev) { ...@@ -66,7 +66,7 @@ void SymbolControl::onModeChange(wxCommandEvent& ev) {
switchEditor(new_intrusive1<SymbolBasicShapeEditor>(this)); switchEditor(new_intrusive1<SymbolBasicShapeEditor>(this));
break; break;
case ID_MODE_SYMMETRY: case ID_MODE_SYMMETRY:
switchEditor(new_intrusive1<SymbolSymmetryEditor>(this)); switchEditor(new_intrusive2<SymbolSymmetryEditor>(this, selected_parts.getASymmetry()));
break; break;
} }
} }
...@@ -113,6 +113,24 @@ void SymbolControl::onUpdateSelection() { ...@@ -113,6 +113,24 @@ void SymbolControl::onUpdateSelection() {
Refresh(false); Refresh(false);
} }
break; break;
} case ID_MODE_SYMMETRY: {
// can only select a single part!
SymbolSymmetryP symmetry = selected_parts.getASymmetry();
if (!symmetry) {
if (selected_symmetry && selected_parts.select(selected_symmetry)) {
signalSelectionChange();
}
break;
}
if (symmetry != selected_symmetry) {
if (symmetry && selected_parts.select(symmetry)) {
signalSelectionChange();
}
// begin editing another part
selected_symmetry = symmetry;
Refresh(false);
}
break;
} case ID_MODE_SHAPES: } case ID_MODE_SHAPES:
if (!selected_parts.empty()) { if (!selected_parts.empty()) {
// there can't be a selection // there can't be a selection
...@@ -136,6 +154,9 @@ void SymbolControl::activatePart(const SymbolPartP& part) { ...@@ -136,6 +154,9 @@ void SymbolControl::activatePart(const SymbolPartP& part) {
if (part->isSymbolShape()) { if (part->isSymbolShape()) {
selected_parts.select(part); selected_parts.select(part);
switchEditor(new_intrusive2<SymbolPointEditor>(this, static_pointer_cast<SymbolShape>(part))); switchEditor(new_intrusive2<SymbolPointEditor>(this, static_pointer_cast<SymbolShape>(part)));
} else if (part->isSymbolSymmetry()) {
selected_parts.select(part);
switchEditor(new_intrusive2<SymbolSymmetryEditor>(this, static_pointer_cast<SymbolSymmetry>(part)));
} }
} }
......
...@@ -75,6 +75,7 @@ class SymbolControl : public wxControl, public SymbolViewer { ...@@ -75,6 +75,7 @@ class SymbolControl : public wxControl, public SymbolViewer {
SymbolPartsSelection selected_parts; SymbolPartsSelection selected_parts;
SymbolPartP highlight_part; ///< part the mouse cursor is over SymbolPartP highlight_part; ///< part the mouse cursor is over
SymbolShapeP selected_shape; ///< if there is a single selection SymbolShapeP selected_shape; ///< if there is a single selection
SymbolSymmetryP selected_symmetry; ///< if there is a single selection
/// Parent window /// Parent window
SymbolWindow* parent; SymbolWindow* parent;
......
...@@ -486,13 +486,13 @@ const Image& SymbolPartList::itemPreview(int i, const SymbolPartP& part) { ...@@ -486,13 +486,13 @@ const Image& SymbolPartList::itemPreview(int i, const SymbolPartP& part) {
if (s->combine == SYMBOL_COMBINE_SUBTRACT) { if (s->combine == SYMBOL_COMBINE_SUBTRACT) {
// temporarily render using subtract instead, otherwise we don't see anything // temporarily render using subtract instead, otherwise we don't see anything
s->combine = SYMBOL_COMBINE_BORDER; s->combine = SYMBOL_COMBINE_BORDER;
img = render_symbol(sym, filter, 0.08, ITEM_HEIGHT * 4); img = render_symbol(sym, filter, 0.08, ITEM_HEIGHT * 4, true);
s->combine = SYMBOL_COMBINE_SUBTRACT; s->combine = SYMBOL_COMBINE_SUBTRACT;
} else { } else {
img = render_symbol(sym, filter, 0.08, ITEM_HEIGHT * 4); img = render_symbol(sym, filter, 0.08, ITEM_HEIGHT * 4, true);
} }
} else { } else {
img = render_symbol(sym, filter, 0.08, ITEM_HEIGHT * 4); img = render_symbol(sym, filter, 0.08, ITEM_HEIGHT * 4, true);
} }
resample(img, p.image); resample(img, p.image);
p.up_to_date = true; p.up_to_date = true;
......
...@@ -55,6 +55,15 @@ bool SymbolPartsSelection::select(const SymbolPartP& part, SelectMode mode) { ...@@ -55,6 +55,15 @@ bool SymbolPartsSelection::select(const SymbolPartP& part, SelectMode mode) {
return true; return true;
} }
void SymbolPartsSelection::clearChildren(SymbolPart* part) {
if (SymbolGroup* g = part->isSymbolGroup()) {
FOR_EACH(p, g->parts) {
if (selected(p)) selection.erase(p);
clearChildren(p.get());
}
}
}
SymbolShapeP SymbolPartsSelection::getAShape() const { SymbolShapeP SymbolPartsSelection::getAShape() const {
FOR_EACH(s, selection) { FOR_EACH(s, selection) {
if (s->isSymbolShape()) return static_pointer_cast<SymbolShape>(s); if (s->isSymbolShape()) return static_pointer_cast<SymbolShape>(s);
...@@ -62,13 +71,11 @@ SymbolShapeP SymbolPartsSelection::getAShape() const { ...@@ -62,13 +71,11 @@ SymbolShapeP SymbolPartsSelection::getAShape() const {
return SymbolShapeP(); return SymbolShapeP();
} }
void SymbolPartsSelection::clearChildren(SymbolPart* part) { SymbolSymmetryP SymbolPartsSelection::getASymmetry() const {
if (SymbolGroup* g = part->isSymbolGroup()) { FOR_EACH(s, selection) {
FOR_EACH(p, g->parts) { if (s->isSymbolSymmetry()) return static_pointer_cast<SymbolSymmetry>(s);
if (selected(p)) selection.erase(p);
clearChildren(p.get());
}
} }
return SymbolSymmetryP();
} }
......
...@@ -15,6 +15,7 @@ class Vector2D; ...@@ -15,6 +15,7 @@ class Vector2D;
DECLARE_POINTER_TYPE(Symbol); DECLARE_POINTER_TYPE(Symbol);
DECLARE_POINTER_TYPE(SymbolPart); DECLARE_POINTER_TYPE(SymbolPart);
DECLARE_POINTER_TYPE(SymbolShape); DECLARE_POINTER_TYPE(SymbolShape);
DECLARE_POINTER_TYPE(SymbolSymmetry);
class SymbolGroup; class SymbolGroup;
// ----------------------------------------------------------------------------- : Selection // ----------------------------------------------------------------------------- : Selection
...@@ -61,6 +62,13 @@ class SymbolPartsSelection { ...@@ -61,6 +62,13 @@ class SymbolPartsSelection {
/// Get any SymbolShape if there is one selected /// Get any SymbolShape if there is one selected
SymbolShapeP getAShape() const; SymbolShapeP getAShape() const;
/// Get any SymbolSymmetry if there is one selected
SymbolSymmetryP getASymmetry() const;
/// Get the only selected thing
inline SymbolPartP getOnlyOne() const {
assert(selection.size() == 1);
return *selection.begin();
}
private: private:
Symbol* root; Symbol* root;
......
...@@ -16,11 +16,13 @@ ...@@ -16,11 +16,13 @@
// ----------------------------------------------------------------------------- : SymbolSymmetryEditor // ----------------------------------------------------------------------------- : SymbolSymmetryEditor
SymbolSymmetryEditor::SymbolSymmetryEditor(SymbolControl* control) SymbolSymmetryEditor::SymbolSymmetryEditor(SymbolControl* control, const SymbolSymmetryP& sym)
: SymbolEditorBase(control) : SymbolEditorBase(control)
, mode(ID_SYMMETRY_ROTATION) , symmetry(control->selected_symmetry)
, drawing(false) , symmetryMoveAction(nullptr)
{ {
symmetry = sym;
control->selected_symmetry = symmetry;
control->SetCursor(*wxCROSS_CURSOR); control->SetCursor(*wxCROSS_CURSOR);
} }
...@@ -28,7 +30,23 @@ SymbolSymmetryEditor::SymbolSymmetryEditor(SymbolControl* control) ...@@ -28,7 +30,23 @@ SymbolSymmetryEditor::SymbolSymmetryEditor(SymbolControl* control)
void SymbolSymmetryEditor::draw(DC& dc) { void SymbolSymmetryEditor::draw(DC& dc) {
if (symmetry) { if (symmetry) {
control.highlightPart(dc, *symmetry); control.highlightPart(dc, *symmetry, HIGHLIGHT_BORDER);
Color color(255,100,0);
Vector2D center = control.rotation.tr(symmetry->center);
Vector2D handle = control.rotation.tr(symmetry->center + symmetry->handle);
if (symmetry->kind == SYMMETRY_REFLECTION) {
// draw line to handle
dc.SetPen(wxPen(color,1,wxDOT));
dc.DrawLine(center.x, center.y, handle.x, handle.y);
// draw handle
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(color);
dc.DrawCircle(handle.x, handle.y, hovered == SELECTION_HANDLE ? 7 : 4);
}
// draw center
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(color);
dc.DrawCircle(center.x, center.y, hovered == SELECTION_CENTER ? 8 : 5);
} }
} }
...@@ -39,29 +57,41 @@ void SymbolSymmetryEditor::initUI(wxToolBar* tb, wxMenuBar* mb) { ...@@ -39,29 +57,41 @@ void SymbolSymmetryEditor::initUI(wxToolBar* tb, wxMenuBar* mb) {
copies->SetHelpText(_HELP_("copies")); copies->SetHelpText(_HELP_("copies"));
copies->SetSize(50, -1); copies->SetSize(50, -1);
tb->AddSeparator(); tb->AddSeparator();
tb->AddTool(ID_ADD_SYMMETRY, _TOOL_("add symmetry"), load_resource_tool_image(_("symmetry_add")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("add symmetry"), _HELP_("add symmetry"));
tb->AddSeparator();
tb->AddTool(ID_SYMMETRY_ROTATION, _TOOL_("rotation"), load_resource_image(_("symmetry_rotation")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("rotation"), _HELP_("rotation")); tb->AddTool(ID_SYMMETRY_ROTATION, _TOOL_("rotation"), load_resource_image(_("symmetry_rotation")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("rotation"), _HELP_("rotation"));
tb->AddTool(ID_SYMMETRY_REFLECTION, _TOOL_("reflection"), load_resource_image(_("symmetry_reflection")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("reflection"), _HELP_("reflection")); tb->AddTool(ID_SYMMETRY_REFLECTION, _TOOL_("reflection"), load_resource_image(_("symmetry_reflection")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("reflection"), _HELP_("reflection"));
tb->AddSeparator();
tb->AddControl(copies); tb->AddControl(copies);
tb->Realize(); tb->Realize();
control.SetCursor(*wxCROSS_CURSOR); control.SetCursor(*wxCROSS_CURSOR);
stopActions(); // set status text resetActions(); // set status text
} }
void SymbolSymmetryEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) { void SymbolSymmetryEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
tb->DeleteTool(ID_SYMMETRY_REFLECTION); tb->DeleteTool(ID_SYMMETRY_REFLECTION);
tb->DeleteTool(ID_SYMMETRY_ROTATION); tb->DeleteTool(ID_SYMMETRY_ROTATION);
tb->DeleteTool(ID_ADD_SYMMETRY);
// HACK: hardcoded size of rest of toolbar // HACK: hardcoded size of rest of toolbar
tb->DeleteToolByPos(7); // delete separator tb->DeleteToolByPos(7); // delete separator
tb->DeleteTool(ID_COPIES); // delete sides tb->DeleteToolByPos(7); // delete separator
tb->DeleteToolByPos(7); // delete separator
tb->DeleteTool(ID_COPIES); // delete copies
#if wxVERSION_NUMBER < 2600 #if wxVERSION_NUMBER < 2600
delete sides; delete copies;
#endif #endif
} }
void SymbolSymmetryEditor::onUpdateUI(wxUpdateUIEvent& ev) { void SymbolSymmetryEditor::onUpdateUI(wxUpdateUIEvent& ev) {
if (ev.GetId() >= ID_SYMMETRY && ev.GetId() < ID_SYMMETRY_MAX) { if (ev.GetId() >= ID_SYMMETRY && ev.GetId() < ID_SYMMETRY_MAX) {
ev.Check(ev.GetId() == mode); ev.Enable(symmetry);
ev.Check(symmetry && symmetry->kind == ev.GetId() - ID_SYMMETRY);
} else if (ev.GetId() == ID_COPIES) { } else if (ev.GetId() == ID_COPIES) {
ev.Enable(symmetry);
if (symmetry) {
copies->SetValue(symmetry->copies);
}
} else if (ev.GetId() == ID_ADD_SYMMETRY) {
ev.Enable(true); ev.Enable(true);
} else { } else {
ev.Enable(false); // we don't know about this item ev.Enable(false); // we don't know about this item
...@@ -70,8 +100,28 @@ void SymbolSymmetryEditor::onUpdateUI(wxUpdateUIEvent& ev) { ...@@ -70,8 +100,28 @@ void SymbolSymmetryEditor::onUpdateUI(wxUpdateUIEvent& ev) {
void SymbolSymmetryEditor::onCommand(int id) { void SymbolSymmetryEditor::onCommand(int id) {
if (id >= ID_SYMMETRY && id < ID_SYMMETRY_MAX) { if (id >= ID_SYMMETRY && id < ID_SYMMETRY_MAX) {
mode = id; SymbolSymmetryType kind = id == ID_SYMMETRY_ROTATION ? SYMMETRY_ROTATION : SYMMETRY_REFLECTION;
stopActions(); if (symmetry && symmetry->kind != kind) {
getSymbol()->actions.add(new SymmetryTypeAction(*symmetry, kind));
control.Refresh(false);
}
resetActions();
} else if (id == ID_COPIES) {
if (symmetry && symmetry->copies != copies->GetValue()) {
getSymbol()->actions.add(new SymmetryCopiesAction(*symmetry, copies->GetValue()));
control.Refresh(false);
}
resetActions();
} else if (id == ID_ADD_SYMMETRY) {
symmetry = new_intrusive<SymbolSymmetry>();
symmetry->kind = SYMMETRY_ROTATION;
symmetry->copies = 2;
symmetry->center = Vector2D(0.5,0.5);
symmetry->handle = Vector2D(0.2,0);
symmetry->name = symmetry->expectedName();
getSymbol()->actions.add(new GroupSymbolPartsAction(*getSymbol(), control.selected_parts.get(), symmetry));
control.selected_parts.select(symmetry);
control.Refresh(false);
} }
} }
...@@ -80,81 +130,70 @@ int SymbolSymmetryEditor::modeToolId() { return ID_MODE_SYMMETRY; } ...@@ -80,81 +130,70 @@ int SymbolSymmetryEditor::modeToolId() { return ID_MODE_SYMMETRY; }
// ----------------------------------------------------------------------------- : Mouse events // ----------------------------------------------------------------------------- : Mouse events
void SymbolSymmetryEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) { void SymbolSymmetryEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {
// Start drawing if (!symmetry) return;
drawing = true; selection = findSelection(pos);
center = handle = pos;
SetStatusText(_HELP_("drag to draw symmetry"));
} }
void SymbolSymmetryEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) { void SymbolSymmetryEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {
if (drawing && symmetry) { if (!symmetry) return;
// Finalize the symmetry if (isEditing()) {
getSymbol()->actions.add(new AddSymbolPartAction(*getSymbol(), symmetry)); resetActions();
// Select the part
control.selectPart(symmetry);
// No need to clean up, this editor gets destroyed
//stopActions();
} }
} }
void SymbolSymmetryEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) { void SymbolSymmetryEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
if (!symmetry) return;
// Resize the object // Resize the object
if (drawing) { if (selection == SELECTION_NONE) return;
handle = to; if (!symmetryMoveAction) {
makePart(center, handle, ev.ControlDown(), settings.symbol_grid_snap != ev.ShiftDown()); symmetryMoveAction = new SymmetryMoveAction(*symmetry, selection == SELECTION_HANDLE);
symmetryMoveAction->constrain = ev.ControlDown();
symmetryMoveAction->snap = ev.ShiftDown() != settings.symbol_grid_snap ? settings.symbol_grid_size : 0;
getSymbol()->actions.add(symmetryMoveAction);
}
symmetryMoveAction->move(to - from);
control.Refresh(false); control.Refresh(false);
}
void SymbolSymmetryEditor::onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
Selection old_hovered = hovered;
hovered = findSelection(to);
if (hovered != old_hovered) control.Refresh(false);
// TODO: set status text
}
SymbolSymmetryEditor::Selection SymbolSymmetryEditor::findSelection(const Vector2D& pos) {
if (!symmetry) return SELECTION_NONE;
Vector2D pos_pixel = control.rotation.tr(pos);
Vector2D center = control.rotation.tr(symmetry->center);
if ((center - pos_pixel).lengthSqr() < 5*5) return SELECTION_CENTER;
if (symmetry->kind == SYMMETRY_REFLECTION) {
Vector2D handle = control.rotation.tr(symmetry->center + symmetry->handle);
if ((handle - pos_pixel).lengthSqr() < 5*5) return SELECTION_HANDLE;
} }
return SELECTION_NONE;
} }
// ----------------------------------------------------------------------------- : Other events // ----------------------------------------------------------------------------- : Other events
void SymbolSymmetryEditor::onKeyChange(wxKeyEvent& ev) { void SymbolSymmetryEditor::onKeyChange(wxKeyEvent& ev) {
if (drawing) { if (symmetryMoveAction) {
if (ev.GetKeyCode() == WXK_CONTROL || ev.GetKeyCode() == WXK_SHIFT) { if (ev.GetKeyCode() == WXK_CONTROL || ev.GetKeyCode() == WXK_SHIFT) {
// changed constrains // changed constrains
makePart(center, handle, ev.ControlDown(), settings.symbol_grid_snap != ev.ShiftDown()); symmetryMoveAction->constrain = ev.ControlDown();
symmetryMoveAction->snap = ev.ShiftDown() != settings.symbol_grid_snap ? settings.symbol_grid_size : 0;
control.Refresh(false); control.Refresh(false);
} else if (ev.GetKeyCode() == WXK_ESCAPE) { } else if (ev.GetKeyCode() == WXK_ESCAPE) {
// cancel drawing // cancel drawing
stopActions(); resetActions();
} }
} }
} }
bool SymbolSymmetryEditor::isEditing() { return drawing; } bool SymbolSymmetryEditor::isEditing() {
return symmetryMoveAction;
// ----------------------------------------------------------------------------- : Generating shapes
void SymbolSymmetryEditor::stopActions() {
symmetry = SymbolSymmetryP();
drawing = false;
SetStatusText(_HELP_("draw symmetry"));
control.Refresh(false);
} }
void SymbolSymmetryEditor::makePart(Vector2D a, Vector2D b, bool constrained, bool snap) { void SymbolSymmetryEditor::resetActions() {
// snap symmetryMoveAction = nullptr;
if (snap) {
a = snap_vector(a, settings.symbol_grid_size);
b = snap_vector(b, settings.symbol_grid_size);
}
// constrain
Vector2D dir = b - a;
if (constrained) {
double angle = atan2(dir.y, dir.x);
// multiples of 2pi/24 i.e. 24 stops
double mult = (2 * M_PI) / 24;
angle = floor(angle / mult + 0.5) * mult;
dir = Vector2D(cos(angle), sin(angle)) * dir.length();
}
// make part
if (!symmetry) {
symmetry = new_intrusive<SymbolSymmetry>();
}
symmetry->kind = mode == ID_SYMMETRY_ROTATION ? SYMMETRY_ROTATION : SYMMETRY_REFLECTION;
symmetry->copies = copies->GetValue();
symmetry->center = a;
symmetry->handle = dir;
symmetry->name = capitalize(mode == ID_SYMMETRY_ROTATION ? _TYPE_("rotation") : _TYPE_("reflection"))
+ String::Format(_(" (%d)"), symmetry->copies);
} }
...@@ -12,14 +12,15 @@ ...@@ -12,14 +12,15 @@
#include <util/prec.hpp> #include <util/prec.hpp>
#include <gui/symbol/editor.hpp> #include <gui/symbol/editor.hpp>
class SymmetryMoveAction;
// ----------------------------------------------------------------------------- : SymbolSymmetryEditor // ----------------------------------------------------------------------------- : SymbolSymmetryEditor
/// Editor for adding symmetries /// Editor for adding symmetries
class SymbolSymmetryEditor : public SymbolEditorBase { class SymbolSymmetryEditor : public SymbolEditorBase {
public: public:
/** The symmetry parameter is optional, if it is not set, then only new ones can be created */ /** The symmetry parameter is optional, if it is not set, then only new ones can be created */
//%SymbolSymmetryEditor(SymbolControl* control, SymbolSymmetryP symmetry); SymbolSymmetryEditor(SymbolControl* control, const SymbolSymmetryP& symmetry);
SymbolSymmetryEditor(SymbolControl* control);
// --------------------------------------------------- : Drawing // --------------------------------------------------- : Drawing
...@@ -37,6 +38,7 @@ class SymbolSymmetryEditor : public SymbolEditorBase { ...@@ -37,6 +38,7 @@ class SymbolSymmetryEditor : public SymbolEditorBase {
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev); virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev);
virtual void onLeftUp (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); virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
// --------------------------------------------------- : Other events // --------------------------------------------------- : Other events
...@@ -47,18 +49,23 @@ class SymbolSymmetryEditor : public SymbolEditorBase { ...@@ -47,18 +49,23 @@ class SymbolSymmetryEditor : public SymbolEditorBase {
// --------------------------------------------------- : Data // --------------------------------------------------- : Data
private: private:
int mode; SymbolSymmetryP& symmetry;
SymbolSymmetryP symmetry;
bool drawing;
Vector2D center, handle;
// controls // controls
wxSpinCtrl* copies; wxSpinCtrl* copies;
// Actions
SymmetryMoveAction* symmetryMoveAction;
// What is selected?
enum Selection {
SELECTION_NONE,
SELECTION_HANDLE, // dragging a handle
SELECTION_CENTER, // dragging the rotation center
} selection, hovered;
/// Cancel the drawing Selection findSelection(const Vector2D& pos);
void stopActions();
/// Make the symmetry object /// Done with dragging
void makePart(Vector2D a, Vector2D b, bool constrained, bool snap); void resetActions();
}; };
......
...@@ -297,14 +297,15 @@ void SymbolWindow::onUpdateUI(wxUpdateUIEvent& ev) { ...@@ -297,14 +297,15 @@ void SymbolWindow::onUpdateUI(wxUpdateUIEvent& ev) {
void SymbolWindow::onSelectFromList(wxCommandEvent& ev) { void SymbolWindow::onSelectFromList(wxCommandEvent& ev) {
if (inSelectionEvent) return ; if (inSelectionEvent) return;
inSelectionEvent = true; inSelectionEvent = true;
control->onUpdateSelection(); control->onUpdateSelection();
inSelectionEvent = false; inSelectionEvent = false;
} }
void SymbolWindow::onActivateFromList(wxCommandEvent& ev) { void SymbolWindow::onActivateFromList(wxCommandEvent& ev) {
//% control->activatePart(control->getSymbol()->parts.at(ev.GetIndex())); if (control->selected_parts.size() == 1) {
// TODO control->activatePart(control->selected_parts.getOnlyOne());
}
} }
void SymbolWindow::onSelectFromControl() { void SymbolWindow::onSelectFromControl() {
...@@ -329,6 +330,7 @@ BEGIN_EVENT_TABLE(SymbolWindow, wxFrame) ...@@ -329,6 +330,7 @@ BEGIN_EVENT_TABLE(SymbolWindow, wxFrame)
EVT_TOOL_RANGE (ID_MODE_MIN, ID_MODE_MAX, SymbolWindow::onModeChange) EVT_TOOL_RANGE (ID_MODE_MIN, ID_MODE_MAX, SymbolWindow::onModeChange)
EVT_TOOL_RANGE (ID_CHILD_MIN, ID_CHILD_MAX, SymbolWindow::onExtraTool) EVT_TOOL_RANGE (ID_CHILD_MIN, ID_CHILD_MAX, SymbolWindow::onExtraTool)
EVT_UPDATE_UI (wxID_ANY, SymbolWindow::onUpdateUI) EVT_UPDATE_UI (wxID_ANY, SymbolWindow::onUpdateUI)
EVT_COMMAND_RANGE(ID_CHILD_MIN, ID_CHILD_MAX, wxEVT_COMMAND_SPINCTRL_UPDATED, SymbolWindow::onExtraTool)
EVT_PART_SELECT (ID_PART_LIST, SymbolWindow::onSelectFromList) EVT_PART_SELECT (ID_PART_LIST, SymbolWindow::onSelectFromList)
EVT_PART_ACTIVATE (ID_PART_LIST, SymbolWindow::onActivateFromList) EVT_PART_ACTIVATE (ID_PART_LIST, SymbolWindow::onActivateFromList)
......
...@@ -44,8 +44,7 @@ void filter_symbol(Image& symbol, const SymbolFilter& filter) { ...@@ -44,8 +44,7 @@ void filter_symbol(Image& symbol, const SymbolFilter& filter) {
// HACK: wxGTK seems to fail sometimes if you ask it to allocate the alpha channel. // HACK: wxGTK seems to fail sometimes if you ask it to allocate the alpha channel.
// This manually allocates the memory and gives it to the image to handle. // This manually allocates the memory and gives it to the image to handle.
if (!alpha) { if (!alpha) {
alpha = (Byte*) malloc (sizeof(Byte) * width * height); alpha = (Byte*) malloc(width * height);
memset(alpha, 255, width * height);
symbol.SetAlpha(alpha); symbol.SetAlpha(alpha);
} }
for (UInt y = 0 ; y < width ; ++y) { for (UInt y = 0 ; y < width ; ++y) {
...@@ -53,6 +52,9 @@ void filter_symbol(Image& symbol, const SymbolFilter& filter) { ...@@ -53,6 +52,9 @@ void filter_symbol(Image& symbol, const SymbolFilter& filter) {
// Determine set // Determine set
// green -> border or outside // green -> border or outside
// green+red=white -> border // green+red=white -> border
if (data[1] != data[2]) {
// yellow/blue = editing hint, leave alone
} else {
SymbolSet point = data[1] ? (data[0] ? SYMBOL_BORDER : SYMBOL_OUTSIDE) : SYMBOL_INSIDE; SymbolSet point = data[1] ? (data[0] ? SYMBOL_BORDER : SYMBOL_OUTSIDE) : SYMBOL_INSIDE;
// Call filter // Call filter
AColor result = filter.color((double)x / width, (double)y / height, point); AColor result = filter.color((double)x / width, (double)y / height, point);
...@@ -61,6 +63,7 @@ void filter_symbol(Image& symbol, const SymbolFilter& filter) { ...@@ -61,6 +63,7 @@ void filter_symbol(Image& symbol, const SymbolFilter& filter) {
data[1] = result.Green(); data[1] = result.Green();
data[2] = result.Blue(); data[2] = result.Blue();
alpha[0] = result.alpha; alpha[0] = result.alpha;
}
// next // next
data += 3; data += 3;
alpha += 1; alpha += 1;
...@@ -68,8 +71,8 @@ void filter_symbol(Image& symbol, const SymbolFilter& filter) { ...@@ -68,8 +71,8 @@ void filter_symbol(Image& symbol, const SymbolFilter& filter) {
} }
} }
Image render_symbol(const SymbolP& symbol, const SymbolFilter& filter, double border_radius, int size) { Image render_symbol(const SymbolP& symbol, const SymbolFilter& filter, double border_radius, int size, bool edit_hints) {
Image i = render_symbol(symbol, border_radius, size); Image i = render_symbol(symbol, border_radius, size, edit_hints);
filter_symbol(i, filter); filter_symbol(i, filter);
return i; return i;
} }
......
...@@ -35,7 +35,7 @@ class AColor : public Color { ...@@ -35,7 +35,7 @@ class AColor : public Color {
void filter_symbol(Image& symbol, const SymbolFilter& filter); void filter_symbol(Image& symbol, const SymbolFilter& filter);
/// Render a Symbol to an Image and filter it /// Render a Symbol to an Image and filter it
Image render_symbol(const SymbolP& symbol, const SymbolFilter& filter, double border_radius = 0.05, int size = 100); Image render_symbol(const SymbolP& symbol, const SymbolFilter& filter, double border_radius = 0.05, int size = 100, bool edit_hints = false);
/// Is a point inside a symbol? /// Is a point inside a symbol?
enum SymbolSet enum SymbolSet
......
This diff is collapsed.
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
// ----------------------------------------------------------------------------- : Simple rendering // ----------------------------------------------------------------------------- : Simple rendering
/// Render a Symbol to an Image /// Render a Symbol to an Image
Image render_symbol(const SymbolP& symbol, double border_radius = 0.05, int size = 100); Image render_symbol(const SymbolP& symbol, double border_radius = 0.05, int size = 100, bool editing_hints = false);
// ----------------------------------------------------------------------------- : Symbol Viewer // ----------------------------------------------------------------------------- : Symbol Viewer
...@@ -32,10 +32,11 @@ enum HighlightStyle ...@@ -32,10 +32,11 @@ enum HighlightStyle
class SymbolViewer : public SymbolView { class SymbolViewer : public SymbolView {
public: public:
// --------------------------------------------------- : Data // --------------------------------------------------- : Data
SymbolViewer(const SymbolP& symbol, double size = 500, double border_radius = 0.05); SymbolViewer(const SymbolP& symbol, bool editing_hints, double size = 500, double border_radius = 0.05);
// drawing // drawing
double border_radius; double border_radius;
bool editing_hints;
// --------------------------------------------------- : Point translation // --------------------------------------------------- : Point translation
...@@ -52,13 +53,18 @@ class SymbolViewer : public SymbolView { ...@@ -52,13 +53,18 @@ class SymbolViewer : public SymbolView {
void highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style); void highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style);
void highlightPart(DC& dc, const SymbolShape& shape, HighlightStyle style); void highlightPart(DC& dc, const SymbolShape& shape, HighlightStyle style);
void highlightPart(DC& dc, const SymbolSymmetry& sym); void highlightPart(DC& dc, const SymbolSymmetry& sym, HighlightStyle style);
void highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style); void highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style);
void drawEditingHints(DC& dc);
void onAction(const Action&, bool) {} void onAction(const Action&, bool) {}
private: private:
typedef shared_ptr<wxMemoryDC> MemoryDCP; typedef shared_ptr<wxMemoryDC> MemoryDCP;
/// Inside a reflection?
int in_symmetry;
/// Combine a symbol part with the dc /// Combine a symbol part with the dc
void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, bool allow_overlap, MemoryDCP& borderDC, MemoryDCP& interiorDC); void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, bool allow_overlap, MemoryDCP& borderDC, MemoryDCP& interiorDC);
......
...@@ -95,6 +95,8 @@ tool/grid_snap IMAGE "tool/grid_snap.png" ...@@ -95,6 +95,8 @@ tool/grid_snap IMAGE "tool/grid_snap.png"
tool/group IMAGE "tool/group.png" tool/group IMAGE "tool/group.png"
tool/ungroup IMAGE "tool/ungroup.png" tool/ungroup IMAGE "tool/ungroup.png"
tool/symmetry_add IMAGE "tool/symmetry_add.png"
combine_or IMAGE "../common/combine_or.png" combine_or IMAGE "../common/combine_or.png"
combine_sub IMAGE "../common/combine_sub.png" combine_sub IMAGE "../common/combine_sub.png"
combine_sub_dark IMAGE "../common/combine_sub_dark.png" combine_sub_dark IMAGE "../common/combine_sub_dark.png"
......
...@@ -153,10 +153,11 @@ enum ChildMenuID { ...@@ -153,10 +153,11 @@ enum ChildMenuID {
, ID_SIDES , ID_SIDES
// SymbolSymmetryEditor toolbar/menu // SymbolSymmetryEditor toolbar/menu
, ID_SYMMETRY = 2201 , ID_SYMMETRY = 2301
, ID_SYMMETRY_ROTATION = ID_SHAPE , ID_SYMMETRY_ROTATION = ID_SYMMETRY
, ID_SYMMETRY_REFLECTION , ID_SYMMETRY_REFLECTION
, ID_SYMMETRY_MAX , ID_SYMMETRY_MAX
, ID_ADD_SYMMETRY
, ID_COPIES , ID_COPIES
// On cards panel // On cards panel
......
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