2024-05-01 18:23:32 +08:00
|
|
|
|
using Ink_Canvas.Helpers;
|
2024-06-09 15:14:58 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System;
|
2024-05-01 18:23:32 +08:00
|
|
|
|
using System.Windows;
|
|
|
|
|
using System.Windows.Controls;
|
|
|
|
|
using System.Windows.Ink;
|
2024-06-09 15:14:58 +08:00
|
|
|
|
using System.Windows.Input;
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
using System.Diagnostics;
|
2024-06-10 18:05:32 +08:00
|
|
|
|
using System.Windows.Media.Imaging;
|
2024-05-01 18:23:32 +08:00
|
|
|
|
|
|
|
|
|
namespace Ink_Canvas {
|
|
|
|
|
public partial class MainWindow : Window {
|
|
|
|
|
private enum CommitReason {
|
|
|
|
|
UserInput,
|
|
|
|
|
CodeInput,
|
|
|
|
|
ShapeDrawing,
|
|
|
|
|
ShapeRecognition,
|
|
|
|
|
ClearingCanvas,
|
2024-05-11 01:35:32 +08:00
|
|
|
|
Manipulation
|
2024-05-01 18:23:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CommitReason _currentCommitType = CommitReason.UserInput;
|
|
|
|
|
private bool IsEraseByPoint => inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint;
|
|
|
|
|
private StrokeCollection ReplacedStroke;
|
|
|
|
|
private StrokeCollection AddedStroke;
|
|
|
|
|
private StrokeCollection CuboidStrokeCollection;
|
2024-06-09 15:14:58 +08:00
|
|
|
|
private Dictionary<Stroke, Tuple<StylusPointCollection, StylusPointCollection>> StrokeManipulationHistory;
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
private Dictionary<Stroke, StylusPointCollection> StrokeInitialHistory =
|
|
|
|
|
new Dictionary<Stroke, StylusPointCollection>();
|
|
|
|
|
|
|
|
|
|
private Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>> DrawingAttributesHistory =
|
|
|
|
|
new Dictionary<Stroke, Tuple<DrawingAttributes, DrawingAttributes>>();
|
|
|
|
|
|
|
|
|
|
private Dictionary<Guid, List<Stroke>> DrawingAttributesHistoryFlag = new Dictionary<Guid, List<Stroke>>() {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
{ DrawingAttributeIds.Color, new List<Stroke>() },
|
|
|
|
|
{ DrawingAttributeIds.DrawingFlags, new List<Stroke>() },
|
|
|
|
|
{ DrawingAttributeIds.IsHighlighter, new List<Stroke>() },
|
|
|
|
|
{ DrawingAttributeIds.StylusHeight, new List<Stroke>() },
|
|
|
|
|
{ DrawingAttributeIds.StylusTip, new List<Stroke>() },
|
|
|
|
|
{ DrawingAttributeIds.StylusTipTransform, new List<Stroke>() },
|
|
|
|
|
{ DrawingAttributeIds.StylusWidth, new List<Stroke>() }
|
|
|
|
|
};
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
2024-05-01 18:23:32 +08:00
|
|
|
|
private TimeMachine timeMachine = new TimeMachine();
|
|
|
|
|
|
2024-06-09 21:29:03 +08:00
|
|
|
|
private void ApplyHistoryToCanvas(TimeMachineHistory item, InkCanvas applyCanvas = null) {
|
2024-06-05 19:57:15 +08:00
|
|
|
|
_currentCommitType = CommitReason.CodeInput;
|
2024-06-09 21:29:03 +08:00
|
|
|
|
var canvas = inkCanvas;
|
|
|
|
|
if (applyCanvas != null && applyCanvas is InkCanvas) {
|
|
|
|
|
canvas = applyCanvas;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-05 19:57:15 +08:00
|
|
|
|
if (item.CommitType == TimeMachineHistoryType.UserInput) {
|
|
|
|
|
if (!item.StrokeHasBeenCleared) {
|
2024-06-05 20:25:26 +08:00
|
|
|
|
foreach (var strokes in item.CurrentStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (!canvas.Strokes.Contains(strokes))
|
|
|
|
|
canvas.Strokes.Add(strokes);
|
|
|
|
|
} else {
|
2024-06-05 20:25:26 +08:00
|
|
|
|
foreach (var strokes in item.CurrentStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (canvas.Strokes.Contains(strokes))
|
|
|
|
|
canvas.Strokes.Remove(strokes);
|
2024-06-05 19:57:15 +08:00
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
} else if (item.CommitType == TimeMachineHistoryType.ShapeRecognition) {
|
2024-06-05 19:57:15 +08:00
|
|
|
|
if (item.StrokeHasBeenCleared) {
|
2024-06-05 20:25:26 +08:00
|
|
|
|
foreach (var strokes in item.CurrentStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (canvas.Strokes.Contains(strokes))
|
|
|
|
|
canvas.Strokes.Remove(strokes);
|
2024-06-05 19:57:15 +08:00
|
|
|
|
|
2024-06-05 20:25:26 +08:00
|
|
|
|
foreach (var strokes in item.ReplacedStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (!canvas.Strokes.Contains(strokes))
|
|
|
|
|
canvas.Strokes.Add(strokes);
|
|
|
|
|
} else {
|
2024-06-05 20:25:26 +08:00
|
|
|
|
foreach (var strokes in item.CurrentStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (!canvas.Strokes.Contains(strokes))
|
|
|
|
|
canvas.Strokes.Add(strokes);
|
2024-06-05 19:57:15 +08:00
|
|
|
|
|
2024-06-05 20:25:26 +08:00
|
|
|
|
foreach (var strokes in item.ReplacedStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (canvas.Strokes.Contains(strokes))
|
|
|
|
|
canvas.Strokes.Remove(strokes);
|
2024-06-05 19:57:15 +08:00
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
} else if (item.CommitType == TimeMachineHistoryType.Manipulation) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
if (!item.StrokeHasBeenCleared) {
|
|
|
|
|
foreach (var currentStroke in item.StylusPointDictionary) {
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (canvas.Strokes.Contains(currentStroke.Key)) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
currentStroke.Key.StylusPoints = currentStroke.Value.Item2;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
} else {
|
|
|
|
|
foreach (var currentStroke in item.StylusPointDictionary) {
|
|
|
|
|
if (canvas.Strokes.Contains(currentStroke.Key)) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
currentStroke.Key.StylusPoints = currentStroke.Value.Item1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
} else if (item.CommitType == TimeMachineHistoryType.DrawingAttributes) {
|
|
|
|
|
if (!item.StrokeHasBeenCleared) {
|
|
|
|
|
foreach (var currentStroke in item.DrawingAttributes) {
|
|
|
|
|
if (canvas.Strokes.Contains(currentStroke.Key)) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
currentStroke.Key.DrawingAttributes = currentStroke.Value.Item2;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
} else {
|
|
|
|
|
foreach (var currentStroke in item.DrawingAttributes) {
|
|
|
|
|
if (canvas.Strokes.Contains(currentStroke.Key)) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
currentStroke.Key.DrawingAttributes = currentStroke.Value.Item1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (item.CommitType == TimeMachineHistoryType.Clear) {
|
2024-06-05 19:57:15 +08:00
|
|
|
|
if (!item.StrokeHasBeenCleared) {
|
2024-06-05 20:25:26 +08:00
|
|
|
|
if (item.CurrentStroke != null)
|
|
|
|
|
foreach (var currentStroke in item.CurrentStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (!canvas.Strokes.Contains(currentStroke))
|
|
|
|
|
canvas.Strokes.Add(currentStroke);
|
2024-06-05 19:57:15 +08:00
|
|
|
|
|
2024-06-05 20:25:26 +08:00
|
|
|
|
if (item.ReplacedStroke != null)
|
|
|
|
|
foreach (var replacedStroke in item.ReplacedStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (canvas.Strokes.Contains(replacedStroke))
|
|
|
|
|
canvas.Strokes.Remove(replacedStroke);
|
|
|
|
|
} else {
|
2024-06-05 20:25:26 +08:00
|
|
|
|
if (item.ReplacedStroke != null)
|
|
|
|
|
foreach (var replacedStroke in item.ReplacedStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (!canvas.Strokes.Contains(replacedStroke))
|
|
|
|
|
canvas.Strokes.Add(replacedStroke);
|
2024-06-05 19:57:15 +08:00
|
|
|
|
|
2024-06-05 20:25:26 +08:00
|
|
|
|
if (item.CurrentStroke != null)
|
|
|
|
|
foreach (var currentStroke in item.CurrentStroke)
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (canvas.Strokes.Contains(currentStroke))
|
|
|
|
|
canvas.Strokes.Remove(currentStroke);
|
2024-06-05 19:57:15 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_currentCommitType = CommitReason.UserInput;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 21:29:03 +08:00
|
|
|
|
private StrokeCollection ApplyHistoriesToNewStrokeCollection(TimeMachineHistory[] items) {
|
|
|
|
|
InkCanvas fakeInkCanv = new InkCanvas() {
|
|
|
|
|
Width = inkCanvas.ActualWidth,
|
|
|
|
|
Height = inkCanvas.ActualHeight,
|
|
|
|
|
EditingMode = InkCanvasEditingMode.None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (items != null && items.Length > 0) {
|
|
|
|
|
foreach (var timeMachineHistory in items) {
|
|
|
|
|
ApplyHistoryToCanvas(timeMachineHistory, fakeInkCanv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fakeInkCanv.Strokes;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 18:23:32 +08:00
|
|
|
|
private void TimeMachine_OnUndoStateChanged(bool status) {
|
|
|
|
|
var result = status ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
|
BtnUndo.Visibility = result;
|
|
|
|
|
BtnUndo.IsEnabled = status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void TimeMachine_OnRedoStateChanged(bool status) {
|
|
|
|
|
var result = status ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
|
BtnRedo.Visibility = result;
|
|
|
|
|
BtnRedo.IsEnabled = status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void StrokesOnStrokesChanged(object sender, StrokeCollectionChangedEventArgs e) {
|
|
|
|
|
if (!isHidingSubPanelsWhenInking) {
|
|
|
|
|
isHidingSubPanelsWhenInking = true;
|
|
|
|
|
HideSubPanels(); // 书写时自动隐藏二级菜单
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 21:29:03 +08:00
|
|
|
|
foreach (var stroke in e?.Removed) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
stroke.StylusPointsChanged -= Stroke_StylusPointsChanged;
|
|
|
|
|
stroke.StylusPointsReplaced -= Stroke_StylusPointsReplaced;
|
|
|
|
|
stroke.DrawingAttributesChanged -= Stroke_DrawingAttributesChanged;
|
|
|
|
|
StrokeInitialHistory.Remove(stroke);
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
foreach (var stroke in e?.Added) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
stroke.StylusPointsChanged += Stroke_StylusPointsChanged;
|
|
|
|
|
stroke.StylusPointsReplaced += Stroke_StylusPointsReplaced;
|
|
|
|
|
stroke.DrawingAttributesChanged += Stroke_DrawingAttributesChanged;
|
|
|
|
|
StrokeInitialHistory[stroke] = stroke.StylusPoints.Clone();
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 18:23:32 +08:00
|
|
|
|
if (_currentCommitType == CommitReason.CodeInput || _currentCommitType == CommitReason.ShapeDrawing) return;
|
2024-06-05 20:25:26 +08:00
|
|
|
|
|
2024-05-01 18:23:32 +08:00
|
|
|
|
if ((e.Added.Count != 0 || e.Removed.Count != 0) && IsEraseByPoint) {
|
|
|
|
|
if (AddedStroke == null) AddedStroke = new StrokeCollection();
|
|
|
|
|
if (ReplacedStroke == null) ReplacedStroke = new StrokeCollection();
|
|
|
|
|
AddedStroke.Add(e.Added);
|
|
|
|
|
ReplacedStroke.Add(e.Removed);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-06-05 19:57:15 +08:00
|
|
|
|
|
2024-05-01 18:23:32 +08:00
|
|
|
|
if (e.Added.Count != 0) {
|
|
|
|
|
if (_currentCommitType == CommitReason.ShapeRecognition) {
|
|
|
|
|
timeMachine.CommitStrokeShapeHistory(ReplacedStroke, e.Added);
|
|
|
|
|
ReplacedStroke = null;
|
|
|
|
|
return;
|
2024-06-09 21:29:03 +08:00
|
|
|
|
} else {
|
2024-05-01 18:23:32 +08:00
|
|
|
|
timeMachine.CommitStrokeUserInputHistory(e.Added);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e.Removed.Count != 0) {
|
|
|
|
|
if (_currentCommitType == CommitReason.ShapeRecognition) {
|
|
|
|
|
ReplacedStroke = e.Removed;
|
|
|
|
|
return;
|
2024-06-09 21:29:03 +08:00
|
|
|
|
} else if (!IsEraseByPoint || _currentCommitType == CommitReason.ClearingCanvas) {
|
2024-05-01 18:23:32 +08:00
|
|
|
|
timeMachine.CommitStrokeEraseHistory(e.Removed);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-09 15:14:58 +08:00
|
|
|
|
|
2024-06-09 21:29:03 +08:00
|
|
|
|
private void Stroke_DrawingAttributesChanged(object sender, PropertyDataChangedEventArgs e) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
var key = sender as Stroke;
|
|
|
|
|
var currentValue = key.DrawingAttributes.Clone();
|
|
|
|
|
DrawingAttributesHistory.TryGetValue(key, out var previousTuple);
|
|
|
|
|
var previousValue = previousTuple?.Item1 ?? currentValue.Clone();
|
|
|
|
|
var needUpdateValue = !DrawingAttributesHistoryFlag[e.PropertyGuid].Contains(key);
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (needUpdateValue) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
DrawingAttributesHistoryFlag[e.PropertyGuid].Add(key);
|
|
|
|
|
Debug.Write(e.PreviousValue.ToString());
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
if (e.PropertyGuid == DrawingAttributeIds.Color && needUpdateValue) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
previousValue.Color = (Color)e.PreviousValue;
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
if (e.PropertyGuid == DrawingAttributeIds.IsHighlighter && needUpdateValue) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
previousValue.IsHighlighter = (bool)e.PreviousValue;
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
if (e.PropertyGuid == DrawingAttributeIds.StylusHeight && needUpdateValue) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
previousValue.Height = (double)e.PreviousValue;
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
if (e.PropertyGuid == DrawingAttributeIds.StylusWidth && needUpdateValue) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
previousValue.Width = (double)e.PreviousValue;
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
if (e.PropertyGuid == DrawingAttributeIds.StylusTip && needUpdateValue) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
previousValue.StylusTip = (StylusTip)e.PreviousValue;
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
if (e.PropertyGuid == DrawingAttributeIds.StylusTipTransform && needUpdateValue) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
previousValue.StylusTipTransform = (Matrix)e.PreviousValue;
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
if (e.PropertyGuid == DrawingAttributeIds.DrawingFlags && needUpdateValue) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
previousValue.IgnorePressure = (bool)e.PreviousValue;
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
DrawingAttributesHistory[key] =
|
|
|
|
|
new Tuple<DrawingAttributes, DrawingAttributes>(previousValue, currentValue);
|
2024-06-09 15:14:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 21:29:03 +08:00
|
|
|
|
private void Stroke_StylusPointsReplaced(object sender, StylusPointsReplacedEventArgs e) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
StrokeInitialHistory[sender as Stroke] = e.NewStylusPoints.Clone();
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 21:29:03 +08:00
|
|
|
|
private void Stroke_StylusPointsChanged(object sender, EventArgs e) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
var selectedStrokes = inkCanvas.GetSelectedStrokes();
|
|
|
|
|
var count = selectedStrokes.Count;
|
|
|
|
|
if (count == 0) count = inkCanvas.Strokes.Count;
|
2024-06-09 21:29:03 +08:00
|
|
|
|
if (StrokeManipulationHistory == null) {
|
|
|
|
|
StrokeManipulationHistory =
|
|
|
|
|
new Dictionary<Stroke, Tuple<StylusPointCollection, StylusPointCollection>>();
|
2024-06-09 15:14:58 +08:00
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
2024-06-09 15:14:58 +08:00
|
|
|
|
StrokeManipulationHistory[sender as Stroke] =
|
2024-06-09 21:29:03 +08:00
|
|
|
|
new Tuple<StylusPointCollection, StylusPointCollection>(StrokeInitialHistory[sender as Stroke],
|
|
|
|
|
(sender as Stroke).StylusPoints.Clone());
|
|
|
|
|
if ((StrokeManipulationHistory.Count == count || sender == null) && dec.Count == 0) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
timeMachine.CommitStrokeManipulationHistory(StrokeManipulationHistory);
|
2024-06-09 21:29:03 +08:00
|
|
|
|
foreach (var item in StrokeManipulationHistory) {
|
2024-06-09 15:14:58 +08:00
|
|
|
|
StrokeInitialHistory[item.Key] = item.Value.Item2;
|
|
|
|
|
}
|
2024-06-09 21:29:03 +08:00
|
|
|
|
|
2024-06-09 15:14:58 +08:00
|
|
|
|
StrokeManipulationHistory = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-01 18:23:32 +08:00
|
|
|
|
}
|
2024-06-05 19:57:15 +08:00
|
|
|
|
}
|