diff --git a/Editor/Contexts/HitTestBezierContext.cs b/Editor/Contexts/HitTestBezierContext.cs index 368f85f..98664c8 100644 --- a/Editor/Contexts/HitTestBezierContext.cs +++ b/Editor/Contexts/HitTestBezierContext.cs @@ -252,9 +252,9 @@ namespace SpiroNet.Editor } } - public void MarkKnot(int knotIndex) + public void MarkKnot(int index, double theta, double x, double y, SpiroPointType type) { - _state.knot_idx = knotIndex; + _state.knot_idx = index; } } } diff --git a/Editor/Contexts/PathBezierContext.cs b/Editor/Contexts/PathBezierContext.cs index ce94959..814408b 100644 --- a/Editor/Contexts/PathBezierContext.cs +++ b/Editor/Contexts/PathBezierContext.cs @@ -19,6 +19,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA */ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Text; @@ -34,6 +36,7 @@ namespace SpiroNet.Editor { private bool _needToClose = false; private StringBuilder _sb = new StringBuilder(); + private IList _knots = new List(); /// /// Format double value using en-GB culture info. @@ -59,6 +62,15 @@ namespace SpiroNet.Editor return _sb.ToString(); } + /// + /// Get output spiro control point knots for current Path segment. + /// + /// The spiro control point knots for current Path segment. + public IList GetKnots() + { + return _knots; + } + /// /// Get output Path data string format. /// @@ -126,11 +138,24 @@ namespace SpiroNet.Editor } /// - /// Mark current control point knot. Currenlty not implemented, may be usefull for marking generated curves to original spiro code points. + /// Mark current control point knot. + /// Currenlty not implemented, may be usefull for marking generated curves to original spiro code points. /// - /// The current spiros control point knot index. - public void MarkKnot(int knotIndex) + /// The spiros control point knot index. + /// The spiros control point knot theta angle. + /// The spiros control point X location. + /// The spiros control point Y location. + /// The spiros control point type. + public void MarkKnot(int index, double theta, double x, double y, SpiroPointType type) { + _knots.Add(new SpiroKnot() + { + Index = index, + Theta = theta * 180 / Math.PI, + X = x, + Y = y, + Type = type + }); } } } diff --git a/Editor/Contexts/PsBezierContext.cs b/Editor/Contexts/PsBezierContext.cs index 55d41cb..f4f6933 100644 --- a/Editor/Contexts/PsBezierContext.cs +++ b/Editor/Contexts/PsBezierContext.cs @@ -117,7 +117,7 @@ namespace SpiroNet.Editor _state.y = y3; } - public void MarkKnot(int knotIndex) + public void MarkKnot(int index, double theta, double x, double y, SpiroPointType type) { } } diff --git a/Editor/Editor/SpiroEditor.cs b/Editor/Editor/SpiroEditor.cs index d052282..a3b7264 100644 --- a/Editor/Editor/SpiroEditor.cs +++ b/Editor/Editor/SpiroEditor.cs @@ -34,6 +34,7 @@ namespace SpiroNet.Editor private Action _invalidate = null; private PathDrawing _drawing = null; private IDictionary _data = null; + private IDictionary> _knots = null; public SpirtoEditorState State { @@ -65,6 +66,12 @@ namespace SpiroNet.Editor set { Update(ref _data, value); } } + public IDictionary> Knots + { + get { return _knots; } + set { Update(ref _knots, value); } + } + public void ToggleIsStroked() { _state.IsStroked = !_state.IsStroked; @@ -189,9 +196,15 @@ namespace SpiroNet.Editor var bc = new PathBezierContext(); var result = TryGetData(shape, bc); if (_data.ContainsKey(shape)) + { _data[shape] = result ? bc.ToString() : null; + _knots[shape] = result ? bc.GetKnots() : null; + } else + { _data.Add(shape, result ? bc.ToString() : null); + _knots.Add(shape, result ? bc.GetKnots() : null); + } } private static double DistanceSquared(double x0, double y0, double x1, double y1) @@ -367,6 +380,7 @@ namespace SpiroNet.Editor // Delete shape. _drawing.Shapes.Remove(hitShape); _data.Remove(hitShape); + _knots.Remove(hitShape); _invalidate(); } else @@ -384,6 +398,7 @@ namespace SpiroNet.Editor // Delete shape. _drawing.Shapes.Remove(hitShape); _data.Remove(hitShape); + _knots.Remove(hitShape); _invalidate(); return; } @@ -471,6 +486,7 @@ namespace SpiroNet.Editor { Drawing = drawing; Data = new Dictionary(); + Knots = new Dictionary>(); foreach (var shape in _drawing.Shapes) { diff --git a/Editor/Editor/SpiroKnot.cs b/Editor/Editor/SpiroKnot.cs new file mode 100644 index 0000000..03e6f20 --- /dev/null +++ b/Editor/Editor/SpiroKnot.cs @@ -0,0 +1,55 @@ +/* +SpiroNet.Editor +Copyright (C) 2015 Wiesław Šoltés + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 3 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +*/ +using System; + +namespace SpiroNet.Editor +{ + /// + /// Spiro control point knot. + /// + public struct SpiroKnot + { + /// + /// The spiros control point knot index. + /// + public int Index; + + /// + /// The spiros control point knot theta angle. + /// + public double Theta; + + /// + /// The spiros control point X location. + /// + public double X; + + /// + /// The spiros control point Y location. + /// + public double Y; + + /// + /// The spiros control point type. + /// + public SpiroPointType Type; + } +} diff --git a/Editor/SpiroNet.Editor.csproj b/Editor/SpiroNet.Editor.csproj index 2e8195e..a447204 100644 --- a/Editor/SpiroNet.Editor.csproj +++ b/Editor/SpiroNet.Editor.csproj @@ -47,6 +47,7 @@ + diff --git a/Spiro/IBezierContext.cs b/Spiro/IBezierContext.cs index 2b7072f..2617bc5 100644 --- a/Spiro/IBezierContext.cs +++ b/Spiro/IBezierContext.cs @@ -73,7 +73,11 @@ namespace SpiroNet /// /// Called by spiro to mark current control point knot. /// - /// The current spiros control point knot index. - void MarkKnot(int knotIndex); + /// The spiros control point knot index. + /// The spiros control point knot theta angle. + /// The spiros control point X location. + /// The spiros control point Y location. + /// The spiros control point type. + void MarkKnot(int index, double theta, double x, double y, SpiroPointType type); } } diff --git a/Spiro/SpiroImpl.cs b/Spiro/SpiroImpl.cs index b809fa4..6fa60ba 100644 --- a/Spiro/SpiroImpl.cs +++ b/Spiro/SpiroImpl.cs @@ -769,9 +769,12 @@ namespace SpiroNet if (i == 0) bc.MoveTo(x0, y0, s[0].Type == SpiroPointType.OpenContour ? true : false); - bc.MarkKnot(i); + bc.MarkKnot(i, get_knot_th(s, i), s[i].X, s[i].Y, s[i].Type); spiro_seg_to_bpath(s[i].ks, x0, y0, x1, y1, bc, 0); } + + if (nsegs == n - 1) + bc.MarkKnot(n - 1, get_knot_th(s, n - 1), s[n - 1].X, s[n - 1].Y, s[n - 1].Type); } public static double get_knot_th(SpiroSegment[] s, int i) diff --git a/Wpf/Controls/SpiroCanvas.cs b/Wpf/Controls/SpiroCanvas.cs index 1919ac3..dc025ac 100644 --- a/Wpf/Controls/SpiroCanvas.cs +++ b/Wpf/Controls/SpiroCanvas.cs @@ -32,6 +32,12 @@ namespace SpiroNet.Wpf { public class SpiroCanvas : Canvas { + private static Geometry LeftKnot = Geometry.Parse("M0,-4 A 4,4 0 0 0 0,4"); + private static Geometry RightKnot = Geometry.Parse("M0,-4 A 4,4 0 0 1 0,4"); + private static Geometry EndKnot = Geometry.Parse("M-3.5,-3.5 L3.5,3.5 M-3.5,3.5 L3.5,-3.5"); + private static Geometry EndOpenContourKnot = Geometry.Parse("M-3.5,-3.5 L0,0 -3.5,3.5"); + private static Geometry OpenContourKnot = Geometry.Parse("M3.5,-3.5 L0,0 3.5,3.5"); + private Brush _geometryBrush; private Brush _geometryPenBrush; private Pen _geometryPen; @@ -39,7 +45,9 @@ namespace SpiroNet.Wpf private Brush _hitGeometryPenBrush; private Pen _hitGeometryPen; private Brush _pointBrush; + private Pen _pointPen; private Brush _hitPointBrush; + private Pen _hitPointPen; public SpiroEditor Editor { @@ -77,9 +85,13 @@ namespace SpiroNet.Wpf _pointBrush = new SolidColorBrush(Color.FromArgb(192, 0, 0, 255)); _pointBrush.Freeze(); + _pointPen = new Pen(_pointBrush, 2.0); + _pointPen.Freeze(); _hitPointBrush = new SolidColorBrush(Color.FromArgb(192, 255, 0, 0)); _hitPointBrush.Freeze(); + _hitPointPen = new Pen(_hitPointBrush, 2.0); + _hitPointPen.Freeze(); } protected override void OnRender(DrawingContext dc) @@ -102,14 +114,15 @@ namespace SpiroNet.Wpf private void DrawShape(DrawingContext dc, PathShape shape) { - if (shape == null || Editor == null || Editor.Data == null) + if (shape == null || Editor == null) return; var hitShape = Editor.State.HitShape; var hitShapePointIndex = Editor.State.HitShapePointIndex; + // draw shapes string data; - if (Editor.Data.TryGetValue(shape, out data) && !string.IsNullOrEmpty(data)) + if (Editor.Data != null && Editor.Data.TryGetValue(shape, out data) && !string.IsNullOrEmpty(data)) { var geometry = Geometry.Parse(data); if (shape == hitShape && hitShapePointIndex == -1) @@ -128,12 +141,76 @@ namespace SpiroNet.Wpf } } - if (shape.Points != null) + // draw shape knots + IList knots; + Editor.Knots.TryGetValue(shape, out knots); + if (knots != null) + { + for (int i = 0; i < knots.Count; i++) + { + var knot = knots[i]; + var brush = shape == hitShape && i == hitShapePointIndex ? _hitPointBrush : _pointBrush; + var pen = shape == hitShape && i == hitShapePointIndex ? _hitPointPen : _pointPen; + + switch (knot.Type) + { + case SpiroPointType.Corner: + dc.DrawRectangle(brush, null, new Rect(knot.X - 3.5, knot.Y - 3.5, 7, 7)); + break; + case SpiroPointType.G4: + dc.DrawEllipse(brush, null, new Point(knot.X, knot.Y), 3.5, 3.5); + break; + case SpiroPointType.G2: + dc.PushTransform(new RotateTransform(knot.Theta, knot.X, knot.Y)); + dc.DrawRectangle(brush, null, new Rect(knot.X - 1.5, knot.Y - 3.5, 3, 7)); + dc.Pop(); + break; + case SpiroPointType.Left: + dc.PushTransform(new RotateTransform(knot.Theta, knot.X, knot.Y)); + dc.PushTransform(new TranslateTransform(knot.X, knot.Y)); + dc.DrawGeometry(brush, null, LeftKnot); + dc.Pop(); + dc.Pop(); + break; + case SpiroPointType.Right: + dc.PushTransform(new RotateTransform(knot.Theta, knot.X, knot.Y)); + dc.PushTransform(new TranslateTransform(knot.X, knot.Y)); + dc.DrawGeometry(brush, null, RightKnot); + dc.Pop(); + dc.Pop(); + break; + case SpiroPointType.End: + dc.PushTransform(new RotateTransform(knot.Theta, knot.X, knot.Y)); + dc.PushTransform(new TranslateTransform(knot.X, knot.Y)); + dc.DrawGeometry(null, pen, EndKnot); + dc.Pop(); + dc.Pop(); + break; + case SpiroPointType.OpenContour: + dc.PushTransform(new RotateTransform(knot.Theta, knot.X, knot.Y)); + dc.PushTransform(new TranslateTransform(knot.X, knot.Y)); + dc.DrawGeometry(null, pen, OpenContourKnot); + dc.Pop(); + dc.Pop(); + break; + case SpiroPointType.EndOpenContour: + dc.PushTransform(new RotateTransform(knot.Theta, knot.X, knot.Y)); + dc.PushTransform(new TranslateTransform(knot.X, knot.Y)); + dc.DrawGeometry(null, pen, EndOpenContourKnot); + dc.Pop(); + dc.Pop(); + break; + } + } + } + // draw shape points if knots do not exist + else { for (int i = 0; i < shape.Points.Count; i++) { var point = shape.Points[i]; var brush = shape == hitShape && i == hitShapePointIndex ? _hitPointBrush : _pointBrush; + var pen = shape == hitShape && i == hitShapePointIndex ? _hitPointPen : _pointPen; switch (point.Type) { @@ -141,25 +218,15 @@ namespace SpiroNet.Wpf dc.DrawRectangle(brush, null, new Rect(point.X - 3.5, point.Y - 3.5, 7, 7)); break; case SpiroPointType.G4: - dc.DrawEllipse(brush, null, new Point(point.X, point.Y), 3.5, 3.5); - break; case SpiroPointType.G2: - dc.DrawEllipse(brush, null, new Point(point.X, point.Y), 3.5, 3.5); - break; case SpiroPointType.Left: - dc.DrawRectangle(brush, null, new Rect(point.X - 3.5, point.Y - 3.5, 7, 7)); - break; case SpiroPointType.Right: - dc.DrawRectangle(brush, null, new Rect(point.X - 3.5, point.Y - 3.5, 7, 7)); - break; case SpiroPointType.End: - dc.DrawRectangle(brush, null, new Rect(point.X - 3.5, point.Y - 3.5, 7, 7)); - break; case SpiroPointType.OpenContour: - dc.DrawRectangle(brush, null, new Rect(point.X - 3.5, point.Y - 3.5, 7, 7)); - break; case SpiroPointType.EndOpenContour: - dc.DrawRectangle(brush, null, new Rect(point.X - 3.5, point.Y - 3.5, 7, 7)); + dc.PushTransform(new TranslateTransform(point.X, point.Y)); + dc.DrawGeometry(null, pen, EndKnot); + dc.Pop(); break; } } diff --git a/Wpf/Controls/SpiroControl.xaml.cs b/Wpf/Controls/SpiroControl.xaml.cs index e7cb134..2bb31cf 100644 --- a/Wpf/Controls/SpiroControl.xaml.cs +++ b/Wpf/Controls/SpiroControl.xaml.cs @@ -62,7 +62,8 @@ namespace SpiroNet.Wpf Height = 600, Shapes = new ObservableCollection() }, - Data = new Dictionary() + Data = new Dictionary(), + Knots = new Dictionary>() }; _editor.Commands.NewCommand = Command.Create(_editor.New);