/
docNemo
/
clothes-graph
Обзор
Документация
Войти
/
docNemo
/
clothes-graph
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
src/ClothesGraph.App/ViewModels/PiecesViewModel.cs
186 строк
6 KB
docNemo
Вкладка деталей выкройки, создание документа и пересчёт производных вкладок
09 авг 2026, 14:03
09 авг 2026, 14:03
dd71293
Код
Авторство
О чём код?
using System; using System.Collections.ObjectModel; using System.Linq; using ClothesGraph.Model; using ClothesGraph.Model.Drafting; using ClothesGraph.Model.Pieces; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; namespace ClothesGraph.App.ViewModels; /// <summary>Линия чертежа, пригодная для включения в контур детали.</summary> public sealed record CurveOption(NodeId Id, string Name) { public override string ToString() => Name; } /// <summary> /// Детали выкройки документа. /// </summary> /// <remarks> /// Деталь опирается на построения чертежа, поэтому пересобирается при каждом /// его пересчёте: иначе контур на экране отстал бы от конструкции, по которой /// он построен, и разошёлся бы с тем, что уйдёт на печать. /// </remarks> public sealed partial class PiecesViewModel : ObservableObject { private readonly PatternDocument _document; [ObservableProperty] private PieceEditorViewModel? _selected; [ObservableProperty] private CurveOption? _selectedCurve; [ObservableProperty] private string _summary = string.Empty; public PiecesViewModel(PatternDocument document) { _document = document; Pieces = []; Curves = []; Reload(); } public ObservableCollection<PieceEditorViewModel> Pieces { get; } /// <summary>Линии чертежа для сборки контура.</summary> public ObservableCollection<CurveOption> Curves { get; } public bool HasPieces => Pieces.Count > 0; /// <summary>Геометрия деталей пересчитана; предпросмотр нужно перерисовать.</summary> public event EventHandler? GeometryChanged; /// <summary> /// Пересобирает список деталей целиком. /// </summary> /// <remarks> /// Вызывается при появлении и удалении деталей. Правка одной детали /// проходит через <see cref="Refresh"/>, который список не трогает: /// пересоздание сбрасывало бы выбор под руками пользователя. /// </remarks> private void Reload() { var previous = Selected?.Piece.Id; Pieces.Clear(); foreach (var piece in _document.Pieces) Pieces.Add(new PieceEditorViewModel(_document, this, piece)); Selected = Pieces.FirstOrDefault(editor => editor.Piece.Id == previous) ?? Pieces.FirstOrDefault(); OnPropertyChanged(nameof(HasPieces)); Refresh(); } /// <summary>Пересчитывает чертёж и пересобирает геометрию всех деталей.</summary> public void Refresh() { RebuildCurves(); var (evaluation, values) = _document.Evaluate(); foreach (var editor in Pieces) editor.Rebuild(evaluation, values); var unsound = Pieces.Count(editor => editor.HasProblems); Summary = Pieces.Count == 0 ? "Деталей нет. Соберите первую из линий чертежа" : unsound == 0 ? $"Деталей: {Pieces.Count}, все контуры замкнуты" : $"Деталей: {Pieces.Count}, с замечаниями: {unsound}"; GeometryChanged?.Invoke(this, EventArgs.Empty); } private void RebuildCurves() { var current = _document.Draft.Nodes .OfType<CurveNode>() .Select(node => new CurveOption(node.Id, node.Name)) .ToArray(); if (Curves.SequenceEqual(current)) return; var previous = SelectedCurve?.Id; Curves.Clear(); foreach (var option in current) Curves.Add(option); SelectedCurve = Curves.FirstOrDefault(option => option.Id == previous) ?? Curves.FirstOrDefault(); } /// <summary> /// Принимает правку детали: помечает документ изменённым и пересобирает /// то, что от правки зависит. /// </summary> /// <param name="rebuild"> /// Пересчитывать ли геометрию. Имя, количество и надпись на неё не влияют, /// и пересчёт ради них перестраивал бы контуры всех деталей на каждой букве. /// </param> internal void NotifyEdited(bool rebuild = true) { _document.MarkModified(); if (rebuild) Refresh(); } /// <summary>Создаёт пустую деталь, готовую к сборке контура.</summary> [RelayCommand] private void AddPiece() { var piece = new PatternPiece(PieceId.New(), UniqueName(), _document.ParseFormula("1")); _document.Pieces.Add(piece); _document.MarkModified(); Reload(); Selected = Pieces.FirstOrDefault(editor => editor.Piece.Id == piece.Id); } /// <summary> /// Удаляет выбранную деталь вместе со швами, которые её упоминают. /// </summary> /// <remarks> /// Шов на удалённой детали не имеет смысла и при сверке приводил бы к /// отчёту о несуществующем участке. /// </remarks> [RelayCommand] private void RemovePiece() { if (Selected is not { } editor) return; _document.Seams.RemoveForPiece(editor.Piece.Id); _document.Pieces.Remove(editor.Piece); _document.MarkModified(); Reload(); } private string UniqueName() { for (var index = 1; ; index++) { var candidate = $"Деталь {index}"; if (_document.Pieces.All(piece => piece.Name != candidate)) return candidate; } } }