-
Notifications
You must be signed in to change notification settings - Fork 68
/
Copy pathMathPainter.cs
170 lines (152 loc) · 6.87 KB
/
MathPainter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
using System;
using System.Drawing;
using CSharpMath.Display;
using CSharpMath.Enumerations;
using CSharpMath.Rendering;
using CSharpMath.Interfaces;
using TFonts = CSharpMath.Rendering.MathFonts;
using CSharpMath.FrontEnd;
using CSharpMath.Structures;
using Typography.OpenFont;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace CSharpMath.Rendering {
public abstract class MathPainter<TCanvas, TColor> : ICanvasPainter<TCanvas, MathSource, TColor> {
#region Constructors
public MathPainter(float fontSize = 20f) {
FontSize = fontSize;
LocalTypefaces.CollectionChanged += CollectionChanged;
ErrorColor = UnwrapColor(new Color(255, 0, 0));
TextColor = UnwrapColor(new Color(0, 0, 0));
BackgroundColor = UnwrapColor(new Color(0, 0, 0, 0));
}
void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => Redisplay(0);
#endregion Constructors
#region Fields
//_field == private field, __field == property-only field
protected bool _displayChanged = true;
protected MathListDisplay<TFonts, Glyph> _displayList;
protected readonly GraphicsContext _context = new GraphicsContext();
#endregion Fields
#region Non-redisplaying properties
/// <summary>
/// Unit of measure: points;
/// Defaults to <see cref="FontSize"/>.
/// </summary>
public float? ErrorFontSize { get; set; }
public bool DisplayErrorInline { get; set; } = true;
public TColor ErrorColor { get; set; }
public TColor TextColor { get; set; }
public TColor BackgroundColor { get; set; }
public (TColor glyph, TColor textRun)? GlyphBoxColor { get; set; }
public PaintStyle PaintStyle { get; set; } = PaintStyle.Fill;
public float Magnification { get; set; } = 1;
public RectangleF? Measure {
get {
if (MathList != null && _displayList == null) UpdateDisplay();
return _displayList?.ComputeDisplayBounds();
}
}
public string ErrorMessage => Source.Error;
#endregion Non-redisplaying properties
#region Redisplaying properties
protected void Redisplay<T>(T assignment) => _displayChanged = true;
/// <summary>
/// Unit of measure: points
/// </summary>
public float FontSize { get => __size; set => Redisplay(__size = value); } float __size = 20f;
public ObservableCollection<Typeface> LocalTypefaces { get; } = new ObservableCollection<Typeface>();
LineStyle __style = LineStyle.Display; public LineStyle LineStyle { get => __style; set => Redisplay(__style = value); }
MathSource __source = new MathSource(); public MathSource Source { get => __source; set => Redisplay(__source = value); }
public IMathList MathList { get => Source.MathList; set => Source = new MathSource(value); }
public string LaTeX { get => Source.LaTeX; set => Source = new MathSource(value); }
#endregion Redisplaying properties
#region Methods
private static PointF GetDisplayPosition(MathListDisplay<TFonts, Glyph> displayList,
float fontSize, bool bottomLeftCoords,
float width, float height,
TextAlignment alignment, Thickness padding, float offsetX, float offsetY) {
float x, y;
float displayWidth = displayList.Width;
if ((alignment & TextAlignment.Left) != 0)
x = padding.Left;
else if ((alignment & TextAlignment.Right) != 0)
x = width - padding.Right - displayWidth;
else
x = padding.Left + (width - padding.Left - padding.Right - displayWidth) / 2;
float contentHeight = displayList.Ascent + displayList.Descent;
if (contentHeight < fontSize / 2) {
contentHeight = fontSize / 2;
}
if (!bottomLeftCoords) {
//Canvas is inverted!
if ((alignment & (TextAlignment.Top | TextAlignment.Bottom)) != 0) {
alignment ^= TextAlignment.Top;
alignment ^= TextAlignment.Bottom;
}
//invert y-coordinate as canvas is inverted
offsetY *= -1;
}
if ((alignment & TextAlignment.Top) != 0)
y = padding.Top + displayList.Descent;
else if ((alignment & TextAlignment.Bottom) != 0)
y = height - padding.Bottom - displayList.Ascent;
else {
float availableHeight = height - padding.Top - padding.Bottom;
y = ((availableHeight - contentHeight) / 2) + padding.Top + displayList.Descent;
}
return new PointF(x + offsetX, (y + offsetY) - (bottomLeftCoords ? 0 : height));
}
public void UpdateDisplay() {
var fonts = new TFonts(LocalTypefaces, FontSize);
_displayList = TypesettingContext.Instance.CreateLine(MathList, fonts, LineStyle);
_displayChanged = false;
}
public abstract Color WrapColor(TColor color);
public abstract TColor UnwrapColor(Color color);
public abstract ICanvas WrapCanvas(TCanvas canvas);
protected abstract bool CoordinatesFromBottomLeftInsteadOfTopLeft { get; }
public void Draw(TCanvas canvas, TextAlignment alignment = TextAlignment.Center, Thickness padding = default, float offsetX = 0, float offsetY = 0) {
if (MathList == null) return;
if (_displayChanged) UpdateDisplay();
var c = WrapCanvas(canvas);
Draw(c, GetDisplayPosition(_displayList, FontSize, CoordinatesFromBottomLeftInsteadOfTopLeft, c.Width, c.Height, alignment, padding, offsetX, offsetY));
}
public void Draw(TCanvas canvas, float x, float y) =>
Draw(WrapCanvas(canvas), new PointF(x, CoordinatesFromBottomLeftInsteadOfTopLeft ? y : -y));
public void Draw(TCanvas canvas, PointF position) {
if (CoordinatesFromBottomLeftInsteadOfTopLeft) position.Y *= -1;
Draw(WrapCanvas(canvas), position);
}
private void Draw(ICanvas canvas, PointF position) {
if (MathList != null) {
canvas.Save();
if (!CoordinatesFromBottomLeftInsteadOfTopLeft) {
//invert the canvas vertically
canvas.Scale(1, -1);
}
canvas.Scale(Magnification, Magnification);
canvas.DefaultColor = WrapColor(TextColor);
canvas.CurrentColor = WrapColor(BackgroundColor);
canvas.FillColor();
canvas.CurrentStyle = PaintStyle;
if (_displayChanged) UpdateDisplay();
_displayList.Position = position;
_context.Canvas = canvas;
T? Nullable<T>(T nonnull) where T : struct => new T?(nonnull);
_context.GlyphBoxColor = GlyphBoxColor.HasValue ? Nullable((WrapColor(GlyphBoxColor.Value.glyph), WrapColor(GlyphBoxColor.Value.textRun))) : null;
_displayList.Draw(_context);
canvas.Restore();
} else if (DisplayErrorInline && ErrorMessage.IsNonEmpty()) {
canvas.Save();
canvas.CurrentColor = WrapColor(BackgroundColor);
canvas.FillColor();
var size = ErrorFontSize ?? FontSize;
canvas.CurrentColor = WrapColor(ErrorColor);
canvas.FillText(ErrorMessage, 0, size, size);
canvas.Restore();
}
}
#endregion Methods
}
}