mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-03-03 00:00:57 +08:00
311 lines
9.6 KiB
C#
311 lines
9.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Media;
|
|
using ZXing.Common;
|
|
using ZXing.Rendering;
|
|
using ZXing;
|
|
using System.Windows;
|
|
|
|
namespace AIStudio.Wpf.DiagramDesigner.Additionals.Controls
|
|
{
|
|
/// <summary>
|
|
/// Renders a barcode into a geometry
|
|
/// Autor: Rob Fonseca-Ensor
|
|
/// </summary>
|
|
public class GeometryRenderer : IBarcodeRenderer<Geometry>
|
|
{
|
|
/// <summary>
|
|
/// Renders the specified matrix.
|
|
/// </summary>
|
|
/// <param name="matrix">The matrix.</param>
|
|
/// <param name="format">The format.</param>
|
|
/// <param name="content">The content.</param>
|
|
/// <returns></returns>
|
|
public Geometry Render(BitMatrix matrix, BarcodeFormat format, string content)
|
|
{
|
|
return Render(matrix, format, content, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renders the specified matrix.
|
|
/// </summary>
|
|
/// <param name="matrix">The matrix.</param>
|
|
/// <param name="format">The format.</param>
|
|
/// <param name="content">The content.</param>
|
|
/// <param name="options">The options.</param>
|
|
/// <returns></returns>
|
|
public Geometry Render(BitMatrix matrix, BarcodeFormat format, string content, EncodingOptions options)
|
|
{
|
|
var edges = new HashSet<Edge>();
|
|
var edgeMap = new Dictionary<Coordinate, List<Edge>>();
|
|
var cols = matrix.Width;
|
|
var rows = matrix.Height;
|
|
|
|
for (int c = 0; c <= cols; c++)
|
|
{
|
|
for (int r = 0; r <= rows; r++)
|
|
{
|
|
var cell = GetCell(c, r, matrix);
|
|
var westCell = GetCell(c - 1, r, matrix);
|
|
var northCell = GetCell(c, r - 1, matrix);
|
|
if (northCell != cell)
|
|
{
|
|
AddEdge(new Edge(c, r, c + 1, r), edges, edgeMap);
|
|
}
|
|
|
|
if (westCell != cell)
|
|
{
|
|
AddEdge(new Edge(c, r, c, r + 1), edges, edgeMap);
|
|
}
|
|
}
|
|
}
|
|
|
|
var cycles = new List<List<Coordinate>>();
|
|
|
|
while (edges.Count > 0)
|
|
{
|
|
var edge = edges.First();
|
|
RemoveEdge(edge, edges, edgeMap);
|
|
|
|
if (IsEdgeLeftHand(matrix, edge))
|
|
{
|
|
edge = edge.Reversed();
|
|
}
|
|
|
|
var currentCycle = new List<Coordinate> { edge.From, edge.To };
|
|
while (edge.To != currentCycle[0])
|
|
{
|
|
var moves = from direction in Turns(edge.From - edge.To)
|
|
let nextCoordinate = direction + edge.To
|
|
from e in EdgesFrom(edge.To, edgeMap)
|
|
where e.To == nextCoordinate || e.From == nextCoordinate
|
|
select e;
|
|
|
|
var nextEdge = moves.First();
|
|
RemoveEdge(nextEdge, edges, edgeMap);
|
|
edge = nextEdge.To != edge.To ? nextEdge : nextEdge.Reversed();
|
|
currentCycle.Add(edge.To);
|
|
}
|
|
cycles.Add(currentCycle);
|
|
}
|
|
|
|
return new PathGeometry(cycles.Select(x => new PathFigure(x.First().ToPoint(1), x.Skip(1).Select(y => new LineSegment(y.ToPoint(1), true)), true)));
|
|
|
|
}
|
|
|
|
private static bool IsEdgeLeftHand(BitMatrix b, Edge edge)
|
|
{
|
|
var cell = GetCell(edge.From.Col, edge.From.Row, b);
|
|
return (edge.From.Row < edge.To.Row && cell) || (!(edge.From.Col < edge.To.Col && cell));
|
|
}
|
|
|
|
private static IEnumerable<Edge> EdgesFrom(Coordinate c, Dictionary<Coordinate, List<Edge>> edgeMap)
|
|
{
|
|
return edgeMap.ContainsKey(c) ? edgeMap[c] : Enumerable.Empty<Edge>();
|
|
}
|
|
|
|
private static IEnumerable<Coordinate> Turns(Coordinate currentDirection)
|
|
{
|
|
int index = Array.IndexOf(Coordinate.Directions, currentDirection);
|
|
return Coordinate.Directions.Skip(index + 1).Concat(Coordinate.Directions.Take(index));
|
|
}
|
|
|
|
private static bool GetCell(int c, int r, BitMatrix matrix)
|
|
{
|
|
if (r < 0 || r >= matrix.Height)
|
|
{
|
|
return false;
|
|
}
|
|
if (c < 0 || c >= matrix.Width)
|
|
{
|
|
return false;
|
|
}
|
|
return matrix[c, r];
|
|
}
|
|
|
|
private static void RemoveEdge(Edge e, HashSet<Edge> edges, Dictionary<Coordinate, List<Edge>> edgeMap)
|
|
{
|
|
edges.Remove(e);
|
|
edgeMap[e.From].Remove(e);
|
|
edgeMap[e.To].Remove(e);
|
|
}
|
|
|
|
private static void AddEdge(Edge e, HashSet<Edge> edges, Dictionary<Coordinate, List<Edge>> edgeMap)
|
|
{
|
|
edges.Add(e);
|
|
AddCoordinate(e.From, e, edgeMap);
|
|
AddCoordinate(e.To, e, edgeMap);
|
|
}
|
|
|
|
private static void AddCoordinate(Coordinate c, Edge e, Dictionary<Coordinate, List<Edge>> edgeMap)
|
|
{
|
|
List<Edge> list;
|
|
if (!edgeMap.TryGetValue(c, out list))
|
|
{
|
|
edgeMap[c] = list = new List<Edge>();
|
|
}
|
|
list.Add(e);
|
|
}
|
|
|
|
|
|
private struct Coordinate
|
|
{
|
|
public readonly int Row, Col;
|
|
|
|
public Coordinate(int col, int row)
|
|
{
|
|
Col = col;
|
|
Row = row;
|
|
}
|
|
|
|
public bool Equals(Coordinate other)
|
|
{
|
|
return other.Row == Row && other.Col == Col;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (ReferenceEquals(null, obj))
|
|
return false;
|
|
if (obj.GetType() != typeof(Coordinate))
|
|
return false;
|
|
return Equals((Coordinate)obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
unchecked
|
|
{
|
|
return (Row * 397) ^ Col;
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
var s = "";
|
|
if (this == North)
|
|
s = " n";
|
|
if (this == West)
|
|
s = " w";
|
|
if (this == South)
|
|
s = " s";
|
|
if (this == East)
|
|
s = " e";
|
|
return String.Format("({0}, {1}{2})", Col, Row, s);
|
|
}
|
|
|
|
public static bool operator ==(Coordinate left, Coordinate right)
|
|
{
|
|
return left.Equals(right);
|
|
}
|
|
|
|
public static bool operator !=(Coordinate left, Coordinate right)
|
|
{
|
|
return !left.Equals(right);
|
|
}
|
|
|
|
public static Coordinate operator +(Coordinate c1, Coordinate c2)
|
|
{
|
|
return new Coordinate(c1.Col + c2.Col, c1.Row + c2.Row);
|
|
}
|
|
|
|
public static Coordinate operator -(Coordinate c1, Coordinate c2)
|
|
{
|
|
return new Coordinate(c1.Col - c2.Col, c1.Row - c2.Row);
|
|
}
|
|
|
|
public Point ToPoint(double scale)
|
|
{
|
|
return new Point(Col * scale, Row * scale);
|
|
}
|
|
|
|
private static readonly Coordinate West = new Coordinate(-1, 0);
|
|
private static readonly Coordinate South = new Coordinate(0, 1);
|
|
private static readonly Coordinate East = new Coordinate(1, 0);
|
|
private static readonly Coordinate North = new Coordinate(0, -1);
|
|
|
|
public static readonly Coordinate[] Directions = new[]
|
|
{
|
|
West,
|
|
South,
|
|
East,
|
|
North,
|
|
};
|
|
}
|
|
|
|
private struct Edge
|
|
{
|
|
public readonly Coordinate From, To;
|
|
|
|
public Edge(Coordinate from, Coordinate to)
|
|
{
|
|
From = from;
|
|
To = to;
|
|
}
|
|
|
|
public Edge(int fromCol, int fromRow, int toCol, int toRow)
|
|
: this(new Coordinate(fromCol, fromRow), new Coordinate(toCol, toRow))
|
|
{
|
|
|
|
}
|
|
|
|
|
|
public bool Equals(Edge other)
|
|
{
|
|
return other.From.Equals(From) && other.To.Equals(To);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (ReferenceEquals(null, obj))
|
|
return false;
|
|
if (obj.GetType() != typeof(Edge))
|
|
return false;
|
|
return Equals((Edge)obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
unchecked
|
|
{
|
|
return (From.GetHashCode() * 397) ^ To.GetHashCode();
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
char angle = ' ';
|
|
if (From.Col == To.Col)
|
|
{
|
|
angle = '|';
|
|
}
|
|
if (From.Row == To.Row)
|
|
{
|
|
angle = '-';
|
|
}
|
|
|
|
|
|
return string.Format("[{0} {2} {1}]", From, To, angle);
|
|
}
|
|
|
|
public static bool operator ==(Edge left, Edge right)
|
|
{
|
|
return left.Equals(right);
|
|
}
|
|
|
|
public static bool operator !=(Edge left, Edge right)
|
|
{
|
|
return !left.Equals(right);
|
|
}
|
|
|
|
public Edge Reversed()
|
|
{
|
|
return new Edge(To, From);
|
|
}
|
|
}
|
|
}
|
|
}
|