mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
Rotation and reflection should now work correctly;
Finished the symmetry editor git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@538 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -519,6 +519,7 @@ void GroupSymbolPartsActionBase::perform(bool to_undo) {
|
||||
|
||||
GroupSymbolPartsAction::GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group)
|
||||
: GroupSymbolPartsActionBase(root)
|
||||
, group(group)
|
||||
{
|
||||
// group parts in the old parts list
|
||||
bool done = false;
|
||||
@@ -539,7 +540,7 @@ GroupSymbolPartsAction::GroupSymbolPartsAction(SymbolGroup& root, const set<Symb
|
||||
group->calculateBounds();
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -301,6 +301,8 @@ class GroupSymbolPartsAction : public GroupSymbolPartsActionBase {
|
||||
GroupSymbolPartsAction(SymbolGroup& root, const set<SymbolPartP>& parts, const SymbolGroupP& group);
|
||||
|
||||
virtual String getName(bool to_undo) const;
|
||||
private:
|
||||
SymbolGroupP group;
|
||||
};
|
||||
|
||||
/// Break up one or more SymbolGroups
|
||||
|
||||
@@ -427,3 +427,90 @@ Action* control_point_remove_action(const SymbolShapeP& shape, const set<Control
|
||||
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 {
|
||||
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
|
||||
#endif
|
||||
|
||||
@@ -212,6 +212,11 @@ SymbolPartP SymbolSymmetry::clone() const {
|
||||
return part;
|
||||
}
|
||||
|
||||
String SymbolSymmetry::expectedName() const {
|
||||
return capitalize(kind == SYMMETRY_ROTATION ? _TYPE_("rotation") : _TYPE_("reflection"))
|
||||
+ String::Format(_(" (%d)"), copies);
|
||||
}
|
||||
|
||||
IMPLEMENT_REFLECTION(SymbolSymmetry) {
|
||||
REFLECT_BASE(SymbolPart);
|
||||
REFLECT(kind);
|
||||
|
||||
+4
-2
@@ -222,7 +222,7 @@ class SymbolGroup : public SymbolPart {
|
||||
// ----------------------------------------------------------------------------- : SymbolSymmetry
|
||||
|
||||
enum SymbolSymmetryType
|
||||
{ SYMMETRY_ROTATION = SYMBOL_COMBINE_BORDER + 1 // for icons
|
||||
{ SYMMETRY_ROTATION
|
||||
, SYMMETRY_REFLECTION
|
||||
};
|
||||
|
||||
@@ -240,10 +240,12 @@ class SymbolSymmetry : public SymbolGroup {
|
||||
|
||||
virtual String typeName() 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 const SymbolSymmetry* isSymbolSymmetry() const { return this; }
|
||||
|
||||
String expectedName() const;
|
||||
|
||||
DECLARE_REFLECTION();
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
SymbolControl::SymbolControl(SymbolWindow* parent, int id, const SymbolP& symbol)
|
||||
: wxControl(parent, id)
|
||||
, SymbolViewer(symbol)
|
||||
, SymbolViewer(symbol, true)
|
||||
, parent(parent)
|
||||
{
|
||||
onChangeSymbol();
|
||||
@@ -66,7 +66,7 @@ void SymbolControl::onModeChange(wxCommandEvent& ev) {
|
||||
switchEditor(new_intrusive1<SymbolBasicShapeEditor>(this));
|
||||
break;
|
||||
case ID_MODE_SYMMETRY:
|
||||
switchEditor(new_intrusive1<SymbolSymmetryEditor>(this));
|
||||
switchEditor(new_intrusive2<SymbolSymmetryEditor>(this, selected_parts.getASymmetry()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -113,6 +113,24 @@ void SymbolControl::onUpdateSelection() {
|
||||
Refresh(false);
|
||||
}
|
||||
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:
|
||||
if (!selected_parts.empty()) {
|
||||
// there can't be a selection
|
||||
@@ -136,6 +154,9 @@ void SymbolControl::activatePart(const SymbolPartP& part) {
|
||||
if (part->isSymbolShape()) {
|
||||
selected_parts.select(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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,8 +73,9 @@ class SymbolControl : public wxControl, public SymbolViewer {
|
||||
public:
|
||||
/// What parts are selected?
|
||||
SymbolPartsSelection selected_parts;
|
||||
SymbolPartP highlight_part; ///< part the mouse cursor is over
|
||||
SymbolShapeP selected_shape; ///< if there is a single selection
|
||||
SymbolPartP highlight_part; ///< part the mouse cursor is over
|
||||
SymbolShapeP selected_shape; ///< if there is a single selection
|
||||
SymbolSymmetryP selected_symmetry; ///< if there is a single selection
|
||||
|
||||
/// Parent window
|
||||
SymbolWindow* parent;
|
||||
|
||||
@@ -486,13 +486,13 @@ const Image& SymbolPartList::itemPreview(int i, const SymbolPartP& part) {
|
||||
if (s->combine == SYMBOL_COMBINE_SUBTRACT) {
|
||||
// temporarily render using subtract instead, otherwise we don't see anything
|
||||
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;
|
||||
} else {
|
||||
img = render_symbol(sym, filter, 0.08, ITEM_HEIGHT * 4);
|
||||
img = render_symbol(sym, filter, 0.08, ITEM_HEIGHT * 4, true);
|
||||
}
|
||||
} 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);
|
||||
p.up_to_date = true;
|
||||
|
||||
@@ -55,13 +55,6 @@ bool SymbolPartsSelection::select(const SymbolPartP& part, SelectMode mode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SymbolShapeP SymbolPartsSelection::getAShape() const {
|
||||
FOR_EACH(s, selection) {
|
||||
if (s->isSymbolShape()) return static_pointer_cast<SymbolShape>(s);
|
||||
}
|
||||
return SymbolShapeP();
|
||||
}
|
||||
|
||||
void SymbolPartsSelection::clearChildren(SymbolPart* part) {
|
||||
if (SymbolGroup* g = part->isSymbolGroup()) {
|
||||
FOR_EACH(p, g->parts) {
|
||||
@@ -71,6 +64,20 @@ void SymbolPartsSelection::clearChildren(SymbolPart* part) {
|
||||
}
|
||||
}
|
||||
|
||||
SymbolShapeP SymbolPartsSelection::getAShape() const {
|
||||
FOR_EACH(s, selection) {
|
||||
if (s->isSymbolShape()) return static_pointer_cast<SymbolShape>(s);
|
||||
}
|
||||
return SymbolShapeP();
|
||||
}
|
||||
|
||||
SymbolSymmetryP SymbolPartsSelection::getASymmetry() const {
|
||||
FOR_EACH(s, selection) {
|
||||
if (s->isSymbolSymmetry()) return static_pointer_cast<SymbolSymmetry>(s);
|
||||
}
|
||||
return SymbolSymmetryP();
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Position based
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ class Vector2D;
|
||||
DECLARE_POINTER_TYPE(Symbol);
|
||||
DECLARE_POINTER_TYPE(SymbolPart);
|
||||
DECLARE_POINTER_TYPE(SymbolShape);
|
||||
DECLARE_POINTER_TYPE(SymbolSymmetry);
|
||||
class SymbolGroup;
|
||||
|
||||
// ----------------------------------------------------------------------------- : Selection
|
||||
@@ -61,6 +62,13 @@ class SymbolPartsSelection {
|
||||
|
||||
/// Get any SymbolShape if there is one selected
|
||||
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:
|
||||
Symbol* root;
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
// ----------------------------------------------------------------------------- : SymbolSymmetryEditor
|
||||
|
||||
SymbolSymmetryEditor::SymbolSymmetryEditor(SymbolControl* control)
|
||||
SymbolSymmetryEditor::SymbolSymmetryEditor(SymbolControl* control, const SymbolSymmetryP& sym)
|
||||
: SymbolEditorBase(control)
|
||||
, mode(ID_SYMMETRY_ROTATION)
|
||||
, drawing(false)
|
||||
, symmetry(control->selected_symmetry)
|
||||
, symmetryMoveAction(nullptr)
|
||||
{
|
||||
symmetry = sym;
|
||||
control->selected_symmetry = symmetry;
|
||||
control->SetCursor(*wxCROSS_CURSOR);
|
||||
}
|
||||
|
||||
@@ -28,7 +30,23 @@ SymbolSymmetryEditor::SymbolSymmetryEditor(SymbolControl* control)
|
||||
|
||||
void SymbolSymmetryEditor::draw(DC& dc) {
|
||||
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) {
|
||||
copies->SetHelpText(_HELP_("copies"));
|
||||
copies->SetSize(50, -1);
|
||||
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_REFLECTION, _TOOL_("reflection"), load_resource_image(_("symmetry_reflection")), wxNullBitmap, wxITEM_CHECK, _TOOLTIP_("reflection"), _HELP_("reflection"));
|
||||
tb->AddSeparator();
|
||||
tb->AddControl(copies);
|
||||
tb->Realize();
|
||||
control.SetCursor(*wxCROSS_CURSOR);
|
||||
stopActions(); // set status text
|
||||
resetActions(); // set status text
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
|
||||
tb->DeleteTool(ID_SYMMETRY_REFLECTION);
|
||||
tb->DeleteTool(ID_SYMMETRY_ROTATION);
|
||||
tb->DeleteTool(ID_ADD_SYMMETRY);
|
||||
// HACK: hardcoded size of rest of toolbar
|
||||
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
|
||||
delete sides;
|
||||
delete copies;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||
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) {
|
||||
ev.Enable(symmetry);
|
||||
if (symmetry) {
|
||||
copies->SetValue(symmetry->copies);
|
||||
}
|
||||
} else if (ev.GetId() == ID_ADD_SYMMETRY) {
|
||||
ev.Enable(true);
|
||||
} else {
|
||||
ev.Enable(false); // we don't know about this item
|
||||
@@ -70,8 +100,28 @@ void SymbolSymmetryEditor::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||
|
||||
void SymbolSymmetryEditor::onCommand(int id) {
|
||||
if (id >= ID_SYMMETRY && id < ID_SYMMETRY_MAX) {
|
||||
mode = id;
|
||||
stopActions();
|
||||
SymbolSymmetryType kind = id == ID_SYMMETRY_ROTATION ? SYMMETRY_ROTATION : SYMMETRY_REFLECTION;
|
||||
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; }
|
||||
// ----------------------------------------------------------------------------- : Mouse events
|
||||
|
||||
void SymbolSymmetryEditor::onLeftDown (const Vector2D& pos, wxMouseEvent& ev) {
|
||||
// Start drawing
|
||||
drawing = true;
|
||||
center = handle = pos;
|
||||
SetStatusText(_HELP_("drag to draw symmetry"));
|
||||
if (!symmetry) return;
|
||||
selection = findSelection(pos);
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::onLeftUp (const Vector2D& pos, wxMouseEvent& ev) {
|
||||
if (drawing && symmetry) {
|
||||
// Finalize the symmetry
|
||||
getSymbol()->actions.add(new AddSymbolPartAction(*getSymbol(), symmetry));
|
||||
// Select the part
|
||||
control.selectPart(symmetry);
|
||||
// No need to clean up, this editor gets destroyed
|
||||
//stopActions();
|
||||
if (!symmetry) return;
|
||||
if (isEditing()) {
|
||||
resetActions();
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev) {
|
||||
if (!symmetry) return;
|
||||
// Resize the object
|
||||
if (drawing) {
|
||||
handle = to;
|
||||
makePart(center, handle, ev.ControlDown(), settings.symbol_grid_snap != ev.ShiftDown());
|
||||
control.Refresh(false);
|
||||
if (selection == SELECTION_NONE) return;
|
||||
if (!symmetryMoveAction) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
void SymbolSymmetryEditor::onKeyChange(wxKeyEvent& ev) {
|
||||
if (drawing) {
|
||||
if (symmetryMoveAction) {
|
||||
if (ev.GetKeyCode() == WXK_CONTROL || ev.GetKeyCode() == WXK_SHIFT) {
|
||||
// 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);
|
||||
} else if (ev.GetKeyCode() == WXK_ESCAPE) {
|
||||
// cancel drawing
|
||||
stopActions();
|
||||
resetActions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolSymmetryEditor::isEditing() { return drawing; }
|
||||
|
||||
// ----------------------------------------------------------------------------- : Generating shapes
|
||||
|
||||
void SymbolSymmetryEditor::stopActions() {
|
||||
symmetry = SymbolSymmetryP();
|
||||
drawing = false;
|
||||
SetStatusText(_HELP_("draw symmetry"));
|
||||
control.Refresh(false);
|
||||
bool SymbolSymmetryEditor::isEditing() {
|
||||
return symmetryMoveAction;
|
||||
}
|
||||
|
||||
void SymbolSymmetryEditor::makePart(Vector2D a, Vector2D b, bool constrained, bool snap) {
|
||||
// snap
|
||||
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);
|
||||
void SymbolSymmetryEditor::resetActions() {
|
||||
symmetryMoveAction = nullptr;
|
||||
}
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
#include <util/prec.hpp>
|
||||
#include <gui/symbol/editor.hpp>
|
||||
|
||||
class SymmetryMoveAction;
|
||||
|
||||
// ----------------------------------------------------------------------------- : SymbolSymmetryEditor
|
||||
|
||||
/// Editor for adding symmetries
|
||||
class SymbolSymmetryEditor : public SymbolEditorBase {
|
||||
public:
|
||||
/** 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);
|
||||
SymbolSymmetryEditor(SymbolControl* control, const SymbolSymmetryP& symmetry);
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
|
||||
@@ -37,6 +38,7 @@ class SymbolSymmetryEditor : public SymbolEditorBase {
|
||||
|
||||
virtual void onLeftDown (const Vector2D& pos, wxMouseEvent& ev);
|
||||
virtual void onLeftUp (const Vector2D& pos, wxMouseEvent& ev);
|
||||
virtual void onMouseMove (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
|
||||
virtual void onMouseDrag (const Vector2D& from, const Vector2D& to, wxMouseEvent& ev);
|
||||
|
||||
// --------------------------------------------------- : Other events
|
||||
@@ -47,18 +49,23 @@ class SymbolSymmetryEditor : public SymbolEditorBase {
|
||||
|
||||
// --------------------------------------------------- : Data
|
||||
private:
|
||||
int mode;
|
||||
SymbolSymmetryP symmetry;
|
||||
bool drawing;
|
||||
Vector2D center, handle;
|
||||
SymbolSymmetryP& symmetry;
|
||||
// controls
|
||||
wxSpinCtrl* copies;
|
||||
// Actions
|
||||
SymmetryMoveAction* symmetryMoveAction;
|
||||
|
||||
/// Cancel the drawing
|
||||
void stopActions();
|
||||
// What is selected?
|
||||
enum Selection {
|
||||
SELECTION_NONE,
|
||||
SELECTION_HANDLE, // dragging a handle
|
||||
SELECTION_CENTER, // dragging the rotation center
|
||||
} selection, hovered;
|
||||
|
||||
/// Make the symmetry object
|
||||
void makePart(Vector2D a, Vector2D b, bool constrained, bool snap);
|
||||
Selection findSelection(const Vector2D& pos);
|
||||
|
||||
/// Done with dragging
|
||||
void resetActions();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -297,14 +297,15 @@ void SymbolWindow::onUpdateUI(wxUpdateUIEvent& ev) {
|
||||
|
||||
|
||||
void SymbolWindow::onSelectFromList(wxCommandEvent& ev) {
|
||||
if (inSelectionEvent) return ;
|
||||
if (inSelectionEvent) return;
|
||||
inSelectionEvent = true;
|
||||
control->onUpdateSelection();
|
||||
inSelectionEvent = false;
|
||||
}
|
||||
void SymbolWindow::onActivateFromList(wxCommandEvent& ev) {
|
||||
//% control->activatePart(control->getSymbol()->parts.at(ev.GetIndex()));
|
||||
// TODO
|
||||
if (control->selected_parts.size() == 1) {
|
||||
control->activatePart(control->selected_parts.getOnlyOne());
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolWindow::onSelectFromControl() {
|
||||
@@ -329,6 +330,7 @@ BEGIN_EVENT_TABLE(SymbolWindow, wxFrame)
|
||||
EVT_TOOL_RANGE (ID_MODE_MIN, ID_MODE_MAX, SymbolWindow::onModeChange)
|
||||
EVT_TOOL_RANGE (ID_CHILD_MIN, ID_CHILD_MAX, SymbolWindow::onExtraTool)
|
||||
EVT_UPDATE_UI (wxID_ANY, SymbolWindow::onUpdateUI)
|
||||
EVT_COMMAND_RANGE(ID_CHILD_MIN, ID_CHILD_MAX, wxEVT_COMMAND_SPINCTRL_UPDATED, SymbolWindow::onExtraTool)
|
||||
|
||||
EVT_PART_SELECT (ID_PART_LIST, SymbolWindow::onSelectFromList)
|
||||
EVT_PART_ACTIVATE (ID_PART_LIST, SymbolWindow::onActivateFromList)
|
||||
|
||||
@@ -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.
|
||||
// This manually allocates the memory and gives it to the image to handle.
|
||||
if (!alpha) {
|
||||
alpha = (Byte*) malloc (sizeof(Byte) * width * height);
|
||||
memset(alpha, 255, width * height);
|
||||
alpha = (Byte*) malloc(width * height);
|
||||
symbol.SetAlpha(alpha);
|
||||
}
|
||||
for (UInt y = 0 ; y < width ; ++y) {
|
||||
@@ -53,14 +52,18 @@ void filter_symbol(Image& symbol, const SymbolFilter& filter) {
|
||||
// Determine set
|
||||
// green -> border or outside
|
||||
// green+red=white -> border
|
||||
SymbolSet point = data[1] ? (data[0] ? SYMBOL_BORDER : SYMBOL_OUTSIDE) : SYMBOL_INSIDE;
|
||||
// Call filter
|
||||
AColor result = filter.color((double)x / width, (double)y / height, point);
|
||||
// Store color
|
||||
data[0] = result.Red();
|
||||
data[1] = result.Green();
|
||||
data[2] = result.Blue();
|
||||
alpha[0] = result.alpha;
|
||||
if (data[1] != data[2]) {
|
||||
// yellow/blue = editing hint, leave alone
|
||||
} else {
|
||||
SymbolSet point = data[1] ? (data[0] ? SYMBOL_BORDER : SYMBOL_OUTSIDE) : SYMBOL_INSIDE;
|
||||
// Call filter
|
||||
AColor result = filter.color((double)x / width, (double)y / height, point);
|
||||
// Store color
|
||||
data[0] = result.Red();
|
||||
data[1] = result.Green();
|
||||
data[2] = result.Blue();
|
||||
alpha[0] = result.alpha;
|
||||
}
|
||||
// next
|
||||
data += 3;
|
||||
alpha += 1;
|
||||
@@ -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 i = render_symbol(symbol, border_radius, 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, edit_hints);
|
||||
filter_symbol(i, filter);
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class AColor : public Color {
|
||||
void filter_symbol(Image& symbol, const SymbolFilter& filter);
|
||||
|
||||
/// 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?
|
||||
enum SymbolSet
|
||||
|
||||
+113
-81
@@ -14,7 +14,7 @@ DECLARE_TYPEOF_COLLECTION(SymbolPartP);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Simple rendering
|
||||
|
||||
Image render_symbol(const SymbolP& symbol, double border_radius, int size) {
|
||||
Image render_symbol(const SymbolP& symbol, double border_radius, int size, bool editing_hints) {
|
||||
SymbolViewer viewer(symbol, size, border_radius);
|
||||
Bitmap bmp(size, size);
|
||||
wxMemoryDC dc;
|
||||
@@ -28,11 +28,12 @@ Image render_symbol(const SymbolP& symbol, double border_radius, int size) {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Constructor
|
||||
|
||||
SymbolViewer::SymbolViewer(const SymbolP& symbol, double size, double border_radius)
|
||||
: border_radius(border_radius)
|
||||
SymbolViewer::SymbolViewer(const SymbolP& symbol, bool editing_hints, double size, double border_radius)
|
||||
: border_radius(border_radius), editing_hints(editing_hints)
|
||||
, rotation(0, RealRect(0,0,size,size), size)
|
||||
, multiply(size,0,0,size)
|
||||
, origin(0,0)
|
||||
, in_symmetry(0)
|
||||
{
|
||||
setSymbol(symbol);
|
||||
}
|
||||
@@ -42,7 +43,7 @@ void SymbolViewer::setZoom(double zoom) {
|
||||
multiply = Matrix2D(zoom,0, 0,zoom);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Drawing
|
||||
// ----------------------------------------------------------------------------- : Drawing : Combining
|
||||
|
||||
typedef shared_ptr<wxMemoryDC> MemoryDCP;
|
||||
|
||||
@@ -52,7 +53,7 @@ MemoryDCP getTempDC(DC& dc) {
|
||||
Bitmap buffer(s.GetWidth(), s.GetHeight(), 1);
|
||||
MemoryDCP newDC(new wxMemoryDC);
|
||||
newDC->SelectObject(buffer);
|
||||
clearDC_black(*newDC);
|
||||
clearDC(*newDC, *wxBLACK_BRUSH);
|
||||
return newDC;
|
||||
}
|
||||
|
||||
@@ -66,6 +67,7 @@ void combineBuffers(DC& dc, DC* borders, DC* interior) {
|
||||
void SymbolViewer::draw(DC& dc) {
|
||||
bool paintedSomething = false;
|
||||
bool buffersFilled = false;
|
||||
in_symmetry = 0;
|
||||
// Temporary dcs
|
||||
MemoryDCP borderDC;
|
||||
MemoryDCP interiorDC;
|
||||
@@ -80,12 +82,15 @@ void SymbolViewer::draw(DC& dc) {
|
||||
}
|
||||
}
|
||||
// Draw all parts
|
||||
combineSymbolPart(dc, *symbol, paintedSomething, buffersFilled, true, borderDC, interiorDC);
|
||||
|
||||
combineSymbolPart(dc, *symbol, paintedSomething, buffersFilled, false, borderDC, interiorDC);
|
||||
// Output the final parts from the buffer
|
||||
if (buffersFilled) {
|
||||
combineBuffers(dc, borderDC.get(), interiorDC.get());
|
||||
}
|
||||
// Editing hints?
|
||||
if (editing_hints) {
|
||||
drawEditingHints(dc);
|
||||
}
|
||||
}
|
||||
void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, bool allow_overlap, MemoryDCP& borderDC, MemoryDCP& interiorDC) {
|
||||
if (const SymbolShape* s = part.isSymbolShape()) {
|
||||
@@ -124,8 +129,10 @@ void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paint
|
||||
Matrix2D old_m = multiply;
|
||||
Vector2D old_o = origin;
|
||||
int copies = s->kind == SYMMETRY_REFLECTION ? s->copies / 2 * 2 : s->copies;
|
||||
if (copies > 1) ++in_symmetry;
|
||||
FOR_EACH_CONST_REVERSE(p, s->parts) {
|
||||
for (int i = 0 ; i < copies ; ++i) {
|
||||
for (int i = copies - 1 ; i >= 0 ; --i) {
|
||||
if (i == 0) --in_symmetry;
|
||||
if (s->clip) {
|
||||
// todo: clip
|
||||
}
|
||||
@@ -164,11 +171,14 @@ void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paint
|
||||
origin = old_o + (s->center - s->center * rot) * old_m;
|
||||
}
|
||||
// draw rotated copy
|
||||
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, allow_overlap && i == 0, borderDC, interiorDC);
|
||||
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, allow_overlap && i == copies - 1, borderDC, interiorDC);
|
||||
}
|
||||
}
|
||||
multiply = old_m;
|
||||
origin = old_o;
|
||||
if (editing_hints) {
|
||||
highlightPart(dc, *s, HIGHLIGHT_LESS);
|
||||
}
|
||||
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
|
||||
// Draw all parts, in reverse order (bottom to top)
|
||||
FOR_EACH_CONST_REVERSE(p, g->parts) {
|
||||
@@ -177,83 +187,14 @@ void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paint
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style) {
|
||||
if (const SymbolShape* s = part.isSymbolShape()) {
|
||||
highlightPart(dc, *s, style);
|
||||
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
||||
highlightPart(dc, *s);
|
||||
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
|
||||
highlightPart(dc, *g, style);
|
||||
} else {
|
||||
throw InternalError(_("Invalid symbol part type"));
|
||||
}
|
||||
}
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolShape& shape, HighlightStyle style) {
|
||||
// create point list
|
||||
vector<wxPoint> points;
|
||||
size_t size = shape.points.size();
|
||||
for(size_t i = 0 ; i < size ; ++i) {
|
||||
segment_subdivide(*shape.getPoint((int)i), *shape.getPoint((int)i+1), origin, multiply, points);
|
||||
}
|
||||
// draw
|
||||
if (style == HIGHLIGHT_BORDER) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 2));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
} else if (style == HIGHLIGHT_BORDER_DOT) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 1, wxDOT));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
} else {
|
||||
dc.SetLogicalFunction(wxOR);
|
||||
dc.SetBrush(Color(0,0,64));
|
||||
dc.SetPen (*wxTRANSPARENT_PEN);
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
if (shape.combine == SYMBOL_COMBINE_SUBTRACT || shape.combine == SYMBOL_COMBINE_BORDER) {
|
||||
dc.SetLogicalFunction(wxAND);
|
||||
dc.SetBrush(Color(191,191,255));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
}
|
||||
dc.SetLogicalFunction(wxCOPY);
|
||||
}
|
||||
}
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolSymmetry& sym) {
|
||||
// center
|
||||
RealPoint center = rotation.tr(sym.center);
|
||||
// draw 'spokes'
|
||||
double angle = atan2(sym.handle.y, sym.handle.x);
|
||||
dc.SetPen(wxPen(Color(255,200,0),3));
|
||||
for (int i = 0; i < sym.copies ; ++i) {
|
||||
double a = angle + (i + 0.5) * 2 * M_PI / sym.copies;
|
||||
Vector2D dir(cos(a), sin(a));
|
||||
Vector2D dir2 = rotation.tr(sym.center + 2 * dir);
|
||||
dc.DrawLine(center.x, center.y, dir2.x, dir2.y);
|
||||
}
|
||||
// draw center
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.SetBrush(Color(255,200,0));
|
||||
dc.DrawCircle(center.x, center.y, 5);
|
||||
// draw handle
|
||||
Vector2D dir2 = rotation.tr(sym.center + sym.handle);
|
||||
dc.SetPen(wxPen(Color(255,200,0),1,wxDOT));
|
||||
dc.DrawLine(center.x, center.y, dir2.x, dir2.y);
|
||||
}
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style) {
|
||||
if (style == HIGHLIGHT_BORDER) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 2));
|
||||
dc.DrawRectangle(rotation.tr(RealRect(group.min_pos, RealSize(group.max_pos - group.min_pos))));
|
||||
}
|
||||
FOR_EACH_CONST(part, group.parts) {
|
||||
highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SymbolViewer::combineSymbolShape(const SymbolShape& shape, DC& border, DC& interior, bool directB, bool directI) {
|
||||
// what color should the interior be?
|
||||
// use black when drawing to the screen
|
||||
Byte interiorCol = directI ? 0 : 255;
|
||||
if (editing_hints && in_symmetry) {
|
||||
interiorCol = directI ? 16 : 240;
|
||||
}
|
||||
// how to draw depends on combining mode
|
||||
switch(shape.combine) {
|
||||
case SYMBOL_COMBINE_OVERLAP:
|
||||
@@ -287,6 +228,10 @@ void SymbolViewer::combineSymbolShape(const SymbolShape& shape, DC& border, DC&
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : Drawing : Basic
|
||||
|
||||
|
||||
void SymbolViewer::drawSymbolShape(const SymbolShape& shape, DC* border, DC* interior, Byte borderCol, Byte interiorCol, bool directB, bool clear) {
|
||||
// create point list
|
||||
vector<wxPoint> points;
|
||||
@@ -318,3 +263,90 @@ void SymbolViewer::drawSymbolShape(const SymbolShape& shape, DC* border, DC* int
|
||||
interior->DrawPolygon((int)points.size(), &points[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Drawing : Highlighting
|
||||
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style) {
|
||||
if (const SymbolShape* s = part.isSymbolShape()) {
|
||||
highlightPart(dc, *s, style);
|
||||
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
||||
highlightPart(dc, *s, style);
|
||||
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
|
||||
highlightPart(dc, *g, style);
|
||||
} else {
|
||||
throw InternalError(_("Invalid symbol part type"));
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolShape& shape, HighlightStyle style) {
|
||||
if (style == HIGHLIGHT_LESS) return;
|
||||
// create point list
|
||||
vector<wxPoint> points;
|
||||
size_t size = shape.points.size();
|
||||
for(size_t i = 0 ; i < size ; ++i) {
|
||||
segment_subdivide(*shape.getPoint((int)i), *shape.getPoint((int)i+1), origin, multiply, points);
|
||||
}
|
||||
// draw
|
||||
if (style == HIGHLIGHT_BORDER) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 2));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
} else if (style == HIGHLIGHT_BORDER_DOT) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 1, wxDOT));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
} else {
|
||||
dc.SetLogicalFunction(wxOR);
|
||||
dc.SetBrush(Color(0,0,64));
|
||||
dc.SetPen (*wxTRANSPARENT_PEN);
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
if (shape.combine == SYMBOL_COMBINE_SUBTRACT || shape.combine == SYMBOL_COMBINE_BORDER) {
|
||||
dc.SetLogicalFunction(wxAND);
|
||||
dc.SetBrush(Color(191,191,255));
|
||||
dc.DrawPolygon((int)points.size(), &points[0]);
|
||||
}
|
||||
dc.SetLogicalFunction(wxCOPY);
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolSymmetry& sym, HighlightStyle style) {
|
||||
// highlight parts?
|
||||
FOR_EACH_CONST(part, sym.parts) {
|
||||
highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS));
|
||||
}
|
||||
// Color?
|
||||
Color color = style & HIGHLIGHT_BORDER ? Color(255,100,0)
|
||||
: style & HIGHLIGHT_INTERIOR ? Color(255,200,0)
|
||||
: Color(200,170,0);
|
||||
// center
|
||||
RealPoint center = rotation.tr(sym.center);
|
||||
// draw 'spokes'
|
||||
double angle = atan2(sym.handle.y, sym.handle.x);
|
||||
dc.SetPen(wxPen(color, sym.kind == SYMMETRY_ROTATION ? 1 : 3));
|
||||
for (int i = 0; i < sym.copies ; ++i) {
|
||||
double a = angle + (i + 0.5) * 2 * M_PI / sym.copies;
|
||||
Vector2D dir(cos(a), sin(a));
|
||||
Vector2D dir2 = rotation.tr(sym.center + 2 * dir);
|
||||
dc.DrawLine(center.x, center.y, dir2.x, dir2.y);
|
||||
}
|
||||
// draw center
|
||||
dc.SetPen(*wxBLACK_PEN);
|
||||
dc.SetBrush(color);
|
||||
dc.DrawCircle(center.x, center.y, sym.kind == SYMMETRY_ROTATION ? 7 : 5);
|
||||
}
|
||||
|
||||
void SymbolViewer::highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style) {
|
||||
if (style == HIGHLIGHT_BORDER) {
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.SetPen (wxPen(Color(255,0,0), 2));
|
||||
dc.DrawRectangle(rotation.tr(RealRect(group.min_pos, RealSize(group.max_pos - group.min_pos))));
|
||||
}
|
||||
FOR_EACH_CONST(part, group.parts) {
|
||||
highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SymbolViewer::drawEditingHints(DC& dc) {
|
||||
// TODO?
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// ----------------------------------------------------------------------------- : Simple rendering
|
||||
|
||||
/// 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
|
||||
|
||||
@@ -32,11 +32,12 @@ enum HighlightStyle
|
||||
class SymbolViewer : public SymbolView {
|
||||
public:
|
||||
// --------------------------------------------------- : 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
|
||||
double border_radius;
|
||||
|
||||
bool editing_hints;
|
||||
|
||||
// --------------------------------------------------- : Point translation
|
||||
|
||||
void setZoom(double zoom);
|
||||
@@ -50,15 +51,20 @@ class SymbolViewer : public SymbolView {
|
||||
/// Draw the symbol to a dc
|
||||
void draw(DC& dc);
|
||||
|
||||
void highlightPart(DC& dc, const SymbolPart& part, HighlightStyle style);
|
||||
void highlightPart(DC& dc, const SymbolShape& shape, HighlightStyle style);
|
||||
void highlightPart(DC& dc, const SymbolSymmetry& sym);
|
||||
void highlightPart(DC& dc, const SymbolGroup& group, 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 SymbolSymmetry& sym, HighlightStyle style);
|
||||
void highlightPart(DC& dc, const SymbolGroup& group, HighlightStyle style);
|
||||
|
||||
void drawEditingHints(DC& dc);
|
||||
|
||||
void onAction(const Action&, bool) {}
|
||||
|
||||
|
||||
private:
|
||||
typedef shared_ptr<wxMemoryDC> MemoryDCP;
|
||||
/// Inside a reflection?
|
||||
int in_symmetry;
|
||||
|
||||
/// 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);
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 117 B After Width: | Height: | Size: 120 B |
Binary file not shown.
|
Before Width: | Height: | Size: 123 B After Width: | Height: | Size: 126 B |
@@ -95,6 +95,8 @@ tool/grid_snap IMAGE "tool/grid_snap.png"
|
||||
tool/group IMAGE "tool/group.png"
|
||||
tool/ungroup IMAGE "tool/ungroup.png"
|
||||
|
||||
tool/symmetry_add IMAGE "tool/symmetry_add.png"
|
||||
|
||||
combine_or IMAGE "../common/combine_or.png"
|
||||
combine_sub IMAGE "../common/combine_sub.png"
|
||||
combine_sub_dark IMAGE "../common/combine_sub_dark.png"
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 330 B |
@@ -153,10 +153,11 @@ enum ChildMenuID {
|
||||
, ID_SIDES
|
||||
|
||||
// SymbolSymmetryEditor toolbar/menu
|
||||
, ID_SYMMETRY = 2201
|
||||
, ID_SYMMETRY_ROTATION = ID_SHAPE
|
||||
, ID_SYMMETRY = 2301
|
||||
, ID_SYMMETRY_ROTATION = ID_SYMMETRY
|
||||
, ID_SYMMETRY_REFLECTION
|
||||
, ID_SYMMETRY_MAX
|
||||
, ID_ADD_SYMMETRY
|
||||
, ID_COPIES
|
||||
|
||||
// On cards panel
|
||||
|
||||
Reference in New Issue
Block a user