/
docNemo
/
clothes-graph
Обзор
Документация
Войти
/
docNemo
/
clothes-graph
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
src/ClothesGraph.Printing/PrintContent.cs
137 строк
6 KB
docNemo
Фундамент продукта: параметрическое построение выкроек, печать, установщик
08 авг 2026, 22:17
08 авг 2026, 22:17
cd47cce
Код
Авторство
О чём код?
using System; using System.Collections.Generic; using System.Linq; using ClothesGraph.Geometry; using ClothesGraph.Model.Pieces; namespace ClothesGraph.Printing; /// <summary> /// Деталь, размещённая в области печати. /// </summary> /// <remarks> /// Размещение хранится отдельно от самой детали: положение на листе — свойство /// раскладки, а не конструкции, и при смене формата бумаги меняется оно, а не /// деталь. /// </remarks> public sealed class PlacedPiece { public PlacedPiece(PatternPiece piece, PieceGeometry geometry) { Piece = piece; Geometry = geometry; } public PatternPiece Piece { get; } public PieceGeometry Geometry { get; } /// <summary>Смещение детали в области печати.</summary> public Offset2 Offset { get; set; } = new(Length.Zero, Length.Zero); /// <summary>Поворот детали в градусах против часовой стрелки.</summary> public double RotationDegrees { get; set; } /// <summary>Контур по линии шва с учётом размещения.</summary> public IReadOnlyList<Point2> PlacedOutline => Transform(Geometry.Outline); /// <summary>Линия припуска с учётом размещения.</summary> public IReadOnlyList<Point2> PlacedSeamLine => Transform(Geometry.SeamLine); public IReadOnlyList<Point2> Transform(IReadOnlyList<Point2> points) { if (points.Count == 0) return points; if (RotationDegrees == 0) return points.Select(point => point + Offset).ToArray(); var radians = RotationDegrees * Math.PI / 180.0; var cos = Math.Cos(radians); var sin = Math.Sin(radians); // Поворот выполняется вокруг центра детали, а не вокруг начала // координат: иначе деталь при повороте уезжала бы с листа. var centre = Geometry.Centre; return points.Select(point => { double x = point.X.Units - centre.X.Units; double y = point.Y.Units - centre.Y.Units; return new Point2( Length.FromUnits(centre.X.Units + (long)Math.Round(x * cos - y * sin, MidpointRounding.AwayFromZero)), Length.FromUnits(centre.Y.Units + (long)Math.Round(x * sin + y * cos, MidpointRounding.AwayFromZero))) + Offset; }).ToArray(); } public Point2 TransformPoint(Point2 point) => Transform([point])[0]; /// <summary>Прямоугольник, занимаемый деталью вместе с припуском.</summary> public (Point2 Min, Point2 Max) Bounds() { var points = PlacedSeamLine.Count > 0 ? PlacedSeamLine : PlacedOutline; if (points.Count == 0) return (Point2.Origin, Point2.Origin); return (new Point2( Length.FromUnits(points.Min(p => p.X.Units)), Length.FromUnits(points.Min(p => p.Y.Units))), new Point2( Length.FromUnits(points.Max(p => p.X.Units)), Length.FromUnits(points.Max(p => p.Y.Units)))); } } /// <summary>Что и как выводится на печать.</summary> public sealed class PrintContent { private readonly List<PlacedPiece> _pieces = []; public IReadOnlyList<PlacedPiece> Pieces => _pieces; /// <summary>Подпись документа, выводимая на каждом листе.</summary> public string? Title { get; set; } /// <summary>Название размера, для которого построены детали.</summary> public string? Size { get; set; } public void Add(PlacedPiece piece) => _pieces.Add(piece); public bool Remove(PlacedPiece piece) => _pieces.Remove(piece); /// <summary>Прямоугольник, охватывающий все размещённые детали.</summary> public (Point2 Min, Point2 Max) Bounds() { if (_pieces.Count == 0) return (Point2.Origin, Point2.Origin); var bounds = _pieces.Select(piece => piece.Bounds()).ToArray(); return (new Point2( Length.FromUnits(bounds.Min(b => b.Min.X.Units)), Length.FromUnits(bounds.Min(b => b.Min.Y.Units))), new Point2( Length.FromUnits(bounds.Max(b => b.Max.X.Units)), Length.FromUnits(bounds.Max(b => b.Max.Y.Units)))); } /// <summary> /// Детали, не помещающиеся на лист выбранного формата целиком. /// </summary> /// <remarks> /// Такая деталь не является ошибкой: она разбивается между листами и /// собирается по меткам совмещения. Но пользователя об этом стоит /// предупредить — склейка крупной детали занятие небыстрое. /// </remarks> public IReadOnlyList<PlacedPiece> PiecesLargerThanSheet(PageLayout layout) => _pieces.Where(piece => { var (min, max) = piece.Bounds(); return max.X - min.X > layout.PrintableWidth || max.Y - min.Y > layout.PrintableHeight; }).ToArray(); }