diff --git a/mono/Assistance/Assistance.csproj b/mono/Assistance/Assistance.csproj
index b98c551..24e324e 100644
--- a/mono/Assistance/Assistance.csproj
+++ b/mono/Assistance/Assistance.csproj
@@ -60,6 +60,7 @@
+
diff --git a/mono/Assistance/InputManager.cs b/mono/Assistance/InputManager.cs
index 1049d36..11cfbdc 100644
--- a/mono/Assistance/InputManager.cs
+++ b/mono/Assistance/InputManager.cs
@@ -270,7 +270,7 @@ namespace Assistance {
// calc length
double length = track.points.Count > 0
- ? (point.position - track.getLast().point.position).lenSqr() + track.getLast().length
+ ? (point.position - track.getLast().point.position).len() + track.getLast().length
: 0.0;
// add
diff --git a/mono/Assistance/MainWindow.cs b/mono/Assistance/MainWindow.cs
index 7e38d8a..6e66fd5 100644
--- a/mono/Assistance/MainWindow.cs
+++ b/mono/Assistance/MainWindow.cs
@@ -118,6 +118,11 @@ namespace Assistance {
new ModifierSnowflake(workarea.document, cursor);
endDragAndTrack();
break;
+ case Gdk.Key.W:
+ case Gdk.Key.w:
+ new ModifierSpiro(workarea.document, cursor);
+ endDragAndTrack();
+ break;
case Gdk.Key.I:
case Gdk.Key.i:
Gtk.InputDialog dialog = new Gtk.InputDialog();
diff --git a/mono/Assistance/ModifierSpiro.cs b/mono/Assistance/ModifierSpiro.cs
new file mode 100644
index 0000000..aaec50d
--- /dev/null
+++ b/mono/Assistance/ModifierSpiro.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+
+namespace Assistance {
+ public class ModifierSpiro: Modifier {
+ public static readonly double segmentSize = 0.25*Math.PI;
+
+ public class Handler: Track.Handler {
+ public readonly List angles = new List();
+ public Handler(ModifierSpiro owner, Track original):
+ base(owner, original) { }
+ }
+
+ public class Modifier: Track.Modifier {
+ public double angle;
+ public double radius;
+
+ public Modifier(Track.Handler handler, double angle, double radius):
+ base(handler) { this.angle = angle; this.radius = radius; }
+ public override Track.WayPoint calcWayPoint(double originalIndex) {
+ Track.WayPoint p = original.calcWayPoint(originalIndex);
+
+ Handler handler = (Handler)this.handler;
+ double frac;
+ int i = original.floorIndex(originalIndex, out frac);
+ double angle = i < 0 ? 0
+ : frac <= Geometry.precision ? handler.angles[i]
+ : handler.angles[i]*(1.0 - frac) + handler.angles[i + 1]*frac;
+ angle += this.angle;
+
+ double radius = 2.0*this.radius*p.point.pressure;
+ double s = Math.Sin(angle);
+ double c = Math.Cos(angle);
+
+ p.point.position += new Point(c, s)*radius;
+ p.tangent.position = new Point(-s, c);
+ return p;
+ }
+ }
+
+
+ public ActivePoint center;
+ public int count;
+ public double radius;
+
+ public ModifierSpiro(Document document, Point center, int count = 3, double radius = 10.0): base(document) {
+ this.center = new ActivePoint(this, ActivePoint.Type.CircleCross, center);
+ this.count = count;
+ this.radius = radius;
+ }
+
+ public override void modify(Track track, InputManager.KeyPoint keyPoint, List