Я рисую ноты в приложении avalonia на основе SVG-файла, вот так:
internal class VerovioSvgRenderer
{
private Dictionary _globalDefs = new();
private DrawingGroup _drawings = new();
private Canvas _notes = new();
private List _renderDom = new();
public double Height { get; private set; }
public double Width { get; private set; }
public List Load(string svg)
{
SvgDocument svgDoc = SvgDocument.FromSvg(svg);
Height = svgDoc.ViewBox.Height;
Width = svgDoc.ViewBox.Width;
foreach (SvgElement elem in svgDoc.Children)
{
if (elem is SvgDefinitionList defs)
{
HandlDefs(defs);
}
else if (elem is SvgDescription) { }
else if (elem is SvgUnknownElement unknown)
{
}
else if (elem is SvgFragment frag)
{
HandleSvgFragment(frag);
}
else throw new UnhandledElementException(elem);
}
return _renderDom;
}
private void HandleSvgFragment(SvgFragment frag)
{
var fragmentContainer = new LogicalSvgGroup();
if (frag.ViewBox != SvgViewBox.Empty)
{
double scaleX = Width / frag.ViewBox.Width;
double scaleY = Height / frag.ViewBox.Height;
var containerTransform = new TransformGroup();
containerTransform.Children.Add(new ScaleTransform(scaleX, scaleY));
fragmentContainer.RenderTransform = containerTransform;
}
foreach (var elem in frag.Children)
{
if (elem is SvgGroup g)
{
RenderGroup(g);
}
else throw new UnhandledElementException(elem);
}
var image = new Image()
{
Source = new DrawingImage(_drawings),
Width = frag.ViewBox.Width,
Height = frag.ViewBox.Height,
Stretch = Stretch.None,
};
fragmentContainer.Children.Add(_notes);
fragmentContainer.Children.Add(image);
_renderDom.Add(fragmentContainer);
}
private void RenderGroup(SvgGroup g)
{
foreach (var elem in g.Children)
{
if (elem is SvgGroup _g)
{
if (_g.Children.Count > 0)
{
RenderGroup(_g);
}
}
else if (elem is SvgPath _path)
{
_drawings.Children.Add(_path.ToAvaloniaDrawing());
}
else if (elem is SvgText _text) { }
else if (elem is SvgUse _use)
{
var dictEnry = _use.ReferencedElement.OriginalString.TrimStart('#');
Control? path;
if (_globalDefs.TryGetValue(dictEnry, out path) && path is Path p)
{
TransformGroup totalTransformGroup = new();
NoteLogicalSvgGroup useGroup = new() { NoteId = g.ID };
useGroup.Children.Add(p.DeepCopy());
var originalPathTransform = p.RenderTransform;
//apply the original transformation first
if (originalPathTransform is not null)
{
totalTransformGroup.Children.Add((Transform)originalPathTransform);
}
foreach (var t in _use.ToAvaloniaTransformGroup().Children)
{
totalTransformGroup.Children.Add(t);
}
useGroup.RenderTransform = totalTransformGroup;
_notes.Children.Add(useGroup);
}
}
else if (elem is SvgPolygon _polygon)
{
_drawings.Children.Add(_polygon.ToAvaloniaDrawing());
}
else if (elem is SvgPolyline _polyline)
{
_drawings.Children.Add(_polyline.ToAvaloniaDrawing());
}
else if (elem is SvgEllipse _ellipse)
{
_drawings.Children.Add(_ellipse.ToAvaloniaDrawing());
}
else if (elem is SvgRectangle _rect)
{
_drawings.Children.Add(_rect.ToAvaloniaDrawing());
}
else throw new UnhandledElementException(elem);
}
}
private void HandlDefs(SvgDefinitionList defs)
{
foreach (var elem in defs.Children)
{
if (elem is SvgGroup g)
{
var groupId = g.ID;
foreach (var path in g.Children)
{
if (path is SvgPath p)
{
_globalDefs[groupId] = p.ToAvaloniaPath();
}
else throw new UnhandledElementException(elem);
}
}
else throw new UnhandledElementException(elem);
}
}
}
public class LogicalSvgGroup : Canvas
{
public List Class { get; set; } = new();
public string? Id { get; set; }
}
[PseudoClasses(":highlighted")]
public class NoteLogicalSvgGroup : LogicalSvgGroup
{
public string? NoteId { get; set; }
public void SetHighlighted(bool highlighted)
{
PseudoClasses.Set(":highlighted", highlighted);
}
}
Все остальные элементы добавляются к рисунку, а примечания — это фактические элементы управления, которые мы добавляем на холст, чтобы взаимодействовать с ним позже. Независимо от того, что я пробовал, я не могу заставить элементы управления заметками правильно совпадать с изображением рисунка. Все отображается правильно, но элементы управления нотами лежат не там, где должны. Прежде чем перейти к подходу с рисованием изображения, раньше я также визуализировал все элементы в элементах управления, но это было неэффективно с точки зрения производительности, хотя все отображалось и было правильно размещено. Это соответствующие методы расширения:
public static class SvgExtensions
{
public static List GetClasses(this SvgElement element)
{
if (element.CustomAttributes.TryGetValue("class", out var cls))
return cls.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
return new List();
}
public static TransformGroup ToAvaloniaTransformGroup(this SvgElement self)
{
var transformGroup = new TransformGroup();
if (self.Transforms is not null)
{
//create transform group in reverse order because of how svgs work
for (int i = self.Transforms.Count - 1; i >= 0; i--)
{
var transform = self.Transforms;
if (transform is SvgTranslate t)
{
transformGroup.Children.Add(new TranslateTransform(t.X, t.Y));
}
else if (transform is SvgScale s)
{
transformGroup.Children.Add(new ScaleTransform(s.X, s.Y));
}
else throw new UnhandledTransformException(transform);
}
}
return transformGroup;
}
public static PenLineJoin ToAvaloniaPenLineJoin(this SvgStrokeLineJoin self)
{
return self switch
{
SvgStrokeLineJoin.Miter => PenLineJoin.Miter,
SvgStrokeLineJoin.Round => PenLineJoin.Round,
SvgStrokeLineJoin.Bevel => PenLineJoin.Bevel,
_ => PenLineJoin.Miter
};
}
public static PenLineCap ToAvaloniaPenLineCap(this SvgStrokeLineCap self)
{
return self switch
{
SvgStrokeLineCap.Butt => PenLineCap.Flat,
SvgStrokeLineCap.Round => PenLineCap.Round,
SvgStrokeLineCap.Square => PenLineCap.Square,
_ => PenLineCap.Flat
};
}
public static Avalonia.Controls.Shapes.Path ToAvaloniaPath(this SvgPath self)
{
var path = new Avalonia.Controls.Shapes.Path();
path.RenderTransform = self.ToAvaloniaTransformGroup();
string pathData = self.PathData?.ToString() ?? string.Empty;
var geometry = Geometry.Parse(pathData);
path.Stretch = Stretch.None;
path.Data = geometry;
path.StrokeLineCap = self.StrokeLineCap.ToAvaloniaPenLineCap();
path.StrokeJoin = self.StrokeLineJoin.ToAvaloniaPenLineJoin();
path.StrokeThickness = (double)self.StrokeWidth.Value;
return path;
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgPath self)
{
string pathData = self.PathData?.ToString() ?? string.Empty;
var geometry = Geometry.Parse(pathData);
var drawing = new GeometryDrawing
{
Geometry = geometry,
Brush = Brushes.Black,
Pen = new Pen
{
Brush = Brushes.Black,
Thickness = (double)self.StrokeWidth.Value,
LineCap = self.StrokeLineCap.ToAvaloniaPenLineCap(),
LineJoin = self.StrokeLineJoin.ToAvaloniaPenLineJoin()
}
};
return drawing;
}
public static Avalonia.Controls.Shapes.Path DeepCopy(this Path self)
{
return new()
{
Data = self.Data?.Clone(),
// Stroke = self.Stroke,
// Fill = self.Fill,
StrokeThickness = self.StrokeThickness,
StrokeLineCap = self.StrokeLineCap,
StrokeJoin = self.StrokeJoin,
Stretch = self.Stretch
};
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgEllipse self)
{
var ellipseGeometry = new EllipseGeometry(
new Rect(
self.CenterX - self.RadiusX,
self.CenterY - self.RadiusY,
self.RadiusX * 2,
self.RadiusY * 2
)
);
var drawing = new GeometryDrawing
{
Geometry = ellipseGeometry,
Brush = Brushes.Black,
Pen = new Pen
{
Brush = Brushes.Black,
Thickness = 1.0
}
};
return drawing;
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgPolygon self)
{
var points = new List();
for (int i = 0; i < self.Points.Count - 1; i += 2)
{
double x = (double)self.Points.Value;
double y = (double)self.Points[i + 1].Value;
points.Add(new Point(x, y));
}
var polylineGeometry = new PolylineGeometry(points, true);
var drawing = new GeometryDrawing
{
Geometry = polylineGeometry,
Brush = Brushes.Black,
Pen = new Pen
{
Brush = Brushes.Black,
Thickness = 1.0
}
};
return drawing;
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgPolyline self)
{
var points = new List();
for (int i = 0; i < self.Points.Count - 1; i += 2)
{
double x = (double)self.Points.Value;
double y = (double)self.Points[i + 1].Value;
points.Add(new Point(x, y));
}
var polylineGeometry = new PolylineGeometry(points, false);
var drawing = new GeometryDrawing
{
Geometry = polylineGeometry,
Brush = Brushes.Transparent,
Pen = new Pen
{
Brush = new SolidColorBrush(Colors.Black, self.StrokeOpacity),
Thickness = (double)self.StrokeWidth,
LineCap = self.StrokeLineCap.ToAvaloniaPenLineCap(),
LineJoin = self.StrokeLineJoin.ToAvaloniaPenLineJoin()
}
};
return drawing;
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgRectangle self)
{
var rectGeometry = new RectangleGeometry(
new Rect(
(double)self.X.Value,
(double)self.Y.Value,
(double)self.Width.Value,
(double)self.Height.Value
),
(double)self.CornerRadiusX.Value,
(double)self.CornerRadiusY.Value
);
var drawing = new GeometryDrawing
{
Geometry = rectGeometry,
Brush = Brushes.Transparent,
Pen = new Pen
{
Brush = new SolidColorBrush(Colors.Black, self.StrokeOpacity),
Thickness = (double)self.StrokeWidth,
}
};
return drawing;
}
}
Подробнее здесь: https://stackoverflow.com/questions/797 ... wing-image
Правильный способ выровнять это изображение рисунка авалонии: ⇐ C#
Место общения программистов C#
-
Anonymous
1761076748
Anonymous
Я рисую ноты в приложении avalonia на основе SVG-файла, вот так:
internal class VerovioSvgRenderer
{
private Dictionary _globalDefs = new();
private DrawingGroup _drawings = new();
private Canvas _notes = new();
private List _renderDom = new();
public double Height { get; private set; }
public double Width { get; private set; }
public List Load(string svg)
{
SvgDocument svgDoc = SvgDocument.FromSvg(svg);
Height = svgDoc.ViewBox.Height;
Width = svgDoc.ViewBox.Width;
foreach (SvgElement elem in svgDoc.Children)
{
if (elem is SvgDefinitionList defs)
{
HandlDefs(defs);
}
else if (elem is SvgDescription) { }
else if (elem is SvgUnknownElement unknown)
{
}
else if (elem is SvgFragment frag)
{
HandleSvgFragment(frag);
}
else throw new UnhandledElementException(elem);
}
return _renderDom;
}
private void HandleSvgFragment(SvgFragment frag)
{
var fragmentContainer = new LogicalSvgGroup();
if (frag.ViewBox != SvgViewBox.Empty)
{
double scaleX = Width / frag.ViewBox.Width;
double scaleY = Height / frag.ViewBox.Height;
var containerTransform = new TransformGroup();
containerTransform.Children.Add(new ScaleTransform(scaleX, scaleY));
fragmentContainer.RenderTransform = containerTransform;
}
foreach (var elem in frag.Children)
{
if (elem is SvgGroup g)
{
RenderGroup(g);
}
else throw new UnhandledElementException(elem);
}
var image = new Image()
{
Source = new DrawingImage(_drawings),
Width = frag.ViewBox.Width,
Height = frag.ViewBox.Height,
Stretch = Stretch.None,
};
fragmentContainer.Children.Add(_notes);
fragmentContainer.Children.Add(image);
_renderDom.Add(fragmentContainer);
}
private void RenderGroup(SvgGroup g)
{
foreach (var elem in g.Children)
{
if (elem is SvgGroup _g)
{
if (_g.Children.Count > 0)
{
RenderGroup(_g);
}
}
else if (elem is SvgPath _path)
{
_drawings.Children.Add(_path.ToAvaloniaDrawing());
}
else if (elem is SvgText _text) { }
else if (elem is SvgUse _use)
{
var dictEnry = _use.ReferencedElement.OriginalString.TrimStart('#');
Control? path;
if (_globalDefs.TryGetValue(dictEnry, out path) && path is Path p)
{
TransformGroup totalTransformGroup = new();
NoteLogicalSvgGroup useGroup = new() { NoteId = g.ID };
useGroup.Children.Add(p.DeepCopy());
var originalPathTransform = p.RenderTransform;
//apply the original transformation first
if (originalPathTransform is not null)
{
totalTransformGroup.Children.Add((Transform)originalPathTransform);
}
foreach (var t in _use.ToAvaloniaTransformGroup().Children)
{
totalTransformGroup.Children.Add(t);
}
useGroup.RenderTransform = totalTransformGroup;
_notes.Children.Add(useGroup);
}
}
else if (elem is SvgPolygon _polygon)
{
_drawings.Children.Add(_polygon.ToAvaloniaDrawing());
}
else if (elem is SvgPolyline _polyline)
{
_drawings.Children.Add(_polyline.ToAvaloniaDrawing());
}
else if (elem is SvgEllipse _ellipse)
{
_drawings.Children.Add(_ellipse.ToAvaloniaDrawing());
}
else if (elem is SvgRectangle _rect)
{
_drawings.Children.Add(_rect.ToAvaloniaDrawing());
}
else throw new UnhandledElementException(elem);
}
}
private void HandlDefs(SvgDefinitionList defs)
{
foreach (var elem in defs.Children)
{
if (elem is SvgGroup g)
{
var groupId = g.ID;
foreach (var path in g.Children)
{
if (path is SvgPath p)
{
_globalDefs[groupId] = p.ToAvaloniaPath();
}
else throw new UnhandledElementException(elem);
}
}
else throw new UnhandledElementException(elem);
}
}
}
public class LogicalSvgGroup : Canvas
{
public List Class { get; set; } = new();
public string? Id { get; set; }
}
[PseudoClasses(":highlighted")]
public class NoteLogicalSvgGroup : LogicalSvgGroup
{
public string? NoteId { get; set; }
public void SetHighlighted(bool highlighted)
{
PseudoClasses.Set(":highlighted", highlighted);
}
}
Все остальные элементы добавляются к рисунку, а примечания — это фактические элементы управления, которые мы добавляем на холст, чтобы взаимодействовать с ним позже. Независимо от того, что я пробовал, я не могу заставить элементы управления заметками правильно совпадать с изображением рисунка. Все отображается правильно, но элементы управления нотами лежат не там, где должны. Прежде чем перейти к подходу с рисованием изображения, раньше я также визуализировал все элементы в элементах управления, но это было неэффективно с точки зрения производительности, хотя все отображалось и было правильно размещено. Это соответствующие методы расширения:
public static class SvgExtensions
{
public static List GetClasses(this SvgElement element)
{
if (element.CustomAttributes.TryGetValue("class", out var cls))
return cls.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
return new List();
}
public static TransformGroup ToAvaloniaTransformGroup(this SvgElement self)
{
var transformGroup = new TransformGroup();
if (self.Transforms is not null)
{
//create transform group in reverse order because of how svgs work
for (int i = self.Transforms.Count - 1; i >= 0; i--)
{
var transform = self.Transforms[i];
if (transform is SvgTranslate t)
{
transformGroup.Children.Add(new TranslateTransform(t.X, t.Y));
}
else if (transform is SvgScale s)
{
transformGroup.Children.Add(new ScaleTransform(s.X, s.Y));
}
else throw new UnhandledTransformException(transform);
}
}
return transformGroup;
}
public static PenLineJoin ToAvaloniaPenLineJoin(this SvgStrokeLineJoin self)
{
return self switch
{
SvgStrokeLineJoin.Miter => PenLineJoin.Miter,
SvgStrokeLineJoin.Round => PenLineJoin.Round,
SvgStrokeLineJoin.Bevel => PenLineJoin.Bevel,
_ => PenLineJoin.Miter
};
}
public static PenLineCap ToAvaloniaPenLineCap(this SvgStrokeLineCap self)
{
return self switch
{
SvgStrokeLineCap.Butt => PenLineCap.Flat,
SvgStrokeLineCap.Round => PenLineCap.Round,
SvgStrokeLineCap.Square => PenLineCap.Square,
_ => PenLineCap.Flat
};
}
public static Avalonia.Controls.Shapes.Path ToAvaloniaPath(this SvgPath self)
{
var path = new Avalonia.Controls.Shapes.Path();
path.RenderTransform = self.ToAvaloniaTransformGroup();
string pathData = self.PathData?.ToString() ?? string.Empty;
var geometry = Geometry.Parse(pathData);
path.Stretch = Stretch.None;
path.Data = geometry;
path.StrokeLineCap = self.StrokeLineCap.ToAvaloniaPenLineCap();
path.StrokeJoin = self.StrokeLineJoin.ToAvaloniaPenLineJoin();
path.StrokeThickness = (double)self.StrokeWidth.Value;
return path;
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgPath self)
{
string pathData = self.PathData?.ToString() ?? string.Empty;
var geometry = Geometry.Parse(pathData);
var drawing = new GeometryDrawing
{
Geometry = geometry,
Brush = Brushes.Black,
Pen = new Pen
{
Brush = Brushes.Black,
Thickness = (double)self.StrokeWidth.Value,
LineCap = self.StrokeLineCap.ToAvaloniaPenLineCap(),
LineJoin = self.StrokeLineJoin.ToAvaloniaPenLineJoin()
}
};
return drawing;
}
public static Avalonia.Controls.Shapes.Path DeepCopy(this Path self)
{
return new()
{
Data = self.Data?.Clone(),
// Stroke = self.Stroke,
// Fill = self.Fill,
StrokeThickness = self.StrokeThickness,
StrokeLineCap = self.StrokeLineCap,
StrokeJoin = self.StrokeJoin,
Stretch = self.Stretch
};
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgEllipse self)
{
var ellipseGeometry = new EllipseGeometry(
new Rect(
self.CenterX - self.RadiusX,
self.CenterY - self.RadiusY,
self.RadiusX * 2,
self.RadiusY * 2
)
);
var drawing = new GeometryDrawing
{
Geometry = ellipseGeometry,
Brush = Brushes.Black,
Pen = new Pen
{
Brush = Brushes.Black,
Thickness = 1.0
}
};
return drawing;
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgPolygon self)
{
var points = new List();
for (int i = 0; i < self.Points.Count - 1; i += 2)
{
double x = (double)self.Points[i].Value;
double y = (double)self.Points[i + 1].Value;
points.Add(new Point(x, y));
}
var polylineGeometry = new PolylineGeometry(points, true);
var drawing = new GeometryDrawing
{
Geometry = polylineGeometry,
Brush = Brushes.Black,
Pen = new Pen
{
Brush = Brushes.Black,
Thickness = 1.0
}
};
return drawing;
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgPolyline self)
{
var points = new List();
for (int i = 0; i < self.Points.Count - 1; i += 2)
{
double x = (double)self.Points[i].Value;
double y = (double)self.Points[i + 1].Value;
points.Add(new Point(x, y));
}
var polylineGeometry = new PolylineGeometry(points, false);
var drawing = new GeometryDrawing
{
Geometry = polylineGeometry,
Brush = Brushes.Transparent,
Pen = new Pen
{
Brush = new SolidColorBrush(Colors.Black, self.StrokeOpacity),
Thickness = (double)self.StrokeWidth,
LineCap = self.StrokeLineCap.ToAvaloniaPenLineCap(),
LineJoin = self.StrokeLineJoin.ToAvaloniaPenLineJoin()
}
};
return drawing;
}
public static Avalonia.Media.GeometryDrawing ToAvaloniaDrawing(this SvgRectangle self)
{
var rectGeometry = new RectangleGeometry(
new Rect(
(double)self.X.Value,
(double)self.Y.Value,
(double)self.Width.Value,
(double)self.Height.Value
),
(double)self.CornerRadiusX.Value,
(double)self.CornerRadiusY.Value
);
var drawing = new GeometryDrawing
{
Geometry = rectGeometry,
Brush = Brushes.Transparent,
Pen = new Pen
{
Brush = new SolidColorBrush(Colors.Black, self.StrokeOpacity),
Thickness = (double)self.StrokeWidth,
}
};
return drawing;
}
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79796192/proper-way-to-align-this-avalonia-drawing-image[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия