Commit 697f1265 authored by twanvl's avatar twanvl

Implemented rotation and reflection

parent b90e2dda
......@@ -78,14 +78,17 @@ menu:
store symbol: S&tore Ctrl+Enter
close symbol editor:Close Alt+F4
duplicate: Duplicate Ctrl+D
duplicate: &Duplicate Ctrl+D
group: &Group Ctrl+G
ungroup: &Ungroup Ctrl+U
tool: &Tool
select: &Select F5
rotate: &Rotate F6
points: &Points F7
basic shapes: &Basic Shapes F8
paint: P&aint F9
symmetry: S&ymmetry F9
paint: P&aint F10
############################################################## Menu help texts
help:
......@@ -179,6 +182,8 @@ help:
close symbol editor:Closes the symbol editor
duplicate: Duplicates the selected shapes
group: Group the selected shapes together
ungroup: Break up the selected group
grid: Show gridlines
snap: Snap shapes and points to gridlines
......@@ -188,6 +193,7 @@ help:
rotate: Rotate and shear shapes
points: Edit control points for a shape in the symbol
basic shapes: Draw basic shapes, such as rectangles and circles
symmetry: Add symmetries to the symbol
paint: Paint on the shape using a paintbrush
select editor:
......@@ -247,6 +253,7 @@ tool:
rotate: Rotate
points: Points
basic shapes: Basic Shapes
symmetry: Symmetry
paint: Paint
merge: Merge
......@@ -261,6 +268,9 @@ tool:
polygon: Polygon
star: Star
rotation: Rotation
reflection: Reflection
line segment: Line
curve segment: Curve
free point: Free
......@@ -314,7 +324,8 @@ tooltip:
rotate: Rotate (F6)
points: Points (F7)
basic shapes: Basic Shapes (F8)
paint: Paint on Shape (F9)
symmetry: Symmetry (F9)
paint: Paint on Shape (F10)
merge: Merge with shapes below
subtract: Subtract from shapes below
......@@ -328,6 +339,9 @@ tooltip:
polygon: Polygon
star: Star
rotation: Rotational symmetry (wheel)
reflection: Reflectional symmetry (mirror)
line segment: To straigt line
curve segment: To curve
free point: Unlock point
......@@ -513,6 +527,8 @@ action:
reorder parts: Reorder
change combine mode:Change combine mode
change shape name: Change shape name
group parts: Group
ungroup parts: Ungroup
# Symbol Part Actions
convert to line: Convert to line
......@@ -608,6 +624,10 @@ type:
polygon: polygon
star: star
rotation: rotation
reflection: reflection
group: group
point: point
points: points
handle: handle
......
......@@ -62,13 +62,12 @@ void SymbolPartMoveAction::movePart(SymbolPart& part) {
}
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
s->center -= moved;
} else if (SymbolGroup* g = part.isSymbolGroup()) {
}
if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
movePart(*p);
}
g->calculateBoundsNonRec();
} else {
throw InternalError(_("Invalid symbol part type"));
}
}
......@@ -108,13 +107,12 @@ void SymbolPartMatrixAction::transform(SymbolPart& part, const Matrix2D& m) {
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
s->center = (s->center - center) * m + center;
s->handle = s->handle * m;
} else if (SymbolGroup* g = part.isSymbolGroup()) {
}
if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
transform(*p, m);
}
g->calculateBoundsNonRec();
} else {
throw InternalError(_("Invalid symbol part type"));
}
}
......@@ -284,13 +282,12 @@ void SymbolPartScaleAction::transformPart(SymbolPart& part) {
} else if (SymbolSymmetry* s = part.isSymbolSymmetry()) {
transform(s->center);
s->handle.mul(new_size.div(old_size));
} else if (SymbolGroup* g = part.isSymbolGroup()) {
}
if (SymbolGroup* g = part.isSymbolGroup()) {
FOR_EACH(p, g->parts) {
transformPart(*p);
}
g->calculateBoundsNonRec();
} else {
throw InternalError(_("Invalid symbol part type"));
}
}
......
......@@ -59,7 +59,7 @@ void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2
// ----------------------------------------------------------------------------- : Drawing
void curve_subdivide(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, const Rotation& rot, vector<wxPoint>& out, UInt level) {
void curve_subdivide(const BezierCurve& c, const Vector2D& p0, const Vector2D& p1, double t0, double t1, const Vector2D& origin, const Matrix2D& m, vector<wxPoint>& out, UInt level) {
if (level <= 0) return;
double midtime = (t0+t1) * 0.5f;
Vector2D midpoint = c.pointAt(midtime);
......@@ -70,23 +70,23 @@ void curve_subdivide(const BezierCurve& c, const Vector2D& p0, const Vector2D& p
double treshold = fabs( atan2(d0.x,d0.y) - atan2(d1.x,d1.y)) * (p0-p1).lengthSqr();
bool subdivide = treshold >= .0001;
// subdivide left
curve_subdivide(c, p0, midpoint, t0, midtime, rot, out, level - 1);
curve_subdivide(c, p0, midpoint, t0, midtime, origin, m, out, level - 1);
// add midpoint
if (subdivide) {
out.push_back(rot.tr(midpoint));
out.push_back(origin + midpoint * m);
}
// subdivide right
curve_subdivide(c, midpoint, p1, midtime, t1, rot, out, level - 1);
curve_subdivide(c, midpoint, p1, midtime, t1, origin, m, out, level - 1);
}
void segment_subdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out) {
void segment_subdivide(const ControlPoint& p0, const ControlPoint& p1, const Vector2D& origin, const Matrix2D& m, vector<wxPoint>& out) {
assert(p0.segment_after == p1.segment_before);
// always the start
out.push_back(rot.tr(p0.pos));
out.push_back(origin + p0.pos * m);
if (p0.segment_after == SEGMENT_CURVE) {
// need more points?
BezierCurve curve(p0,p1);
curve_subdivide(curve, p0.pos, p1.pos, 0, 1, rot, out, 5);
curve_subdivide(curve, p0.pos, p1.pos, 0, 1, origin, m, out, 5);
}
}
......
......@@ -15,7 +15,7 @@
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <util/rotation.hpp>
#include <util/vector2d.hpp>
#include <data/symbol.hpp>
// ----------------------------------------------------------------------------- : Evaluation
......@@ -65,9 +65,9 @@ void deCasteljau(const Vector2D& a1, Vector2D& a21, Vector2D& a34, const Vector2
/// Devide a segment into a number of straight lines for display purposes
/** Adds the resulting corner points of those lines to out, the last point is not added.
* All points are converted to display coordinates using rot.tr
* All points are converted to display coordinates by multiplying with m and adding origin
*/
void segment_subdivide(const ControlPoint& p0, const ControlPoint& p1, const Rotation& rot, vector<wxPoint>& out);
void segment_subdivide(const ControlPoint& p0, const ControlPoint& p1, const Vector2D& origin, const Matrix2D& m, vector<wxPoint>& out);
// ----------------------------------------------------------------------------- : Bounds
......
......@@ -235,7 +235,7 @@ void SymbolControl::onChar(wxKeyEvent& ev) {
void SymbolControl::onSize(wxSizeEvent& ev) {
wxSize s = GetClientSize();
rotation.setZoom(min(s.GetWidth(), s.GetHeight()));
setZoom(min(s.GetWidth(), s.GetHeight()));
Refresh(false);
}
void SymbolControl::onUpdateUI(wxUpdateUIEvent& ev) {
......@@ -248,6 +248,9 @@ void SymbolControl::onUpdateUI(wxUpdateUIEvent& ev) {
// can only edit points when a shape is available
ev.Enable(selected_parts.getAShape());
}
if (ev.GetId() == ID_MODE_SYMMETRY) {
ev.Enable(!selected_parts.empty());
}
break;
case ID_MODE_PAINT:
ev.Enable(false); // TODO
......
......@@ -24,10 +24,11 @@ void SymbolPartsSelection::clear() {
}
bool SymbolPartsSelection::select(const SymbolPartP& part, SelectMode mode) {
assert(part);
// make sure part is not the decendent of a part that is already selected
if (mode != SELECT_OVERRIDE) {
FOR_EACH(s, selection) {
if (isAncestor(s.get(), part.get())) return false;
if (s != part && s->isAncestor(*part)) return false;
}
}
// select
......@@ -70,15 +71,6 @@ void SymbolPartsSelection::clearChildren(SymbolPart* part) {
}
}
bool SymbolPartsSelection::isAncestor(SymbolPart* ancestor, SymbolPart* part) {
if (SymbolGroup* g = ancestor->isSymbolGroup()) {
FOR_EACH(p, g->parts) {
if (part == p.get()) return true;
if (isAncestor(p.get(), part)) return true;
}
}
return false;
}
// ----------------------------------------------------------------------------- : Position based
......@@ -116,7 +108,7 @@ bool SymbolPartsSelection::selectRect(const Vector2D& a, const Vector2D& b, cons
}
bool SymbolPartsSelection::selectRect(const SymbolGroup& parent, const Vector2D& a, const Vector2D& b, const Vector2D& c) {
bool changes = false;
FOR_EACH_CONST(p, root->parts) {
FOR_EACH_CONST(p, parent.parts) {
bool in_ab = (p->min_pos.x >= min(a.x, b.x) && p->min_pos.y >= min(a.y, b.y) && p->max_pos.x <= max(a.x, b.x) && p->max_pos.y <= max(a.y, b.y));
bool in_bc = (p->min_pos.x >= min(a.x, c.x) && p->min_pos.y >= min(a.y, c.y) && p->max_pos.x <= max(a.x, c.x) && p->max_pos.y <= max(a.y, c.y));
if (in_ab != in_bc) {
......
......@@ -73,8 +73,6 @@ class SymbolPartsSelection {
/// Make sure not both a parent and its child/decendant are selected
void clearChildren (SymbolPart* part);
/// Is a part another's ancestor?
static bool isAncestor(SymbolPart* ancestor, SymbolPart* part);
};
// ----------------------------------------------------------------------------- : EOF
......
......@@ -17,6 +17,8 @@
/// 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);
// --------------------------------------------------- : Drawing
......
......@@ -29,7 +29,7 @@ SymbolWindow::SymbolWindow(Window* parent) {
SymbolWindow::SymbolWindow(Window* parent, const String& filename) {
// open file
Reader reader(filename);
Reader reader(new_shared1<wxFileInputStream>(filename), filename);
SymbolP symbol;
reader.handle_greedy(symbol);
init(parent, symbol);
......
......@@ -15,12 +15,12 @@ DECLARE_TYPEOF_COLLECTION(SymbolPartP);
// ----------------------------------------------------------------------------- : Simple rendering
Image render_symbol(const SymbolP& symbol, double border_radius, int size) {
SymbolViewer viewer(symbol, border_radius);
SymbolViewer viewer(symbol, size, border_radius);
Bitmap bmp(size, size);
wxMemoryDC dc;
dc.SelectObject(bmp);
clearDC(dc, Color(0,128,0));
viewer.rotation.setZoom(size);
viewer.setZoom(size);
viewer.draw(dc);
dc.SelectObject(wxNullBitmap);
return bmp.ConvertToImage();
......@@ -28,13 +28,20 @@ Image render_symbol(const SymbolP& symbol, double border_radius, int size) {
// ----------------------------------------------------------------------------- : Constructor
SymbolViewer::SymbolViewer(const SymbolP& symbol, double border_radius)
SymbolViewer::SymbolViewer(const SymbolP& symbol, double size, double border_radius)
: border_radius(border_radius)
, rotation(0, RealRect(0,0,500,500), 500)
, rotation(0, RealRect(0,0,size,size), size)
, multiply(size,0,0,size)
, origin(0,0)
{
setSymbol(symbol);
}
void SymbolViewer::setZoom(double zoom) {
rotation.setZoom(zoom);
multiply = Matrix2D(zoom,0, 0,zoom);
}
// ----------------------------------------------------------------------------- : Drawing
typedef shared_ptr<wxMemoryDC> MemoryDCP;
......@@ -73,16 +80,16 @@ void SymbolViewer::draw(DC& dc) {
}
}
// Draw all parts
combineSymbolPart(dc, *symbol, paintedSomething, buffersFilled, borderDC, interiorDC);
combineSymbolPart(dc, *symbol, paintedSomething, buffersFilled, true, borderDC, interiorDC);
// Output the final parts from the buffer
if (buffersFilled) {
combineBuffers(dc, borderDC.get(), interiorDC.get());
}
}
void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, MemoryDCP& borderDC, MemoryDCP& interiorDC) {
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()) {
if (s->combine == SYMBOL_COMBINE_OVERLAP && buffersFilled) {
if (s->combine == SYMBOL_COMBINE_OVERLAP && buffersFilled && allow_overlap) {
// We will be overlapping some previous parts, write them to the screen
combineBuffers(dc, borderDC.get(), interiorDC.get());
// Clear the buffers
......@@ -111,14 +118,61 @@ void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paint
combineSymbolShape(*s, *borderDC, *interiorDC, false, false);
buffersFilled = true;
}
// Paint symmetric versions of this part
// TODO
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
// symmetry, already handled above
// Draw all parts, in reverse order (bottom to top), also draw rotated copies
double b = 2 * atan2(s->handle.y, s->handle.x);
Matrix2D old_m = multiply;
Vector2D old_o = origin;
int copies = s->kind == SYMMETRY_REFLECTION ? s->copies / 2 * 2 : s->copies;
FOR_EACH_CONST_REVERSE(p, s->parts) {
for (int i = 0 ; i < copies ; ++i) {
if (s->clip) {
// todo: clip
}
double a = i * 2 * M_PI / copies;
if (s->kind == SYMMETRY_ROTATION || i % 2 == 0) {
// set matrix
// Calling:
// - p the input point
// - p' the output point
// - rot our rotation matrix
// - d out origin
// - o the current origin (old_o)
// - m the current matrix (old_m)
// We want:
// p' = ((p - d) * rot + d) * m + o
// = (p * rot - d * rot + d) * m + o
// = p * rot * m + (d - d * rot) * m + o
Matrix2D rot(cos(a),-sin(a), sin(a),cos(a));
multiply.mx = rot.mx * old_m;
multiply.my = rot.my * old_m;
origin = old_o + (s->center - s->center * rot) * old_m;
} else {
// reflection
// Calling angle = b
// Matrix2D ref(cos(b),sin(b), sin(b),-cos(b));
// Matrix2D rot(cos(a),-sin(a), sin(a),cos(a));
//
// ref * rot
// /cos b sin b\ /cos a -sin a\
// = \sin b -cos b/ \sin a cos a/
// = /cos(a+b) sin(a+b)\
// \sin(a+b) -cos(a+b)/
Matrix2D rot(cos(a+b),sin(a+b), sin(a+b),-cos(a+b));
multiply.mx = rot.mx * old_m;
multiply.my = rot.my * old_m;
origin = old_o + (s->center - s->center * rot) * old_m;
}
// draw rotated copy
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, allow_overlap && i == 0, borderDC, interiorDC);
}
}
multiply = old_m;
origin = old_o;
} else if (const SymbolGroup* g = part.isSymbolGroup()) {
// Draw all parts, in reverse order (bottom to top)
FOR_EACH_CONST_REVERSE(p, g->parts) {
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, borderDC, interiorDC);
combineSymbolPart(dc, *p, paintedSomething, buffersFilled, allow_overlap, borderDC, interiorDC);
}
}
}
......@@ -139,7 +193,7 @@ void SymbolViewer::highlightPart(DC& dc, const SymbolShape& shape, HighlightStyl
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), rotation, points);
segment_subdivide(*shape.getPoint((int)i), *shape.getPoint((int)i+1), origin, multiply, points);
}
// draw
if (style == HIGHLIGHT_BORDER) {
......@@ -238,7 +292,7 @@ void SymbolViewer::drawSymbolShape(const SymbolShape& shape, DC* border, DC* int
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), rotation, points);
segment_subdivide(*shape.getPoint((int)i), *shape.getPoint((int)i+1), origin, multiply, points);
}
// draw border
if (border && border_radius > 0) {
......
......@@ -32,14 +32,18 @@ enum HighlightStyle
class SymbolViewer : public SymbolView {
public:
// --------------------------------------------------- : Data
SymbolViewer(const SymbolP& symbol, double border_radius = 0.05);
SymbolViewer(const SymbolP& symbol, double size = 500, double border_radius = 0.05);
// drawing
double border_radius;
// --------------------------------------------------- : Point translation
void setZoom(double zoom);
Rotation rotation; ///< Object that handles rotation, scaling and translation
Matrix2D multiply; ///< Scaling/rotation of actual parts
Vector2D origin; ///< Origin of parts
// --------------------------------------------------- : Drawing
......@@ -57,7 +61,7 @@ class SymbolViewer : public SymbolView {
typedef shared_ptr<wxMemoryDC> MemoryDCP;
/// Combine a symbol part with the dc
void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, MemoryDCP& borderDC, MemoryDCP& interiorDC);
void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paintedSomething, bool& buffersFilled, bool allow_overlap, MemoryDCP& borderDC, MemoryDCP& interiorDC);
/// Combines a symbol part with what is currently drawn, the border and interior are drawn separatly
/** directB/directI are true if the border/interior is the screen dc, false if it
......
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