222 lines
8.7 KiB
C#
222 lines
8.7 KiB
C#
|
|
using System;
|
||
|
|
using System.IO;
|
||
|
|
using System.IO.Pipes;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Windows;
|
||
|
|
using System.Windows.Ink;
|
||
|
|
using System.Windows.Input;
|
||
|
|
|
||
|
|
namespace InkCanvasForClass.IACoreHelper
|
||
|
|
{
|
||
|
|
internal static class Program
|
||
|
|
{
|
||
|
|
private static readonly string LogPath = Path.Combine(
|
||
|
|
AppDomain.CurrentDomain.BaseDirectory, "IACoreHelper.log");
|
||
|
|
|
||
|
|
private static void Log(string msg)
|
||
|
|
{
|
||
|
|
try { File.AppendAllText(LogPath, $"{DateTime.Now:HH:mm:ss.fff} {msg}{Environment.NewLine}"); }
|
||
|
|
catch { }
|
||
|
|
}
|
||
|
|
|
||
|
|
[STAThread]
|
||
|
|
static void Main(string[] args)
|
||
|
|
{
|
||
|
|
Log($"=== Helper started, args=[{string.Join(",", args)}], cwd={Environment.CurrentDirectory}, baseDir={AppDomain.CurrentDomain.BaseDirectory} ===");
|
||
|
|
|
||
|
|
if (args.Length < 1 || !int.TryParse(args[0], out int parentPid))
|
||
|
|
{
|
||
|
|
Console.Error.WriteLine("Usage: IACoreHelper.exe <parentPid>");
|
||
|
|
Log("Missing parentPid argument, exiting");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
string pipeName = string.Format(IpcConstants.PipeName, parentPid);
|
||
|
|
Log($"Pipe name: {pipeName}");
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
RunPipeServer(pipeName);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Log($"FATAL: {ex}");
|
||
|
|
Console.Error.WriteLine("IACoreHelper fatal: " + ex.Message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static void RunPipeServer(string pipeName)
|
||
|
|
{
|
||
|
|
while (true)
|
||
|
|
{
|
||
|
|
using (var server = new NamedPipeServerStream(
|
||
|
|
pipeName,
|
||
|
|
PipeDirection.InOut,
|
||
|
|
1,
|
||
|
|
PipeTransmissionMode.Byte,
|
||
|
|
PipeOptions.WriteThrough))
|
||
|
|
{
|
||
|
|
server.WaitForConnection();
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
using (var reader = new BinaryReader(server, System.Text.Encoding.UTF8, leaveOpen: true))
|
||
|
|
using (var writer = new BinaryWriter(server, System.Text.Encoding.UTF8, leaveOpen: true))
|
||
|
|
{
|
||
|
|
byte cmd = reader.ReadByte();
|
||
|
|
|
||
|
|
if (cmd == IpcConstants.CmdShutdown)
|
||
|
|
return;
|
||
|
|
|
||
|
|
if (cmd == IpcConstants.CmdRecognize)
|
||
|
|
{
|
||
|
|
var request = RecognizeRequest.ReadFrom(reader);
|
||
|
|
var response = HandleRecognize(request);
|
||
|
|
response.WriteTo(writer);
|
||
|
|
writer.Flush();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Console.Error.WriteLine("IACoreHelper pipe error: " + ex.Message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// 每次连接后重新监听,支持多次调用
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static RecognizeResponse HandleRecognize(RecognizeRequest request)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
Log($"HandleRecognize: strokes={request.Strokes?.Length ?? 0}");
|
||
|
|
var strokes = BuildStrokeCollection(request);
|
||
|
|
Log($"Built StrokeCollection: count={strokes.Count}");
|
||
|
|
if (strokes.Count == 0)
|
||
|
|
return new RecognizeResponse { Success = false, ShapeName = string.Empty };
|
||
|
|
|
||
|
|
var result = RecognizeCore(strokes);
|
||
|
|
Log($"RecognizeCore result: success={result.Success}, shape={result.ShapeName}");
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Log($"HandleRecognize EXCEPTION: {ex}");
|
||
|
|
Console.Error.WriteLine("IACoreHelper recognize error: " + ex.Message);
|
||
|
|
return new RecognizeResponse { Success = false, ShapeName = string.Empty };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static StrokeCollection BuildStrokeCollection(RecognizeRequest request)
|
||
|
|
{
|
||
|
|
var sc = new StrokeCollection();
|
||
|
|
foreach (var strokeDto in request.Strokes)
|
||
|
|
{
|
||
|
|
if (strokeDto.Points == null || strokeDto.Points.Length == 0)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
var stylusPoints = new StylusPointCollection();
|
||
|
|
foreach (var pt in strokeDto.Points)
|
||
|
|
stylusPoints.Add(new StylusPoint(pt.X, pt.Y, pt.Pressure));
|
||
|
|
|
||
|
|
sc.Add(new Stroke(stylusPoints));
|
||
|
|
}
|
||
|
|
return sc;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static RecognizeResponse RecognizeCore(StrokeCollection strokes)
|
||
|
|
{
|
||
|
|
var analyzer = new InkAnalyzer();
|
||
|
|
analyzer.AddStrokes(strokes);
|
||
|
|
analyzer.SetStrokesType(strokes, StrokeType.Drawing);
|
||
|
|
|
||
|
|
AnalysisAlternate analysisAlternate = null;
|
||
|
|
int strokesCount = strokes.Count;
|
||
|
|
var analysisStatus = analyzer.Analyze();
|
||
|
|
|
||
|
|
if (analysisStatus.Successful)
|
||
|
|
{
|
||
|
|
var alternates = analyzer.GetAlternates();
|
||
|
|
if (alternates.Count > 0)
|
||
|
|
{
|
||
|
|
while (strokesCount >= 2)
|
||
|
|
{
|
||
|
|
var alt0 = alternates[0];
|
||
|
|
if (alt0?.AlternateNodes == null || alt0.AlternateNodes.Count == 0)
|
||
|
|
break;
|
||
|
|
var drawNode = alt0.AlternateNodes[0] as InkDrawingNode;
|
||
|
|
if (drawNode == null)
|
||
|
|
break;
|
||
|
|
bool shapeOk = IsContainShapeType(drawNode.GetShapeName());
|
||
|
|
if (alt0.Strokes.Contains(strokes.Last()) && shapeOk)
|
||
|
|
break;
|
||
|
|
analyzer.RemoveStroke(strokes[strokes.Count - strokesCount]);
|
||
|
|
strokesCount--;
|
||
|
|
analysisStatus = analyzer.Analyze();
|
||
|
|
if (analysisStatus.Successful)
|
||
|
|
alternates = analyzer.GetAlternates();
|
||
|
|
else
|
||
|
|
break;
|
||
|
|
if (alternates.Count == 0)
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (alternates.Count > 0)
|
||
|
|
{
|
||
|
|
var altFinal = alternates[0];
|
||
|
|
if (altFinal?.AlternateNodes != null && altFinal.AlternateNodes.Count > 0)
|
||
|
|
analysisAlternate = altFinal;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
analyzer.Dispose();
|
||
|
|
|
||
|
|
if (analysisAlternate?.AlternateNodes == null || analysisAlternate.AlternateNodes.Count == 0)
|
||
|
|
return new RecognizeResponse { Success = false, ShapeName = string.Empty };
|
||
|
|
|
||
|
|
var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode;
|
||
|
|
if (node == null)
|
||
|
|
return new RecognizeResponse { Success = false, ShapeName = string.Empty };
|
||
|
|
|
||
|
|
var shape = node.GetShape();
|
||
|
|
var center = node.Centroid;
|
||
|
|
var hot = node.HotPoints;
|
||
|
|
|
||
|
|
float[] hotX = new float[hot?.Count ?? 0];
|
||
|
|
float[] hotY = new float[hot?.Count ?? 0];
|
||
|
|
if (hot != null)
|
||
|
|
for (int i = 0; i < hot.Count; i++) { hotX[i] = (float)hot[i].X; hotY[i] = (float)hot[i].Y; }
|
||
|
|
|
||
|
|
// 计算参与识别的笔画在原始集合中的下标
|
||
|
|
var participatingStrokes = analysisAlternate.Strokes;
|
||
|
|
int[] strokeIndices = new int[participatingStrokes?.Count ?? 0];
|
||
|
|
if (participatingStrokes != null)
|
||
|
|
for (int i = 0; i < participatingStrokes.Count; i++)
|
||
|
|
strokeIndices[i] = strokes.IndexOf(participatingStrokes[i]);
|
||
|
|
|
||
|
|
return new RecognizeResponse
|
||
|
|
{
|
||
|
|
Success = true,
|
||
|
|
ShapeName = node.GetShapeName() ?? string.Empty,
|
||
|
|
CentroidX = (float)center.X,
|
||
|
|
CentroidY = (float)center.Y,
|
||
|
|
ShapeWidth = shape != null ? (float)shape.Width : 0f,
|
||
|
|
ShapeHeight = shape != null ? (float)shape.Height : 0f,
|
||
|
|
HotPointsX = hotX,
|
||
|
|
HotPointsY = hotY,
|
||
|
|
StrokeIndices = strokeIndices
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
private static bool IsContainShapeType(string name)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrEmpty(name)) return false;
|
||
|
|
return name.Contains("Triangle") || name.Contains("Circle") ||
|
||
|
|
name.Contains("Rectangle") || name.Contains("Diamond") ||
|
||
|
|
name.Contains("Parallelogram") || name.Contains("Square") ||
|
||
|
|
name.Contains("Ellipse");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|