diff --git a/mono/Assistance/DynamicSurface.cs b/mono/Assistance/DynamicSurface.cs new file mode 100644 index 0000000..9655b9d --- /dev/null +++ b/mono/Assistance/DynamicSurface.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; + +namespace Assistance { + public class DynamicSurface: IDisposable { + private readonly double incrementScale; + + private int offsetX; + private int offsetY; + private Cairo.ImageSurface surface; + + public DynamicSurface(double incrementScale = 1.2) + { this.incrementScale = incrementScale; } + + public bool isEmpty + { get { return surface == null; } } + + public int left + { get { return isEmpty ? 0 : offsetX; } } + public int top + { get { return isEmpty ? 0 : offsetY; } } + public int width + { get { return isEmpty ? 0 : surface.Width; } } + public int height + { get { return isEmpty ? 0 : surface.Height; } } + + public Rectangle getBounds() { + return isEmpty ? new Rectangle() + : new Rectangle((double)offsetX, (double)offsetY, (double)(offsetX + surface.Width), (double)(offsetY + surface.Height)); + } + + public Cairo.Context getContext() { + if (isEmpty) return null; + Cairo.Context context = new Cairo.Context(surface); + context.Antialias = Cairo.Antialias.Gray; + context.Translate(-offsetX, -offsetY); + return context; + } + + public void draw(Cairo.Context context, double alpha = 1.0) { + if (isEmpty) return; + context.Save(); + context.Translate(offsetX, offsetY); + context.SetSource(surface); + if (alpha >= 1.0 - Geometry.precision) + context.Paint(); else context.PaintWithAlpha(alpha); + context.Restore(); + } + + public void clear() { + if (isEmpty) return; + surface.Dispose(); + surface = null; + } + + private bool doExpand(Rectangle rect, bool noScale) { + int rl = (int)Math.Floor(rect.x0 + Geometry.precision); + int rt = (int)Math.Floor(rect.y0 + Geometry.precision); + int rr = Math.Max(rl, (int)Math.Ceiling(rect.x1 - Geometry.precision)); + int rb = Math.Max(rt, (int)Math.Ceiling(rect.y1 - Geometry.precision)); + + int l, t, r, b; + if (surface == null) { + l = rl; t = rt; r = rr; b = rb; + } else { + l = offsetX; + t = offsetY; + r = l + surface.Width; + b = t + surface.Height; + } + + int incX = noScale ? 0 : Math.Max(0, (int)Math.Ceiling( (incrementScale - 1.0)*(Math.Max(r, rr) - Math.Min(l, rl)) )); + int incY = noScale ? 0 : Math.Max(0, (int)Math.Ceiling( (incrementScale - 1.0)*(Math.Max(b, rb) - Math.Min(t, rt)) )); + + if (rl < l) l = rl - incX; + if (rt < t) t = rt - incY; + if (rr > r) r = rr + incX; + if (rb > b) b = rb + incY; + + int w = r - l; + int h = b - t; + if (surface != null && l == offsetX && t == offsetY && w == surface.Width && h == surface.Height) + return false; + + Cairo.ImageSurface newSurface = new Cairo.ImageSurface(Cairo.Format.ARGB32, w, h); + Cairo.Context context = new Cairo.Context(newSurface); + if (surface != null) { + context.Translate(offsetX - l, offsetY - t); + context.SetSource(surface); + context.Paint(); + context.GetTarget().Flush(); + context.Dispose(); + } + + offsetX = l; + offsetY = t; + surface = newSurface; + + return true; + } + + public bool expand(Rectangle rect, bool noScale = false) { + Cairo.Surface surface = this.surface; + if (doExpand(rect, noScale)) { + if (surface != null) surface.Dispose(); + return true; + } + return false; + } + + public bool expandContext(ref Cairo.Context context, Rectangle rect, bool noScale = false) { + Cairo.Surface surface = this.surface; + if (context != null) context.GetTarget().Flush(); + if (doExpand(rect, noScale)) { + Cairo.Context oldContext = context; + context = getContext(); + if (oldContext != null) oldContext.Dispose(); + surface.Dispose(); + return true; + } + return false; + } + + public void Dispose() + { Dispose(true); GC.SuppressFinalize(this); } + protected virtual void Dispose(bool disposing) + { clear(); } + ~DynamicSurface() + { Dispose(false); } + } +} + diff --git a/mono/Assistance/InputManager.cs b/mono/Assistance/InputManager.cs index 2023aff..dff039c 100644 --- a/mono/Assistance/InputManager.cs +++ b/mono/Assistance/InputManager.cs @@ -55,6 +55,7 @@ namespace Assistance { public interface IModifier: Track.IOwner { void activate(); void modify(List tracks, KeyPoint keyPoint, List outTracks); + void draw(Cairo.Context context, List tracks); void deactivate(); } @@ -63,6 +64,9 @@ namespace Assistance { public virtual void modify(Track track, KeyPoint keyPoint, List outTracks) { } public virtual void modify(List tracks, KeyPoint keyPoint, List outTracks) { foreach(Track track in tracks) modify(track, keyPoint, outTracks); } + public virtual void draw(Cairo.Context context, Track tracks) { } + public virtual void draw(Cairo.Context context, List tracks) + { foreach(Track track in tracks) draw(context, track); } public virtual void deactivate() { } } @@ -70,22 +74,20 @@ namespace Assistance { public readonly Workarea workarea; private readonly InputState state = new InputState(); - + + private bool wantActive; + private bool actualActive; private Tool tool; private readonly List modifiers = new List(); - private readonly List tracks = new List(); + private readonly List> tracks = new List>() { new List() }; private readonly List keyPoints = new List(); private int keyPointsSent; - private List subTracks = null; - private readonly List[] subTracksBuf = new List[] { new List(), new List() }; - - InputManager(Workarea workarea) + public InputManager(Workarea workarea) { this.workarea = workarea; } - private void paintRollbackTo(int keyIndex, List subTracks) { if (keyIndex >= keyPoints.Count) return; @@ -146,21 +148,18 @@ namespace Assistance { private void paintTracks() { bool allFinished = true; - foreach(Track track in tracks) + foreach(Track track in tracks[0]) if (!track.isFinished()) { allFinished = false; break; } while(true) { // run modifiers KeyPoint newKeyPoint = new KeyPoint(); - subTracks = tracks; - int i = 0; - foreach(IModifier modifier in modifiers) { - List outTracks = subTracksBuf[i]; - modifier.modify(subTracks, newKeyPoint, outTracks); - subTracks = outTracks; - i = 1 - i; + for(int i = 0; i < modifiers.Count; ++i) { + tracks[i+1].Clear(); + modifiers[i].modify(tracks[i], newKeyPoint, tracks[i+1]); } + List subTracks = tracks[modifiers.Count]; // create handlers foreach(Track track in subTracks) @@ -195,8 +194,8 @@ namespace Assistance { if (newKeyPoint.isFree) { if (allFinished) { paintApply(keyPoints.Count, subTracks); - tracks.Clear(); - this.subTracks.Clear(); + foreach(List ts in tracks) + ts.Clear(); } break; } @@ -223,7 +222,7 @@ namespace Assistance { touchId, state.keyHistoryHolder(ticks), state.buttonHistoryHolder(device, ticks) ); - tracks.Insert(index, track); + tracks[0].Insert(index, track); return track; } @@ -231,23 +230,23 @@ namespace Assistance { int cmp; int a = 0; - cmp = trackCompare(tracks[a], device, touchId); - if (cmp == 0) return tracks[a]; + cmp = trackCompare(tracks[0][a], device, touchId); + if (cmp == 0) return tracks[0][a]; if (cmp < 0) return createTrack(a, device, touchId, ticks); int b = tracks.Count - 1; - cmp = trackCompare(tracks[b], device, touchId); - if (cmp == 0) return tracks[b]; + cmp = trackCompare(tracks[0][b], device, touchId); + if (cmp == 0) return tracks[0][b]; if (cmp > 0) return createTrack(b+1, device, touchId, ticks); // binary search: tracks[a] < tracks[c] < tracks[b] while(true) { int c = (a + b)/2; if (a == c) break; - cmp = trackCompare(tracks[c], device, touchId); + cmp = trackCompare(tracks[0][c], device, touchId); if (cmp < 0) b = c; else if (cmp > 0) a = c; else - return tracks[c]; + return tracks[0][c]; } return createTrack(b, device, touchId, ticks); } @@ -275,18 +274,46 @@ namespace Assistance { } private void touchTracks(bool finish = false) { - foreach(Track track in tracks) + foreach(Track track in tracks[0]) if (!track.isFinished() && track.points.Count > 0) addTrackPoint(track, track.getLast().point, track.getLast().time, finish); paintTracks(); } - private void finishTracks() - { touchTracks(true); } + private void actualActivate() { + bool wasActive = actualActive; + actualActive = wantActive && tool != null; + if (wasActive == actualActive) return; + + if (actualActive) { + foreach(IModifier modifier in modifiers) + modifier.activate(); + tool.activate(); + } else { + touchTracks(true); + tool.deactivate(); + foreach(IModifier modifier in modifiers) + modifier.deactivate(); + } + } + + public bool isActive() + { return actualActive; } + public void activate() + { wantActive = true; actualActivate(); } + public void deactivate() + { wantActive = false; actualActivate(); } + public void finishTracks() + { if (isActive()) touchTracks(true); } + + public List getInputTracks() + { return tracks[0]; } + public List getOutputTracks() + { return tracks[modifiers.Count]; } - public void trackEvent(long touchId, Gdk.Device device, Track.Point point, bool final, long ticks) { - if (tool != null) { + public void trackEvent(Gdk.Device device, long touchId, Track.Point point, bool final, long ticks) { + if (isActive()) { Track track = getTrack(device, touchId, ticks); if (!track.isFinished()) { double time = (double)(ticks - track.keyHistory.ticks)*Timer.step - track.keyHistory.timeOffset; @@ -298,7 +325,7 @@ namespace Assistance { public void keyEvent(bool press, Gdk.Key key, long ticks) { state.keyEvent(press, key, ticks); - if (tool != null) { + if (isActive()) { tool.keyEvent(press, key, state); touchTracks(); } @@ -306,27 +333,30 @@ namespace Assistance { public void buttonEvent(bool press, Gdk.Device device, uint button, long ticks) { state.buttonEvent(press, device, button, ticks); - if (tool != null) { + if (isActive()) { tool.buttonEvent(press, device, button, state); touchTracks(); } } - public Tool getTool() { return tool; } public void setTool(Tool tool) { if (this.tool == tool) { - if (this.tool != null) { + if (actualActive) { finishTracks(); this.tool.deactivate(); } this.tool = tool; - if (this.tool != null) - this.tool.activate(); + if (actualActive) { + if (this.tool != null) + this.tool.activate(); + else + actualActivate(); + } } } @@ -336,17 +366,23 @@ namespace Assistance { { return modifiers[index]; } public void insertModifier(int index, IModifier modifier) { - if (this.tool != null) finishTracks(); + if (actualActive) + finishTracks(); modifiers.Insert(index, modifier); - modifier.activate(); + tracks.Insert(index+1, new List()); + if (actualActive) + modifier.activate(); } public void addModifier(IModifier modifier) { insertModifier(getModifiersCount(), modifier); } public void removeModifier(int index) { - if (this.tool != null) finishTracks(); - modifiers[index].deactivate(); + if (actualActive) { + finishTracks(); + modifiers[index].deactivate(); + } modifiers.RemoveAt(index); + tracks.RemoveAt(index+1); } public void removeModifier(IModifier modifier) { for(int i = 0; i < getModifiersCount(); ++i) @@ -355,16 +391,16 @@ namespace Assistance { } public void clearModifiers() { while(getModifiersCount() > 0) - removeModifier(0); + removeModifier(getModifiersCount() - 1); } public void draw(Cairo.Context context) { // paint not sent sub-tracks - if (subTracks != null && keyPointsSent < keyPoints.Count) { + if (keyPointsSent < keyPoints.Count) { context.Save(); penPreview.apply(context); - foreach(Track track in subTracks) { + foreach(Track track in getOutputTracks()) { TrackHandler handler = (TrackHandler)track.handler; int start = handler.keys[keyPointsSent]; if (start < track.points.Count) { @@ -388,6 +424,10 @@ namespace Assistance { context.Stroke(); context.Restore(); } + + // paint modifiers + for(int i = 0; i < modifiers.Count; ++i) + modifiers[i].draw(context, tracks[i]); } } } diff --git a/mono/Assistance/InputModifierAssistants.cs b/mono/Assistance/InputModifierAssistants.cs index 5a9adf1..43a7841 100644 --- a/mono/Assistance/InputModifierAssistants.cs +++ b/mono/Assistance/InputModifierAssistants.cs @@ -5,11 +5,11 @@ using System.Collections.Generic; namespace Assistance { public class InputModifierAssistants: InputModifierTangents { public readonly Workarea workarea; - public readonly bool interpolate; + public readonly bool defaultTangents; - public InputModifierAssistants(Workarea workarea, bool interpolate = false) { + public InputModifierAssistants(Workarea workarea, bool defaultTangents = false) { this.workarea = workarea; - this.interpolate = interpolate; + this.defaultTangents = defaultTangents; } public new class Modifier: Track.Modifier { @@ -18,6 +18,7 @@ namespace Assistance { public InputManager.KeyPoint.Holder holder = null; public List guidelines = new List(); + public List hintGuidelines = new List(); public override Track.WayPoint calcWayPoint(double originalIndex) { Track.WayPoint p = original.calcWayPoint(originalIndex); @@ -36,7 +37,7 @@ namespace Assistance { Track.Handler handler = new Track.Handler(this, track); modifier = new Modifier(track.handler); workarea.getGuidelines(modifier.guidelines, track.points[0].point.position); - if (interpolate && modifier.guidelines.Count == 0) + if (defaultTangents && modifier.guidelines.Count == 0) { base.modify(track, keyPoint, outTracks); return; } track.handler = handler; @@ -51,7 +52,7 @@ namespace Assistance { } subTrack = track.handler.tracks[0]; - if (subTrack.modifier is InputModifierTangents.Modifier) + if (!(subTrack.modifier is Modifier)) { base.modify(track, keyPoint, outTracks); return; } modifier = (Modifier)subTrack.modifier; @@ -91,6 +92,20 @@ namespace Assistance { subTrack.points.Add(modifier.calcWayPoint(i)); subTrack.wayPointsAdded = subTrack.points.Count - start; } + + public override void draw(Cairo.Context context, Track track) { + if (track.handler == null) return; + Track subTrack = track.handler.tracks[0]; + if (!(subTrack.modifier is Modifier)) + { base.draw(context, track); return; } + Modifier modifier = (Modifier)subTrack.modifier; + if (modifier.guidelines.Count > 0 && subTrack.points.Count > 0) { + workarea.getGuidelines(modifier.hintGuidelines, subTrack.points[0].point.position); + foreach(Guideline gl in modifier.hintGuidelines) + gl.draw(context); + modifier.hintGuidelines.Clear(); + } + } } } diff --git a/mono/Assistance/InputModifierInterpolation.cs b/mono/Assistance/InputModifierInterpolation.cs index 51c63d7..bcd4b60 100644 --- a/mono/Assistance/InputModifierInterpolation.cs +++ b/mono/Assistance/InputModifierInterpolation.cs @@ -26,7 +26,6 @@ namespace Assistance { } Track subTrack = track.handler.tracks[0]; - Track.Modifier modifier = subTrack.modifier; outTracks.Add(subTrack); if (!track.isChanged) diff --git a/mono/Assistance/MainWindow.cs b/mono/Assistance/MainWindow.cs index 8f4a0cd..8a3d7ea 100644 --- a/mono/Assistance/MainWindow.cs +++ b/mono/Assistance/MainWindow.cs @@ -14,19 +14,19 @@ namespace Assistance { Cairo.ImageSurface surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 1, 1); Workarea workarea = new Workarea(); + ToolFull toolFull; + bool dragging = false; + bool painting = false; ActivePoint activePoint; - uint timeStart = 0; - long ticksStart = 0; - long lastTicks = 0; Point offset; Point cursor; - InputState inputState = new InputState(); + long touchId; + long ticksStart = 0; + double timeStart = 0; - Track track = null; - public MainWindow(): base(Gtk.WindowType.Toplevel) { foreach(Gdk.Device device in Display.ListDevices()) { if (device.Name.Contains("tylus")) { @@ -42,6 +42,9 @@ namespace Assistance { | Gdk.EventMask.ButtonMotionMask | Gdk.EventMask.PointerMotionMask; ExtensionEvents = Gdk.ExtensionMode.All; + + toolFull = new ToolFull(workarea); + workarea.setTool(toolFull); } protected override bool OnDeleteEvent(Gdk.Event e) { @@ -95,19 +98,20 @@ namespace Assistance { activePoint.bringToFront(); } - private void beginTrack(Gdk.Device device) { + private void beginTrack(double timeStart) { endDragAndTrack(); - track = new Track(Track.getTouchId(), device); + ++touchId; ticksStart = Timer.ticks(); + this.timeStart = timeStart; } private void endDragAndTrack() { dragging = false; offset = new Point(); - - if (track != null) - workarea.paintTrack(track); - track = null; + if (painting) { + painting = false; + workarea.inputManager.finishTracks(); + } } protected override bool OnKeyPressEvent(Gdk.EventKey e) { @@ -116,10 +120,6 @@ namespace Assistance { new AssistantVanishingPoint(workarea.document, cursor); endDragAndTrack(); break; - case Gdk.Key.Key_2: - new AssistantGrid(workarea.document, cursor); - endDragAndTrack(); - break; case Gdk.Key.Q: case Gdk.Key.q: new ModifierSnowflake(workarea.document, cursor); @@ -137,8 +137,7 @@ namespace Assistance { endDragAndTrack(); break; default: - inputState.keyPress(e.Key, Timer.ticks()); - retryTrackPoint(); + workarea.inputManager.keyEvent(true, e.Key, Timer.ticks()); break; } QueueDraw(); @@ -146,46 +145,29 @@ namespace Assistance { } protected override bool OnKeyReleaseEvent(Gdk.EventKey e) { - inputState.keyRelease(e.Key, Timer.ticks()); - retryTrackPoint(); + workarea.inputManager.keyEvent(false, e.Key, Timer.ticks()); return base.OnKeyReleaseEvent(e); } - void addTrackPoint(Point p, double time, double pressure, Point tilt) { - if (track == null) + void addTrackPoint(Gdk.Device device, Point p, double time, double pressure, Point tilt, bool final) { + if (!painting) return; - TrackPoint point = new TrackPoint(); - point.point = p; - point.time = time; + Track.Point point = new Track.Point(); + point.position = p; point.pressure = pressure; - point.tilt = tilt; + point.tilt = tilt; - long ticks = Timer.ticks(); - if (track.points.Count > 0) { - double t = track.points[ track.points.Count-1 ].time; - if (point.time - t < Geometry.precision) - point.time = t + (ticks - lastTicks)*Timer.step; - } - lastTicks = ticks; - - point.keyState.state = inputState.keyState; - point.keyState.ticks = ticksStart; - point.keyState.timeOffset = point.time; + long ticks = ticksStart + (long)Math.Round((time - timeStart)*Timer.frequency); - point.buttonState.state = inputState.buttonState(track.device); - point.buttonState.ticks = ticksStart; - point.buttonState.timeOffset = point.time; - - track.points.Add(point); + workarea.inputManager.trackEvent(device, touchId, point, final, ticks); } - void addTrackPoint(double x, double y, uint t, Gdk.Device device, double[] axes) { + void addTrackPoint(double x, double y, uint t, Gdk.Device device, double[] axes, bool final) { Point point = windowToWorkarea(new Point(x, y)); double time = (double)(t - timeStart)*0.001; double pressure = 0.5; Point tilt = new Point(0.0, 0.0); - if (device != null && axes != null) { double v; if (device.GetAxis(axes, Gdk.AxisUse.Pressure, out v)) @@ -195,46 +177,25 @@ namespace Assistance { if (device.GetAxis(axes, Gdk.AxisUse.Ytilt, out v)) tilt.y = v; } - - long ticks = Timer.ticks(); - long dticks = Math.Max(1, ticks - lastTicks); - lastTicks = ticks; - if (track.points.Count > 0) { - double prevTime = track.points[ track.points.Count-1 ].time; - if (time - prevTime < Geometry.precision) - time = prevTime + dticks*Timer.step; - } - - addTrackPoint(point, time, pressure, tilt); + addTrackPoint(device, point, time, pressure, tilt, final); } - void addTrackPoint(Gdk.EventButton e) { - addTrackPoint(e.X, e.Y, e.Time, e.Device, e.Axes); - } - - void addTrackPoint(Gdk.EventMotion e) { - addTrackPoint(e.X, e.Y, e.Time, e.Device, e.Axes); - } - - void retryTrackPoint() { - if (track == null || track.points.Count < 1) return; - TrackPoint last = track.points[track.points.Count-1]; - addTrackPoint(last.point, last.time, last.pressure, last.tilt); - } + void addTrackPoint(Gdk.EventButton e, bool press) + { addTrackPoint(e.X, e.Y, e.Time, e.Device, e.Axes, !press); } + void addTrackPoint(Gdk.EventMotion e) + { addTrackPoint(e.X, e.Y, e.Time, e.Device, e.Axes, false); } protected override bool OnButtonPressEvent(Gdk.EventButton e) { cursor = windowToWorkarea(new Point(e.X, e.Y)); - Console.WriteLine(e.Button.ToString()); - inputState.buttonPress(e.Device, e.Button, Timer.ticks()); - retryTrackPoint(); + workarea.inputManager.buttonEvent(true, e.Device, e.Button, Timer.ticks()); if (e.Button == 1) { timeStart = e.Time; activePoint = workarea.findPoint(cursor); if (activePoint != null) { beginDrag(); } else { - beginTrack(e.Device); - addTrackPoint(e); + beginTrack((double)e.Time*0.001); + addTrackPoint(e, true); } } QueueDraw(); @@ -243,13 +204,12 @@ namespace Assistance { protected override bool OnButtonReleaseEvent(Gdk.EventButton e) { cursor = windowToWorkarea(new Point(e.X, e.Y)); - inputState.buttonRelease(e.Device, e.Button, Timer.ticks()); - retryTrackPoint(); + workarea.inputManager.buttonEvent(false, e.Device, e.Button, Timer.ticks()); if (e.Button == 1) { - if (!dragging && track != null) - addTrackPoint(e); + if (!dragging && painting) + addTrackPoint(e, false); endDragAndTrack(); - if (!dragging && track == null) + if (!dragging && !painting) activePoint = workarea.findPoint(cursor); } QueueDraw(); @@ -261,8 +221,7 @@ namespace Assistance { if (dragging) { activePoint.owner.onMovePoint(activePoint, cursor + offset); } else - if (track != null) { - if (e.IsHint) Gdk.Display.Default.Beep(); + if (painting) { addTrackPoint(e); } else { activePoint = workarea.findPoint(cursor); @@ -273,7 +232,7 @@ namespace Assistance { public void draw(Cairo.Context context) { context.Translate(surface.Width/2, surface.Height/2); - workarea.draw(context, activePoint, cursor + offset, track); + workarea.draw(context, activePoint); } } } diff --git a/mono/Assistance/Modifier.cs b/mono/Assistance/Modifier.cs index c3c72bd..9bc55d0 100644 --- a/mono/Assistance/Modifier.cs +++ b/mono/Assistance/Modifier.cs @@ -24,6 +24,9 @@ namespace Assistance { public virtual void modify(Track track, InputManager.KeyPoint keyPoint, List outTracks) { } public virtual void modify(List tracks, InputManager.KeyPoint keyPoint, List outTracks) { foreach(Track track in tracks) modify(track, keyPoint, outTracks); } + public virtual void draw(Cairo.Context context, Track track) { } + public virtual void draw(Cairo.Context context, List tracks) + { foreach(Track track in tracks) draw(context, track); } public virtual void deactivate() { } public virtual void draw(Cairo.Context context) { } diff --git a/mono/Assistance/ModifierSnowflake.cs b/mono/Assistance/ModifierSnowflake.cs index 881794e..d174366 100644 --- a/mono/Assistance/ModifierSnowflake.cs +++ b/mono/Assistance/ModifierSnowflake.cs @@ -77,7 +77,6 @@ namespace Assistance { } // add points - Track.WayPoint p0 = track.getWayPoint(start - 1); for(int i = start; i < track.points.Count; ++i) subTrack.points.Add( subTrack.calcWayPoint(i) ); subTrack.wayPointsAdded += subTrack.points.Count - start; diff --git a/mono/Assistance/ToolFull.cs b/mono/Assistance/ToolFull.cs new file mode 100644 index 0000000..d9ea6b1 --- /dev/null +++ b/mono/Assistance/ToolFull.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; + +namespace Assistance { + public class ToolFull: Tool { + public static readonly Drawing.Pen pen = new Drawing.Pen("Dark Green", 10.0); + public static readonly double levelAlpha = 0.8; + + + private readonly List stack = new List(); + + + public ToolFull(Workarea workarea): base(workarea) { } + + public override ModifierTypes getAvailableModifierTypes() + { return ModifierTypes.All; } + + private DynamicSurface getSurface(int level) + { return level > 0 ? stack[level - 1] : workarea.document.canvas; } + private DynamicSurface getSurface() + { return getSurface(stack.Count); } + + public override bool paintPush() { + stack.Add(new DynamicSurface()); + return true; + } + + private void paintPoint(DynamicSurface surface, Cairo.Context context, Track.WayPoint point) { + Point p = point.point.position; + double r = pen.width*point.point.pressure; + double rr = r + 1.0; + + surface.expandContext(ref context, new Rectangle(p.x - rr, p.y - rr, p.x + rr, p.y + rr)); + + context.Save(); + pen.apply(context); + context.Arc(p.x, p.y, r, 0.0, 2.0*Math.PI); + context.Fill(); + context.Restore(); + } + + public override void paintTracks(List tracks) { + DynamicSurface surface = getSurface(); + Cairo.Context context = surface.getContext(); + while(true) { + Track track = null; + long minTicks = long.MaxValue; + double minTimeOffset = 0.0; + foreach(Track t in tracks) { + if (t.wayPointsAdded > 0) { + long ticks = t.ticks; + double timeOffset = t.timeOffset + t.points[t.points.Count - t.wayPointsAdded].time; + if ((ticks - minTicks)*Timer.frequency + timeOffset - minTimeOffset < 0.0) { + track = t; + minTicks = ticks; + minTimeOffset = timeOffset; + } + } + } + if (track == null) break; + paintPoint(surface, context, track.points[track.points.Count - track.wayPointsAdded]); + --track.wayPointsAdded; + } + context.GetTarget().Flush(); + context.Dispose(); + } + + public override int paintApply(int count) { + int level = stack.Count - count; + DynamicSurface surface = getSurface(level); + Cairo.Context context = surface.getContext(); + for(int i = level; i < stack.Count; ++i) { + surface.expandContext(ref context, stack[i].getBounds(), true); + stack[i].draw(context); + } + context.GetTarget().Flush(); + context.Dispose(); + paintPop(count); + return count; + } + + public override void paintCancel() + { getSurface().clear(); } + + public override void paintPop(int count) { + int level = stack.Count - count; + for(int i = stack.Count - 1; i >= level; --i) + stack[i].Dispose(); + stack.RemoveRange(level, count); + } + + public override void draw(Cairo.Context context) { + double alpha = levelAlpha; + foreach(DynamicSurface surface in stack) + { surface.draw(context, alpha); alpha *= levelAlpha; } + } + } +} \ No newline at end of file diff --git a/mono/Assistance/Workarea.cs b/mono/Assistance/Workarea.cs index 90ced8b..4850dff 100644 --- a/mono/Assistance/Workarea.cs +++ b/mono/Assistance/Workarea.cs @@ -5,9 +5,47 @@ using System.Linq; namespace Assistance { public class Workarea { public readonly Document document; + public readonly InputManager inputManager; + + private readonly InputModifierTangents modifierTangents; + private readonly InputModifierAssistants modifierAssistantsSimple; + private readonly InputModifierAssistants modifierAssistantsTangents; + private readonly InputModifierInterpolation modifierInterpolation; public Workarea() { document = new Document(this); + inputManager = new InputManager(this); + + modifierTangents = new InputModifierTangents(); + modifierAssistantsSimple = new InputModifierAssistants(this, false); + modifierAssistantsTangents = new InputModifierAssistants(this, true); + modifierInterpolation = new InputModifierInterpolation(); + } + + public Tool getTool() + { return inputManager.getTool(); } + + public void setTool(Tool tool, bool force = false) { + if (getTool() != tool || force) { + inputManager.deactivate(); + inputManager.clearModifiers(); + if (tool != null) { + Tool.ModifierTypes types = tool.getAvailableModifierTypes(); + if ((Tool.ModifierTypes.Tangents & types) != 0 && (Tool.ModifierTypes.Guideline & types) == 0) + inputManager.addModifier(modifierTangents); + if ((Tool.ModifierTypes.Tangents & types) == 0 && (Tool.ModifierTypes.Guideline & types) != 0) + inputManager.addModifier(modifierAssistantsSimple); + if ((Tool.ModifierTypes.Tangents & types) != 0 && (Tool.ModifierTypes.Guideline & types) != 0) + inputManager.addModifier(modifierAssistantsTangents); + if ((Tool.ModifierTypes.Multiline & types) != 0) + foreach(Modifier modifier in document.modifiers) + inputManager.addModifier(modifier); + if ((Tool.ModifierTypes.Interpolation & types) != 0) + inputManager.addModifier(modifierInterpolation); + } + inputManager.setTool(tool); + inputManager.activate(); + } } public ActivePoint findPoint(Point position) { @@ -22,32 +60,12 @@ namespace Assistance { assistant.getGuidelines(outGuidelines, target); } - public void draw(Cairo.Context context, ActivePoint activePoint, Point target, Track track) { + public void draw(Cairo.Context context, ActivePoint activePoint) { // canvas document.canvas.draw(context); - // guidelines and track - List guidelines = new List(); - if (track != null && track.points.Count > 0) { - Guideline guideline; - Track modifiedTrack = modifyTrackByAssistant(track, out guideline); - - getGuidelines(guidelines, modifiedTrack.transform(track.points.Last()).point); - foreach(Guideline gl in guidelines) - gl.draw(context); - - track.draw(context, true); - if (guideline != null) guideline.draw(context, true); - - List modifiedTracks = modifyTrackByModifiers(modifiedTrack); - rebuildTracks(modifiedTracks); - foreach(Track t in modifiedTracks) - t.draw(context); - } else { - getGuidelines(guidelines, target); - foreach(Guideline guideline in guidelines) - guideline.draw(context); - } + // input manager + inputManager.draw(context); // modifiers foreach(Modifier modifier in document.modifiers) @@ -61,45 +79,6 @@ namespace Assistance { foreach(ActivePoint point in document.points) point.draw(context, activePoint == point); } - - public Track modifyTrackByAssistant(Track track, out Guideline guideline) { - guideline = null; - if (track.points.Count < 1) - return track.createChild(Geometry.noTransform); - - List guidelines = new List(); - getGuidelines(guidelines, track.points[0].point); - guideline = Guideline.findBest(guidelines, track); - if (guideline == null) - return track.createChild(Geometry.noTransform); - return track.createChild(guideline.transformPoint); - } - - public List modifyTrackByModifiers(Track track) { - List tracks = new List() { track }; - foreach(Modifier modifier in document.modifiers) - tracks = modifier.modify(tracks); - return tracks; - } - - public void rebuildTracks(List tracks) { - foreach(Track track in tracks) - track.rebuild(); - } - - public List modifyTrack(Track track) { - Guideline guideline; - List tracks = modifyTrackByModifiers( - modifyTrackByAssistant(track, out guideline) ); - rebuildTracks(tracks); - return tracks; - } - - public void paintTrack(Track track) { - List tracks = modifyTrack(track); - foreach(Track t in tracks) - document.canvas.paintTrack(t); - } } }