/
docNemo
/
clothes-graph
Обзор
Документация
Войти
/
docNemo
/
clothes-graph
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
src/ClothesGraph.Model/Formulas/Formula.cs
127 строк
5 KB
docNemo
Фундамент продукта: параметрическое построение выкроек, печать, установщик
08 авг 2026, 22:17
08 авг 2026, 22:17
cd47cce
Код
Авторство
О чём код?
using System; using System.Collections.Generic; using System.Linq; using ClothesGraph.Model.Drafting; using ClothesGraph.Model.Measurements; namespace ClothesGraph.Model.Formulas; /// <summary> /// Разобранная формула построения. /// </summary> /// <remarks> /// Хранит дерево выражения, а не исходный текст. Текст для показа собирается /// заново по текущим именам, поэтому переименование мерки или построения /// отражается в формуле само собой и ничего не ломает. /// </remarks> public sealed class Formula { private Formula(Expression root) { Root = root; var measurements = new HashSet<MeasurementId>(); var nodes = new HashSet<NodeId>(); Collect(root, measurements, nodes); Measurements = measurements; Nodes = nodes; } public Expression Root { get; } /// <summary>Мерки, от которых зависит формула.</summary> public IReadOnlyCollection<MeasurementId> Measurements { get; } /// <summary>Построения, от которых зависит формула. Питают граф зависимостей.</summary> public IReadOnlyCollection<NodeId> Nodes { get; } public static Formula Parse(string text, IFormulaScope scope) => new(FormulaParser.Parse(text, scope)); /// <summary>Постоянная величина без ссылок на что-либо.</summary> public static Formula Constant(double value) => new(new NumberExpression(value)); /// <summary> /// Формула из готового дерева. Нужна при чтении файла: там дерево /// восстанавливается из записи, а не разбирается из текста. /// </summary> internal static Formula FromExpression(Expression root) => new(root); public double Evaluate(IFormulaValues values) => Evaluate(Root, values); public string ToText(IFormulaScope scope) => FormulaWriter.Write(Root, scope); private static double Evaluate(Expression expression, IFormulaValues values) => expression switch { NumberExpression number => number.Value, MeasurementExpression measurement => values.GetMeasurement(measurement.Measurement), PropertyExpression property => values.GetNodeProperty(property.Node, property.Property), NegateExpression negate => -Evaluate(negate.Operand, values), BinaryExpression binary => EvaluateBinary(binary, values), CallExpression call => FormulaFunctions.Apply( call.Function, call.Arguments.Select(argument => Evaluate(argument, values)).ToArray()), _ => throw new FormulaEvaluationException($"Неизвестный узел формулы {expression.GetType().Name}") }; private static double EvaluateBinary(BinaryExpression binary, IFormulaValues values) { var left = Evaluate(binary.Left, values); var right = Evaluate(binary.Right, values); switch (binary.Operator) { case BinaryOperator.Add: return left + right; case BinaryOperator.Subtract: return left - right; case BinaryOperator.Multiply: return left * right; case BinaryOperator.Divide: // Деление на ноль не превращается в бесконечность: дальше она // разошлась бы по всему чертежу и причину пришлось бы искать // далеко от места ошибки. if (Math.Abs(right) < 1e-12) throw new FormulaEvaluationException("Деление на ноль"); return left / right; case BinaryOperator.Power: return Math.Pow(left, right); default: throw new FormulaEvaluationException($"Неизвестная операция {binary.Operator}"); } } private static void Collect(Expression expression, HashSet<MeasurementId> measurements, HashSet<NodeId> nodes) { switch (expression) { case MeasurementExpression measurement: measurements.Add(measurement.Measurement); break; case PropertyExpression property: nodes.Add(property.Node); break; case NegateExpression negate: Collect(negate.Operand, measurements, nodes); break; case BinaryExpression binary: Collect(binary.Left, measurements, nodes); Collect(binary.Right, measurements, nodes); break; case CallExpression call: foreach (var argument in call.Arguments) Collect(argument, measurements, nodes); break; } } }