using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; namespace _3dDraw { /// <summary> /// 複数の数式と媒介変数の範囲を保持するためのパラメータクラス /// </summary> public class EquationParameter { public string ExprX { get; set; } public string ExprY { get; set; } public string ExprZ { get; set; } public int TStart { get; set; } public int TEnd { get; set; } // コンストラクタで簡単に初期化できるようにする public EquationParameter(string exprX, string exprY, string exprZ, int tStart, int tEnd) { ExprX = exprX; ExprY = exprY; ExprZ = exprZ; TStart = tStart; TEnd = tEnd; } } /// <summary> /// 数式テキストを解析して3次元座標を自動生成し、描画を支援するクラス。 /// CoordinateConverter を継承しています。 /// </summary> public class ParametricEquationGenerator : CoordinateConverter { /// <summary> /// X, Y, Z軸の数式と媒介変数 t の範囲を指定して、ポイントデータを生成し Points3D にセットします。 /// </summary> /// <param name="exprX">X軸の数式 (例: "const[20]+cos{param[] * const[3]}")</param> /// <param name="exprY">Y軸の数式</param> /// <param name="exprZ">Z軸の数式</param> /// <param name="tStart">媒介変数 t の開始値</param> /// <param name="tEnd">媒介変数 t の終了値</param> public void GenerateAndSetPoints(string exprX, string exprY, string exprZ, int tStart, int tEnd) { // 単一のデータを生成して配列にセット this.Points3D = new double[][,] { GeneratePointsSingle(exprX, exprY, exprZ, tStart, tEnd) }; } /// <summary> /// 複数の数式セットを同時に渡して、複数のポイントデータを生成し Points3D にセットします。 /// </summary> /// <param name="parameters">生成する式のパラメータの可変長配列</param> public void GenerateAndSetPoints(params EquationParameter[] parameters) { if (parameters == null || parameters.Length == 0) return; List<double[,]> pointsList = new List<double[,]>(); foreach (var param in parameters) { // 各パラメータごとに座標配列を生成してリストに追加 pointsList.Add(GeneratePointsSingle(param.ExprX, param.ExprY, param.ExprZ, param.TStart, param.TEnd)); } // 親クラスの配列プロパティに一括セット this.Points3D = pointsList.ToArray(); } /// <summary> /// 単一の数式セットから座標配列を生成する内部メソッド /// </summary> private double[,] GeneratePointsSingle(string exprX, string exprY, string exprZ, int tStart, int tEnd) { int count = tEnd - tStart + 1; if (count <= 0) { throw new ArgumentException("tEnd は tStart 以上である必要があります。"); } double[,] generatedPoints = new double[count, 3]; for (int i = 0; i < count; i++) { double t = tStart + i; // 媒介変数を1ずつ変化させる // それぞれの軸の数式を評価して座標を計算 generatedPoints[i, 0] = EvaluateExpression(exprX, t); generatedPoints[i, 1] = EvaluateExpression(exprY, t); generatedPoints[i, 2] = EvaluateExpression(exprZ, t); } return generatedPoints; } /// <summary> /// 独自の数式ルールを解析して計算結果を返します。 /// </summary> private double EvaluateExpression(string expression, double t) { // 空白を削除して処理しやすくする string expr = expression.Replace(" ", ""); // 1. 媒介変数 param[] を t の値に置換 expr = expr.Replace("param[]", t.ToString("F6")); // 2. 定数 const[値] を実際の数値に置換 // 例: const[20] -> 20, const[-3.5] -> -3.5 expr = Regex.Replace(expr, @"const\[(-?[0-9.]+)\]", "$1"); // 3. 三角関数 sin{}, cos{}, tan{} の処理 (ネストにも対応するため内側から評価) var regexTrig = new Regex(@"(sin|cos|tan)\{([^\{\}]+)\}"); while (regexTrig.IsMatch(expr)) { expr = regexTrig.Replace(expr, match => { string func = match.Groups[1].Value.ToLower(); string innerExpr = match.Groups[2].Value; // { } の中身の数式 // 中身の数式を四則演算で計算 double innerValue = CalculateBasicMath(innerExpr); // Degree(度)からRadian(ラジアン)に変換して三角関数を適用 double rad = innerValue * Math.PI / 180.0; double result = 0; if (func == "sin") result = Math.Sin(rad); else if (func == "cos") result = Math.Cos(rad); else if (func == "tan") result = Math.Tan(rad); return result.ToString("F6"); // 計算結果の数値文字列で置き換え }); } // 4. 三角関数などの処理が終わった後、残った四則演算を計算して返す return CalculateBasicMath(expr); } /// <summary> /// 文字列の四則演算 (+, -, *, /) を評価して計算します。 /// C#標準の DataTable.Compute を利用して解析の手間を省いています。 /// </summary> private double CalculateBasicMath(string expr) { // 計算機の構文エラーを防ぐため、符号の連続を整理 (例: 20+-5 -> 20-5) expr = expr.Replace("+-", "-").Replace("-+", "-").Replace("--", "+").Replace("++", "+"); try { using (var dt = new DataTable()) { // DataTable.Compute は文字列の数式(例: "1+2*3")を自動で計算してくれます var result = dt.Compute(expr, ""); // 名前衝突を避けるため System.Convert を明示的に指定 return System.Convert.ToDouble(result); } } catch (Exception ex) { throw new Exception($"数式の四則演算に失敗しました。数式: '{expr}'", ex); } } } }