Commit a56a8454 authored by twanvl's avatar twanvl

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

parent 767efcdb
This diff is collapsed.
......@@ -20,16 +20,24 @@
// ----------------------------------------------------------------------------- : Transform symbol part
/// Anything that changes a part
/// Anything that changes one or more parts
class SymbolPartAction : public Action {};
/// Anything that changes a set of parts
class SymbolPartsAction : public SymbolPartAction {
public:
SymbolPartsAction(const set<SymbolPartP>& parts);
const set<SymbolPartP> parts; ///< Affected parts
};
/// Anything that changes a part as displayed in the part list
class SymbolPartListAction : public SymbolPartAction {};
// ----------------------------------------------------------------------------- : Moving symbol parts
/// Move some symbol parts
class SymbolPartMoveAction : public SymbolPartAction {
class SymbolPartMoveAction : public SymbolPartsAction {
public:
SymbolPartMoveAction(const set<SymbolPartP>& parts, const Vector2D& delta = Vector2D());
......@@ -40,10 +48,11 @@ class SymbolPartMoveAction : public SymbolPartAction {
void move(const Vector2D& delta);
private:
set<SymbolPartP> parts; ///< Parts to move
Vector2D delta; ///< How much to move
Vector2D moved; ///< How much has been moved
Vector2D min_pos, max_pos; ///< Bounding box of the thing we are moving
void movePart(SymbolPart& part); ///< Move a single part
public:
bool constrain; ///< Constrain movement?
int snap; ///< Snap to grid?
......@@ -52,7 +61,7 @@ class SymbolPartMoveAction : public SymbolPartAction {
// ----------------------------------------------------------------------------- : Rotating symbol parts
/// Transforming symbol parts using a matrix
class SymbolPartMatrixAction : public SymbolPartAction {
class SymbolPartMatrixAction : public SymbolPartsAction {
public:
SymbolPartMatrixAction(const set<SymbolPartP>& parts, const Vector2D& center);
......@@ -61,9 +70,9 @@ class SymbolPartMatrixAction : public SymbolPartAction {
protected:
/// Perform the transformation using the given matrix
void transform(const Vector2D& mx, const Vector2D& my);
void transform(const Matrix2D& m);
void transform(SymbolPart& part, const Matrix2D& m);
set<SymbolPartP> parts; ///< Parts to transform
Vector2D center; ///< Center to transform around
};
......@@ -114,7 +123,7 @@ class SymbolPartShearAction : public SymbolPartMatrixAction {
// ----------------------------------------------------------------------------- : Scaling symbol parts
/// Scale some symbol parts
class SymbolPartScaleAction : public SymbolPartAction {
class SymbolPartScaleAction : public SymbolPartsAction {
public:
SymbolPartScaleAction(const set<SymbolPartP>& parts, int scaleX, int scaleY);
......@@ -127,13 +136,13 @@ class SymbolPartScaleAction : public SymbolPartAction {
void update();
private:
set<SymbolPartP> parts; ///< Parts to scale
Vector2D old_min, old_size; ///< the original pos/size
Vector2D new_real_min, new_real_size; ///< the target pos/sizevoid shearBy(const Vector2D& shear)
Vector2D new_min, new_size; ///< the target pos/size after applying constrains
int scaleX, scaleY; ///< to what corner are we attached?
/// Transform everything in the parts
void transformAll();
void transformPart(SymbolPart&);
/// Transform a single vector
inline Vector2D transform(const Vector2D& v);
public:
......@@ -144,7 +153,7 @@ class SymbolPartScaleAction : public SymbolPartAction {
// ----------------------------------------------------------------------------- : Change combine mode
/// Change the name of a symbol part
class CombiningModeAction : public SymbolPartListAction {
class CombiningModeAction : public SymbolPartsAction {
public:
// All parts must be SymbolParts
CombiningModeAction(const set<SymbolPartP>& parts, SymbolShapeCombine mode);
......@@ -153,22 +162,26 @@ class CombiningModeAction : public SymbolPartListAction {
virtual void perform(bool to_undo);
private:
void add(const SymbolPartP&, SymbolShapeCombine mode);
vector<pair<SymbolShapeP,SymbolShapeCombine> > parts; ///< Affected parts with new combining modes
};
// ----------------------------------------------------------------------------- : Change name
/// Change the name of a symbol part
class SymbolPartNameAction : public SymbolPartListAction {
class SymbolPartNameAction : public SymbolPartAction {
public:
SymbolPartNameAction(const SymbolPartP& part, const String& name);
SymbolPartNameAction(const SymbolPartP& part, const String& name, size_t old_cursor, size_t new_cursor);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
virtual bool merge(const Action& action);
private:
public:
SymbolPartP part; ///< Affected part
String part_name; ///< New name
size_t old_cursor; ///< Cursor position
size_t new_cursor; ///< Cursor position
};
// ----------------------------------------------------------------------------- : Add symbol part
......@@ -224,10 +237,10 @@ class DuplicateSymbolPartsAction : public SymbolPartListAction {
// ----------------------------------------------------------------------------- : Reorder symbol parts
/// Change the position of a part in a symbol, by swapping two parts.
/// Change the position of a part in a symbol, by moving a part.
class ReorderSymbolPartsAction : public SymbolPartListAction {
public:
ReorderSymbolPartsAction(Symbol& symbol, size_t part_id1, size_t part_id2);
ReorderSymbolPartsAction(Symbol& symbol, size_t old_position, size_t new_position);
virtual String getName(bool to_undo) const;
virtual void perform(bool to_undo);
......@@ -235,7 +248,7 @@ class ReorderSymbolPartsAction : public SymbolPartListAction {
private:
Symbol& symbol; ///< Symbol to swap the parts in
public:
size_t part_id1, part_id2; ///< Indices of parts to swap
size_t old_position, new_position; ///< Positions to move from and to
};
......
......@@ -349,8 +349,8 @@ SinglePointRemoveAction::SinglePointRemoveAction(const SymbolShapeP& shape, UInt
Vector2D p = c.pointAt(t);
Vector2D distP = point->pos - p;
// adjust handle sizes
point1.other.delta_after *= ssqrt(distP.dot(point1.other.delta_after) /point1.other.delta_after.lengthSqr()) + 1;
point2.other.delta_before *= ssqrt(distP.dot(point2.other.delta_before)/point2.other.delta_before.lengthSqr()) + 1;
point1.other.delta_after *= ssqrt(dot(distP, point1.other.delta_after) /point1.other.delta_after.lengthSqr()) + 1;
point2.other.delta_before *= ssqrt(dot(distP, point2.other.delta_before)/point2.other.delta_before.lengthSqr()) + 1;
// unlock if needed
if (point1.other.lock == LOCK_SIZE) point1.other.lock = LOCK_DIR;
......
......@@ -245,7 +245,7 @@ void mark_corners(SymbolShape& shape) {
Vector2D after = .6 * shape.getPoint(i+1)->pos + .2 * shape.getPoint(i+2)->pos + .1 * shape.getPoint(i+3)->pos + .1 * shape.getPoint(i+4)->pos;
before = (before - current.pos).normalized();
after = (after - current.pos).normalized();
if (before.dot(after) >= -0.25f) {
if (dot(before,after) >= -0.25f) {
// corner
current.lock = LOCK_FREE;
} else {
......@@ -285,8 +285,8 @@ void merge_corners(SymbolShape& shape) {
for (int j = 0 ; j < 4 ; ++j) {
Vector2D a_ = (shape.getPoint(i-j-1)->pos - prev.pos).normalized();
Vector2D b_ = (shape.getPoint(i+j)->pos - cur.pos).normalized();
double a_dot = a_.dot(ab);
double b_dot = -b_.dot(ab);
double a_dot = dot(a_, ab);
double b_dot = -dot(b_, ab);
if (a_dot < min_a_dot) {
min_a_dot = a_dot;
a = a_;
......@@ -300,8 +300,8 @@ void merge_corners(SymbolShape& shape) {
// t a + ab = u b, solve for t,u
// Gives us:
// t = ab cross b / b cross a
double tden = max(0.00000001, b.cross(a));
double t = ab.cross(b) / tden;
double tden = max(0.00000001, cross(b,a));
double t = cross(ab,b) / tden;
// do these tangent lines intersect, and not too far away?
// if so, then the intersection point is the merged point
if (t >= 0 && t < 20.0) {
......@@ -358,7 +358,7 @@ void straighten(SymbolShape& shape) {
Vector2D bb = next.delta_before.normalized();
// if the area beneath the polygon formed by the handles is small
// then it is a straight line
double cpDot = abs(aa.cross(ab)) + abs(bb.cross(ab));
double cpDot = abs(cross(aa,ab)) + abs(cross(bb,ab));
if (cpDot < treshold) {
cur.segment_after = next.segment_before = SEGMENT_LINE;
cur.delta_after = next.delta_before = Vector2D();
......
......@@ -94,6 +94,11 @@ Vector2D& ControlPoint::getOther(WhichHandle wh) {
// ----------------------------------------------------------------------------- : SymbolPart
void SymbolPart::calculateBounds() {
min_pos = Vector2D::infinity();
max_pos = -Vector2D::infinity();
}
IMPLEMENT_REFLECTION(SymbolPart) {
REFLECT_IF_NOT_READING {
String type = typeName();
......@@ -235,6 +240,19 @@ SymbolPartP SymbolGroup::clone() const {
return part;
}
void SymbolGroup::calculateBounds() {
FOR_EACH(p, parts) p->calculateBounds();
calculateBoundsNonRec();
}
void SymbolGroup::calculateBoundsNonRec() {
min_pos = Vector2D::infinity();
max_pos = -Vector2D::infinity();
FOR_EACH(p, parts) {
min_pos = piecewise_min(min_pos, p->min_pos);
max_pos = piecewise_max(max_pos, p->max_pos);
}
}
IMPLEMENT_REFLECTION(SymbolGroup) {
REFLECT_BASE(SymbolPart);
REFLECT(parts);
......
......@@ -112,6 +112,9 @@ class SymbolPart : public IntrusivePtrVirtualBase {
public:
/// Name/label for this part
String name;
/// Position and size of the part.
/** this is the smallest axis aligned bounding box that fits around the part */
Vector2D min_pos, max_pos;
/// Type of this part
virtual String typeName() const = 0;
......@@ -130,6 +133,9 @@ class SymbolPart : public IntrusivePtrVirtualBase {
virtual SymbolGroup* isSymbolGroup() { return nullptr; }
virtual const SymbolGroup* isSymbolGroup() const { return nullptr; }
/// Calculate the position and size of the part (min_pos and max_pos)
virtual void calculateBounds();
DECLARE_REFLECTION_VIRTUAL();
};
......@@ -162,9 +168,6 @@ class SymbolShape : public SymbolPart {
SymbolShapeCombine combine;
// Center of rotation, relative to the part, when the part is scaled to [0..1]
Vector2D rotation_center;
/// Position and size of the part
/// this is the smallest axis aligned bounding box that fits around the part
Vector2D min_pos, max_pos;
SymbolShape();
......@@ -183,7 +186,7 @@ class SymbolShape : public SymbolPart {
void enforceConstraints();
/// Calculate the position and size of the part
void calculateBounds();
virtual void calculateBounds();
DECLARE_REFLECTION();
};
......@@ -221,7 +224,7 @@ class SymbolSymmetry : public SymbolPart {
/// A group of symbol parts
class SymbolGroup : public SymbolPart {
public:
vector<SymbolPartP> parts; ///< The parts in this group
vector<SymbolPartP> parts; ///< The parts in this group, first item is on top
virtual String typeName() const;
virtual SymbolPartP clone() const;
......@@ -229,16 +232,18 @@ class SymbolGroup : public SymbolPart {
virtual SymbolGroup* isSymbolGroup() { return this; }
virtual const SymbolGroup* isSymbolGroup() const { return this; }
virtual void calculateBounds();
/// re-calculate the bounds, but not of the contained parts
void calculateBoundsNonRec();
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Symbol
/// An editable symbol, consists of any number of SymbolParts
class Symbol : public IntrusivePtrBase<Symbol> {
class Symbol : public SymbolGroup {
public:
/// The parts of this symbol, first item is on top
vector<SymbolPartP> parts;
/// Actions performed on this symbol and the parts in it
ActionStack actions;
......
......@@ -203,7 +203,7 @@ bool pos_on_line(const Vector2D& pos, double range, const Vector2D& p1, const Ve
Vector2D p21 = p2 - p1;
double p21len = p21.lengthSqr();
if (p21len < 0.00001) return false; // line is too short
t = p21.dot(pos - p1) / p21len; // 'time' on line p1->p2
t = dot(p21, pos - p1) / p21len; // 'time' on line p1->p2
if (t < 0 || t > 1) return false; // outside segment
pOut = p1 + p21 * t; // point on line
Vector2D dist = pOut - pos; // distance to line
......
......@@ -9,7 +9,9 @@
#include <gui/symbol/basic_shape_editor.hpp>
#include <gui/util.hpp>
#include <util/window_id.hpp>
#include <data/settings.hpp>
#include <data/action/symbol.hpp>
#include <data/action/symbol_part.hpp>
#include <wx/spinctrl.h>
// ----------------------------------------------------------------------------- : SymbolBasicShapeEditor
......@@ -108,7 +110,7 @@ void SymbolBasicShapeEditor::onMouseDrag (const Vector2D& from, const Vector2D&
// Resize the object
if (drawing) {
end = to;
makeShape(start, end, ev.ControlDown(), ev.ShiftDown());
makeShape(start, end, ev.ControlDown(), settings.symbol_grid_snap, ev.ShiftDown());
control.Refresh(false);
}
}
......@@ -119,7 +121,7 @@ void SymbolBasicShapeEditor::onKeyChange(wxKeyEvent& ev) {
if (drawing) {
if (ev.GetKeyCode() == WXK_CONTROL || ev.GetKeyCode() == WXK_SHIFT) {
// changed constrains
makeShape(start, end, ev.ControlDown(), ev.ShiftDown());
makeShape(start, end, ev.ControlDown(), settings.symbol_grid_snap, ev.ShiftDown());
control.Refresh(false);
} else if (ev.GetKeyCode() == WXK_ESCAPE) {
// cancel drawing
......@@ -156,7 +158,12 @@ inline double sgn(double d) {
return d < 0 ? - 1 : 1;
}
void SymbolBasicShapeEditor::makeShape(const Vector2D& a, const Vector2D& b, bool constrained, bool centered) {
void SymbolBasicShapeEditor::makeShape(Vector2D a, Vector2D b, bool constrained, bool snap, bool centered) {
// snap
if (snap) {
a = snap_vector(a, settings.symbol_grid_size);
b = snap_vector(b, settings.symbol_grid_size);
}
// constrain
Vector2D size = b - a;
if (constrained) {
......
......@@ -63,7 +63,7 @@ class SymbolBasicShapeEditor : public SymbolEditorBase {
/** when centered: a = center, b-a = radius
* otherwise: a = top left, b = bottom right
*/
void makeShape(const Vector2D& a, const Vector2D& b, bool constrained, bool centered);
void makeShape(Vector2D a, Vector2D b, bool constrained, bool snap, bool centered);
/// Make the shape, centered in c, with radius r
void makeCenteredShape(const Vector2D& c, Vector2D r, bool constrained);
......
......@@ -162,20 +162,20 @@ void SymbolControl::draw(DC& dc) {
SymbolViewer::draw(dc);
// draw grid
if (settings.symbol_grid) {
wxSize s = dc.GetSize();
double lines = settings.symbol_grid_size;
double end = rotation.trS(1);
for (int i = 0 ; i <= lines ; ++i) {
int x = (int) floor(rotation.trS(i/lines-0.0001));
//dc.SetPen(Color(0, i%5 == 0 ? 64 : 31, 0));
//dc.SetPen(Color(i%5 == 0 ? 64 : 31, 0, 0));
dc.SetLogicalFunction(wxAND);
dc.SetPen(i%5 == 0 ? Color(191,255,191) : Color(191, 255, 191));
dc.DrawLine(x, 0, x, s.y);
dc.DrawLine(0, x, s.x, x);
dc.DrawLine(x, 0, x, end);
dc.DrawLine(0, x, end, x);
dc.SetLogicalFunction(wxOR);
dc.SetPen(i%5 == 0 ? Color(0,63,0) : Color(0, 31, 0));
dc.DrawLine(x, 0, x, s.y);
dc.DrawLine(0, x, s.x, x);
dc.DrawLine(x, 0, x, end);
dc.DrawLine(0, x, end, x);
}
dc.SetLogicalFunction(wxCOPY);
}
......@@ -251,7 +251,7 @@ void SymbolControl::onUpdateUI(wxUpdateUIEvent& ev) {
ev.Check(editor->modeToolId() == ev.GetId());
if (ev.GetId() == ID_MODE_POINTS) {
// can only edit points when a single part is selected <TODO?>
ev.Enable(selected_parts.size() == 1);
ev.Enable(selected_parts.size() == 1 && (*selected_parts.begin())->isSymbolShape());
}
break;
case ID_MODE_PAINT:
......
......@@ -72,7 +72,8 @@ class SymbolControl : public wxControl, public SymbolViewer {
public:
/// What parts are selected?
set<SymbolPartP> selected_parts;
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
/// Parent window
SymbolWindow* parent;
......
This diff is collapsed.
......@@ -11,61 +11,80 @@
#include <util/prec.hpp>
#include <data/symbol.hpp>
#include <wx/listctrl.h>
// ----------------------------------------------------------------------------- : SymbolPartList
// ----------------------------------------------------------------------------- : Events
// A list view of parts of a symbol
class SymbolPartList : public wxListCtrl, public SymbolView {
public:
SymbolPartList(Window* parent, int id, SymbolP symbol = SymbolP());
DECLARE_EVENT_TYPE(EVENT_PART_SELECT, <not used>)
DECLARE_EVENT_TYPE(EVENT_PART_ACTIVATE, <not used>)
/// Update the list
void update();
/// Handle EVENT_PART_SELECT events
#define EVT_PART_SELECT( id, handler) EVT_COMMAND(id, EVENT_PART_SELECT, handler)
/// Handle EVENT_PART_ACTIVATE events
#define EVT_PART_ACTIVATE(id, handler) EVT_COMMAND(id, EVENT_PART_ACTIVATE, handler)
/// Is there a selection?
inline bool hasSelection() const { return selected != -1; }
/// Return the last part that was selected
/** @pre hasSelection()
*/
inline SymbolPartP getSelection() const { return getPart(selected); }
// ----------------------------------------------------------------------------- : SymbolPartList
/// Get a set of selected parts
void getSelectedParts(set<SymbolPartP>& sel);
/// Select the specified parts, and nothing else
void selectParts(const set<SymbolPartP>& sel);
class SymbolPartList : public wxScrolledWindow, public SymbolView {
public:
SymbolPartList(Window* parent, int id, set<SymbolPartP>& selection, SymbolP symbol = SymbolP());
/// Another symbol is being viewed
void onChangeSymbol();
virtual void onChangeSymbol();
/// Event handler for changes to the symbol
virtual void onAction(const Action& a, bool undone);
virtual void onAction(const Action&, bool);
protected:
/// Get the text of an item
virtual String OnGetItemText(long item, long col) const;
/// Get the icon of an item
virtual int OnGetItemImage(long item) const;
/// Update the control
void update();
/// Update only a subset of the parts
void updateParts(const set<SymbolPartP>& parts);
protected:
virtual wxSize DoGetBestSize() const;
private:
/// The selected item, or -1 if there is no selection
long selected;
set<SymbolPartP>& selection; ///< Store selection here
SymbolPartP mouse_down_on;
int drop_position;
int number_of_items;
/// Get a part from the symbol
SymbolPartP getPart(long item) const;
SymbolPartP typing_in;
size_t cursor;
/// Select an item, also in the list control
/// Deselects all other items
void selectItem(long item);
wxImageList state_icons;
struct Preview {
Preview() : up_to_date(false), image(25,25) {}
bool up_to_date;
Image image;
};
Preview symbol_preview; ///< Preview of the whole symbol
vector<Preview> part_previews;
static const int ITEM_HEIGHT = 25;
// --------------------------------------------------- : Event handling
DECLARE_EVENT_TABLE();
void onSelect (wxListEvent& ev);
void onDeselect (wxListEvent& ev);
void onLabelEdit(wxListEvent& ev);
void onSize (wxSizeEvent& ev);
void onDrag (wxMouseEvent& ev);
void onLeftDown (wxMouseEvent& ev);
void onLeftDClick(wxMouseEvent& ev);
void onLeftUp (wxMouseEvent& ev);
void onMotion (wxMouseEvent& ev);
void onChar(wxKeyEvent& ev);
void onPaint(wxPaintEvent&);
void onSize(wxSizeEvent&);
void OnDraw(DC& dc);
void sendEvent(int type);
void drawItem(DC& dc, int x, int& i, bool parent_active, const SymbolPartP& part);
const Image& itemPreview(int i, const SymbolPartP& part);
const Image& symbolPreview();
void updatePart(const set<SymbolPartP>& parts, int& i, bool parent_updated, const SymbolPartP& part);
SymbolPartP findItem(int i) const;
static SymbolPartP findItem(int& i, const SymbolPartP& part);
static int childCount(const SymbolPartP& part);
void updateCaret(DC& dc, int x, int y, int h, const SymbolPartP& part);
};
// ----------------------------------------------------------------------------- : EOF
......
......@@ -37,11 +37,7 @@ SymbolSelectEditor::SymbolSelectEditor(SymbolControl* control, bool rotate)
handleShearY = wxBitmap(rotate_image(shear,90));
handleCenter = wxBitmap(load_resource_image(_("handle_center")));
// Make sure all parts have updated bounds
FOR_EACH(p, getSymbol()->parts) {
if (SymbolShape* s = p->isSymbolShape()) {
s->calculateBounds();
}
}
getSymbol()->calculateBounds();
resetActions();
}
......@@ -132,24 +128,32 @@ void SymbolSelectEditor::destroyUI(wxToolBar* tb, wxMenuBar* mb) {
void SymbolSelectEditor::onUpdateUI(wxUpdateUIEvent& ev) {
if (ev.GetId() >= ID_SYMBOL_COMBINE && ev.GetId() < ID_SYMBOL_COMBINE_MAX) {
if (control.selected_parts.empty()) {
ev.Check(false);
ev.Enable(false);
} else {
ev.Enable(true);
bool enable = false;
bool check = true;
FOR_EACH(p, control.selected_parts) {
if (SymbolShape* s = p->isSymbolShape()) {
enable = true;
if (s->combine != ev.GetId() - ID_SYMBOL_COMBINE) {
check = false;
break;
}
} // disable when symmetries are selected?
}
ev.Check(check);
}
ev.Enable(enable);
ev.Check(enable && check);
} else if (ev.GetId() == ID_EDIT_DUPLICATE) {
ev.Enable(!control.selected_parts.empty());
} else if (ev.GetId() == ID_EDIT_GROUP) {
ev.Enable(control.selected_parts.size() >= 2);
} else if (ev.GetId() == ID_EDIT_UNGROUP) {
// is a group selected
FOR_EACH(p, control.selected_parts) {
if (p->isSymbolGroup()) {
ev.Enable(true);
return;
}
}
ev.Enable(false);
} else {
ev.Enable(false); // we don't know about this item
}
......@@ -165,10 +169,15 @@ void SymbolSelectEditor::onCommand(int id) {
control.Refresh(false);
} else if (id == ID_EDIT_DUPLICATE && !isEditing()) {
// duplicate selection, not when dragging
DuplicateSymbolPartsAction* action = new DuplicateSymbolPartsAction(
*getSymbol(), control.selected_parts
);
getSymbol()->actions.add(action);
getSymbol()->actions.add(new DuplicateSymbolPartsAction(*getSymbol(), control.selected_parts));
control.Refresh(false);
} else if (id == ID_EDIT_GROUP && !isEditing()) {
// group selection, not when dragging
getSymbol()->actions.add(new GroupSymbolPartsAction(*getSymbol(), control.selected_parts));
control.Refresh(false);
} else if (id == ID_EDIT_UNGROUP && !isEditing()) {
// ungroup selection, not when dragging
getSymbol()->actions.add(new UngroupSymbolPartsAction(*getSymbol(), control.selected_parts));
control.Refresh(false);
}
}
......@@ -383,6 +392,7 @@ void SymbolSelectEditor::onChar(wxKeyEvent& ev) {
if (ev.GetKeyCode() == WXK_DELETE) {
// delete selected parts
getSymbol()->actions.add(new RemoveSymbolPartsAction(*getSymbol(), control.selected_parts));
if (control.selected_parts.find(highlightPart) != control.selected_parts.end()) highlightPart = SymbolPartP(); // deleted it
control.selected_parts.clear();
resetActions();
control.Refresh(false);
......@@ -441,15 +451,25 @@ double SymbolSelectEditor::angleTo(const Vector2D& pos) {
}
SymbolPartP SymbolSelectEditor::findPart(const Vector2D& pos) {
FOR_EACH(p, getSymbol()->parts) {
if (SymbolShape* s = p->isSymbolShape()) {
if (point_in_shape(pos, *s)) return p;
} else if (SymbolSymmetry* s = p->isSymbolSymmetry()) {
SymbolPartP find_part(const SymbolPartP& part, const Vector2D& pos) {
if (SymbolShape* s = part->isSymbolShape()) {
if (point_in_shape(pos, *s)) return part;
} else if (SymbolSymmetry* s = part->isSymbolSymmetry()) {
// TODO
} else if (SymbolGroup* g = part->isSymbolGroup()) {
FOR_EACH(p, g->parts) {
if (find_part(p,pos)) return part;
}
} else {
throw InternalError(_("Invalid symbol part type"));
}
return SymbolPartP();
}
SymbolPartP SymbolSelectEditor::findPart(const Vector2D& pos) {
FOR_EACH(p, getSymbol()->parts) {
SymbolPartP found = find_part(p, pos);
if (found) return found;
}
return SymbolPartP();
}
......@@ -459,10 +479,8 @@ void SymbolSelectEditor::updateBoundingBox() {
minV = Vector2D::infinity();
maxV = -Vector2D::infinity();
FOR_EACH(p, control.selected_parts) {
if (SymbolShape* s = p->isSymbolShape()) {
minV = piecewise_min(minV, s->min_pos);
maxV = piecewise_max(maxV, s->max_pos);
}
minV = piecewise_min(minV, p->min_pos);
maxV = piecewise_max(maxV, p->max_pos);
}
/* // Find rotation center
center = Vector2D(0,0);
......
......@@ -74,6 +74,9 @@ void SymbolWindow::init(Window* parent, SymbolP symbol) {
menuEdit->Append(ID_EDIT_UNDO, _("undo"), _MENU_1_("undo",wxEmptyString), _HELP_("undo"));
menuEdit->Append(ID_EDIT_REDO, _("redo"), _MENU_1_("redo",wxEmptyString), _HELP_("redo"));
menuEdit->AppendSeparator();
menuEdit->Append(ID_EDIT_GROUP, _("group"), _MENU_("group"), _HELP_("group"));
menuEdit->Append(ID_EDIT_UNGROUP, _("ungroup"), _MENU_("ungroup"), _HELP_("ungroup"));
menuEdit->AppendSeparator();
menuEdit->Append(ID_EDIT_DUPLICATE, _("duplicate"), _MENU_("duplicate"), _HELP_("duplicate"));
menuBar->Append(menuEdit, _MENU_("edit"));
......@@ -82,6 +85,7 @@ void SymbolWindow::init(Window* parent, SymbolP symbol) {
menuTool->Append(ID_MODE_ROTATE, _("mode_rotate"), _MENU_("rotate"), _HELP_("rotate"), wxITEM_CHECK);
menuTool->Append(ID_MODE_POINTS, _("mode_curve"), _MENU_("points"), _HELP_("points"), wxITEM_CHECK);
menuTool->Append(ID_MODE_SHAPES, _("circle"), _MENU_("basic shapes"), _HELP_("basic shapes"), wxITEM_CHECK);
menuTool->Append(ID_MODE_SYMMETRY, _("mode_symmetry"), _MENU_("symmetry"), _HELP_("symmetry"), wxITEM_CHECK);
menuTool->Append(ID_MODE_PAINT, _("mode_paint"), _MENU_("paint"), _HELP_("paint"), wxITEM_CHECK);
menuBar->Append(menuTool, _MENU_("tool"));
......@@ -118,11 +122,11 @@ void SymbolWindow::init(Window* parent, SymbolP symbol) {
// Controls
control = new SymbolControl (this, ID_CONTROL, symbol);
parts = new SymbolPartList(this, ID_PART_LIST, symbol);
parts = new SymbolPartList(this, ID_PART_LIST, control->selected_parts, symbol);
// Lay out
wxSizer* es = new wxBoxSizer(wxHORIZONTAL);
es->Add(em, 0, wxEXPAND | wxTOP | wxBOTTOM | wxALIGN_CENTER, 1);
es->Add(em, 1, wxEXPAND | wxTOP | wxBOTTOM | wxALIGN_CENTER, 1);
emp->SetSizer(es);
wxSizer* s = new wxBoxSizer(wxHORIZONTAL);
......@@ -246,21 +250,21 @@ void SymbolWindow::onUpdateUI(wxUpdateUIEvent& ev) {
}
void SymbolWindow::onSelectFromList(wxListEvent& ev) {
void SymbolWindow::onSelectFromList(wxCommandEvent& ev) {
if (inSelectionEvent) return ;
inSelectionEvent = true;
parts->getSelectedParts(control->selected_parts);
control->onUpdateSelection();
inSelectionEvent = false;
}
void SymbolWindow::onActivateFromList(wxListEvent& ev) {
control->activatePart(control->getSymbol()->parts.at(ev.GetIndex()));
void SymbolWindow::onActivateFromList(wxCommandEvent& ev) {
//% control->activatePart(control->getSymbol()->parts.at(ev.GetIndex()));
// TODO
}
void SymbolWindow::onSelectFromControl() {
if (inSelectionEvent) return ;
inSelectionEvent = true;
parts->selectParts(control->selected_parts);
parts->Refresh(false);
inSelectionEvent = false;
}
......@@ -280,7 +284,6 @@ BEGIN_EVENT_TABLE(SymbolWindow, wxFrame)
EVT_TOOL_RANGE (ID_CHILD_MIN, ID_CHILD_MAX, SymbolWindow::onExtraTool)
EVT_UPDATE_UI (wxID_ANY, SymbolWindow::onUpdateUI)
EVT_LIST_ITEM_SELECTED (ID_PART_LIST, SymbolWindow::onSelectFromList)
EVT_LIST_ITEM_DESELECTED (ID_PART_LIST, SymbolWindow::onSelectFromList)
EVT_LIST_ITEM_ACTIVATED (ID_PART_LIST, SymbolWindow::onActivateFromList)
EVT_PART_SELECT (ID_PART_LIST, SymbolWindow::onSelectFromList)
EVT_PART_ACTIVATE (ID_PART_LIST, SymbolWindow::onActivateFromList)
END_EVENT_TABLE ()
......@@ -14,6 +14,7 @@
#include <wx/listctrl.h>
class SymbolControl;
//%%class SymbolPartList;
class SymbolPartList;
DECLARE_POINTER_TYPE(SymbolValue);
DECLARE_POINTER_TYPE(Set);
......@@ -62,9 +63,9 @@ class SymbolWindow : public Frame {
void onUpdateUI(wxUpdateUIEvent&);
/// Changing selected parts in the list
void onSelectFromList(wxListEvent& ev);
void onSelectFromList(wxCommandEvent& ev);
/// Activating a part: open the point editor
void onActivateFromList(wxListEvent& ev);
void onActivateFromList(wxCommandEvent& ev);
bool inSelectionEvent; ///< Prevent recursion in onSelect...
......
......@@ -96,6 +96,10 @@ bool MSE::OnInit() {
// Installer; install it
Installer::installFrom(argv[1], true);
return false;
} else if (arg == _("--symbol-editor")) {
Window* wnd = new SymbolWindow(nullptr);
wnd->Show();
return true;
} else if (arg == _("--create-installer")) {
// create an installer
Installer inst;
......
......@@ -12,6 +12,29 @@
#include <util/error.hpp>
#include <script/value.hpp> // for some strange reason the profile build needs this :(
// ----------------------------------------------------------------------------- : Color
template <> void GetDefaultMember::handle(const AColor& col) {
handle((const Color&)col);
}
template <> void Reader::handle(AColor& col) {
UInt r,g,b,a;
if (wxSscanf(getValue().c_str(),_("rgb(%u,%u,%u)"),&r,&g,&b)) {
col.Set(r,g,b);
col.alpha = 255;
} else if (wxSscanf(getValue().c_str(),_("rgba(%u,%u,%u,%u)"),&r,&g,&b,&a)) {
col.Set(r,g,b);
col.alpha = a;
}
}
template <> void Writer::handle(const AColor& col) {
if (col.alpha == 255) {
handle(String::Format(_("rgb(%u,%u,%u)"), col.Red(), col.Green(), col.Blue()));
} else {
handle(String::Format(_("rgba(%u,%u,%u,%u)"), col.Red(), col.Green(), col.Blue(), col.alpha));
}
}
// ----------------------------------------------------------------------------- : Symbol filtering
void filter_symbol(Image& symbol, const SymbolFilter& filter) {
......
......@@ -20,9 +20,10 @@ class SymbolFilter;
/// Color with alpha channel
class AColor : public Color {
public:
int alpha; ///< The alpha value, in the range [0..255]
inline AColor(int r, int g, int b, int a = 255) : Color(r,g,b), alpha(a) {}
inline AColor(const Color& color, int a = 255) : Color(color), alpha(a) {}
Byte alpha; ///< The alpha value, in the range [0..255]
inline AColor() : alpha(0) {}
inline AColor(Byte r, Byte g, Byte b, Byte a = 255) : Color(r,g,b), alpha(a) {}
inline AColor(const Color& color, Byte a = 255) : Color(color), alpha(a) {}
};
// ----------------------------------------------------------------------------- : Symbol filtering
......@@ -69,14 +70,14 @@ intrusive_ptr<SymbolFilter> read_new<SymbolFilter>(Reader& reader);
class SolidFillSymbolFilter : public SymbolFilter {
public:
inline SolidFillSymbolFilter() {}
inline SolidFillSymbolFilter(Color fill_color, Color border_color)
inline SolidFillSymbolFilter(const AColor& fill_color, const AColor& border_color)
: fill_color(fill_color), border_color(border_color)
{}
virtual AColor color(double x, double y, SymbolSet point) const;
virtual String fillType() const;
virtual bool operator == (const SymbolFilter& that) const;
private:
Color fill_color, border_color;
AColor fill_color, border_color;
DECLARE_REFLECTION();
};
......
......@@ -72,10 +72,8 @@ void SymbolViewer::draw(DC& dc) {
}
}
}
// Draw all parts, in reverse order (bottom to top)
FOR_EACH_REVERSE(p, symbol->parts) {
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, borderDC, interiorDC);
}
// Draw all parts
combineSymbolPart(dc, *symbol, paintedSomething, buffersFilled, borderDC, interiorDC);
// Output the final parts from the buffer
if (buffersFilled) {
......@@ -118,7 +116,8 @@ void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paint
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
// symmetry, already handled above
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH_CONST(p, g->parts) {
// Draw all parts, in reverse order (bottom to top)
FOR_EACH_CONST_REVERSE(p, g->parts) {
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, borderDC, interiorDC);
}
}
......@@ -147,6 +146,10 @@ void SymbolViewer::highlightPart(DC& dc, const SymbolShape& shape, HighlightStyl
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));
......@@ -164,8 +167,13 @@ void SymbolViewer::highlightPart(DC& dc, const SymbolSymmetry& sym) {
// TODO
}
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, style);
highlightPart(dc, *part, (HighlightStyle)(style | HIGHLIGHT_LESS));
}
}
......
......@@ -21,9 +21,11 @@ Image render_symbol(const SymbolP& symbol, double border_radius = 0.05, int size
// ----------------------------------------------------------------------------- : Symbol Viewer
enum HighlightStyle {
HIGHLIGHT_BORDER,
HIGHLIGHT_INTERIOR
enum HighlightStyle
{ HIGHLIGHT_BORDER = 0x01
, HIGHLIGHT_INTERIOR = 0x02
, HIGHLIGHT_LESS = 0x10
, HIGHLIGHT_BORDER_DOT = HIGHLIGHT_BORDER | HIGHLIGHT_LESS
};
/// Class that knows how to draw a symbol
......
......@@ -87,10 +87,13 @@ tool/mode_select IMAGE "tool/mode_select.png"
tool/mode_rotate IMAGE "tool/mode_rotate.png"
tool/mode_curve IMAGE "tool/mode_curve.png"
tool/mode_paint IMAGE "tool/mode_paint.png"
tool/mode_symmetry IMAGE "tool/mode_symmetry.png"
tool/apply IMAGE "tool/apply.png"
tool/duplicate IMAGE "tool/duplicate.png"
tool/grid IMAGE "tool/grid.png"
tool/grid_snap IMAGE "tool/grid_snap.png"
tool/group IMAGE "tool/group.png"
tool/ungroup IMAGE "tool/ungroup.png"
combine_or IMAGE "../common/combine_or.png"
combine_sub IMAGE "../common/combine_sub.png"
......@@ -102,6 +105,17 @@ combine_over IMAGE "../common/combine_over.png"
combine_border IMAGE "../common/combine_border.png"
symmetry_rotation IMAGE "../common/symmetry_rotation.png"
symmetry_reflection IMAGE "../common/symmetry_reflection.png"
symbol_group IMAGE "../common/symbol_group.png"
icon_combine_merge IMAGE "../common/icon_combine_merge.png"
icon_combine_subtract IMAGE "../common/icon_combine_subtract.png"
icon_combine_intersection IMAGE "../common/icon_combine_intersection.png"
icon_combine_difference IMAGE "../common/icon_combine_difference.png"
icon_combine_overlap IMAGE "../common/icon_combine_overlap.png"
icon_combine_border IMAGE "../common/icon_combine_border.png"
icon_symmetry_rotation IMAGE "../common/icon_symmetry_rotation.png"
icon_symmetry_reflection IMAGE "../common/icon_symmetry_reflection.png"
icon_symbol_group IMAGE "../common/icon_symbol_group.png"
handle_rotate IMAGE "../common/handle_rotate.png"
handle_shear_x IMAGE "../common/handle_shear_x.png"
......
......@@ -37,7 +37,14 @@ class RealSize {
: width(w), height(h)
{}
inline RealSize(wxSize s)
: width(s.GetWidth()), height(s.GetHeight())
: width(s.x), height(s.y)
{}
inline explicit RealSize(const Vector2D& v)
: width(v.x), height(v.y)
{}
/// size of an image
inline explicit RealSize(const wxImage& img)
: width(img.GetWidth()), height(img.GetHeight())
{}
/// Negation of a size, negates both components
......
......@@ -74,15 +74,6 @@ class Vector2D {
x /= r; y /= r;
}
/// Inner product with another vector
inline double dot(Vector2D p2) const {
return (x * p2.x) + (y * p2.y);
}
/// Outer product with another vector
inline double cross(Vector2D p2) const {
return (x * p2.y) - (y * p2.x);
}
/// Piecewise multiplication
inline Vector2D mul(Vector2D p2) {
return Vector2D(x * p2.x, y * p2.y);
......@@ -92,11 +83,6 @@ class Vector2D {
return Vector2D(x / p2.x, y / p2.y);
}
/// Apply a 'matrix' to this vector
inline Vector2D mul(Vector2D mx, Vector2D my) {
return Vector2D(dot(mx), dot(my));
}
/// Returns the square of the length of this vector
inline double lengthSqr() const {
return x*x + y*y;
......@@ -106,7 +92,7 @@ class Vector2D {
return sqrt(lengthSqr());
}
/// Returns a normalized version of this vector
/// i.e. length() == 1
/** i.e. length() == 1 */
inline Vector2D normalized() const {
return *this / length();
}
......@@ -122,6 +108,15 @@ class Vector2D {
}
};
/// Inner product of two vectors
inline double dot(const Vector2D& a, const Vector2D& b) {
return (a.x * b.x) + (a.y * b.y);
}
/// Length of the outer product of two vectors
inline double cross(const Vector2D& a, const Vector2D& b) {
return (a.x * b.y) - (a.y * b.x);
}
/// Piecewise minimum
inline Vector2D piecewise_min(const Vector2D& a, const Vector2D& b) {
return Vector2D(
......@@ -140,6 +135,23 @@ inline Vector2D piecewise_max(const Vector2D& a, const Vector2D& b) {
inline Vector2D operator * (double a, const Vector2D& b) { return b * a; }
// ----------------------------------------------------------------------------- : Matrix2D
/// A two dimensional transformation matrix, simply two vectors
class Matrix2D {
public:
Vector2D mx, my;
inline Matrix2D() {}
inline Matrix2D(const Vector2D& mx, const Vector2D& my) : mx(mx), my(my) {}
inline Matrix2D(double a, double b, double c, double d) : mx(a,b), my(c,d) {}
};
/// vector-matrix product
inline Vector2D operator * (const Vector2D& a, const Matrix2D& m) {
return Vector2D(dot(a,m.mx), dot(a,m.my));
}
// ----------------------------------------------------------------------------- : EOF
#endif
......@@ -76,6 +76,7 @@ enum MenuID {
, ID_MODE_ROTATE
, ID_MODE_POINTS
, ID_MODE_SHAPES
, ID_MODE_SYMMETRY
, ID_MODE_PAINT
, ID_MODE_MAX
};
......
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