dhq_boiler
@dhq_boiler

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

[C#]MathNet.NumericsとMathNet.Symbolicsの使い方

解決したいこと

2つのPointによって求められる直線と、長径と短径によって求められる楕円があります。
この時、直線が楕円の接線になっているか調べたいです。
もし接線になっていたら、その接点も調べたいです。

使用するライブラリはMathNet.NumericsとMathNet.Symbolicsを検討しているのですが、Web上を検索してもなかなかヒットしません。誰か使い方がわかる方はいらっしゃいますでしょうか?
よろしければご教授下さい。サンプルコードがあると助かります。

※本件ではUnityは使いません。

自分で試したこと

とりあえず、方程式だけは書いてみました。
あと、楕円についてですが中心が原点でない場合を考える必要がありました。なので、以下のコードは半分くらい無駄になります。

MathNetTest.cs
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace boilersGraphics.Test
{
    [TestFixture]
    public class MathNetTest
    {
        [Test]
        public void Basic()
        {
            //直線の式
            var x = MathNet.Symbolics.Expression.Symbol("x");
            var y = MathNet.Symbolics.Expression.Symbol("y");
            var x1 = MathNet.Symbolics.Expression.Symbol("x1");
            var y1 = MathNet.Symbolics.Expression.Symbol("y1");
            var x2 = MathNet.Symbolics.Expression.Symbol("x2");
            var y2 = MathNet.Symbolics.Expression.Symbol("y2");
            MathNet.Symbolics.Expression left_line = MathNet.Symbolics.Infix.ParseOrThrow("y-y1");
            MathNet.Symbolics.Expression right_line = MathNet.Symbolics.Infix.ParseOrThrow("(y2-y1)/(x2-x1)*(x-x1)");

            //楕円の式
            var a = MathNet.Symbolics.Expression.Symbol("a");
            var b = MathNet.Symbolics.Expression.Symbol("b");
            MathNet.Symbolics.Expression left_ellipse = MathNet.Symbolics.Infix.ParseOrThrow("x^2/a^2+y^2/b^2");
            MathNet.Symbolics.Expression right_ellipse = MathNet.Symbolics.Infix.ParseOrThrow("1");

            //楕円の接線の方程式
            MathNet.Symbolics.Expression left_tangent = MathNet.Symbolics.Infix.ParseOrThrow("x*x1/a^2+y*y1/b^2");
            MathNet.Symbolics.Expression right_tangent = MathNet.Symbolics.Infix.ParseOrThrow("1");
        }
    }
}

中心が原点でない楕円は下記サイトが参考になりそうです。

0

1Answer

直線 y - y1 = (y2 - y1) / (x2 - x1) * (x - x1)と
楕円の接線 x * x3 / a^2 + y * y3 / b^2 = 1について
さまざまなサイトを検索して下記のコードまでたどり着きました。

ただ、このコードのRational.Simplify(variable, Algebraic.Expand(-coeff[0] / coeff[1]))という処理でとても時間がかかってしまい(10分以上)、現実的ではありません。
搭載しているCPUはAMD Ryzen 9 5950Xです。CPUパワーは十分だと思うのですが...。

なぜこれ程時間がかかるのかを解明したいです。

MathNetTest.cs
using MathNet.Symbolics;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace boilersGraphics.Test
{

    using Expr = MathNet.Symbolics.Expression;
    [TestFixture]
    public class MathNetTest
    {
        [Test]
        public void Basic()
        {
            //直線の式
            var x = MathNet.Symbolics.Expression.Symbol("x");
            var y = MathNet.Symbolics.Expression.Symbol("y");
            var x1 = MathNet.Symbolics.Expression.Symbol("x1");
            var y1 = MathNet.Symbolics.Expression.Symbol("y1");
            var x2 = MathNet.Symbolics.Expression.Symbol("x2");
            var y2 = MathNet.Symbolics.Expression.Symbol("y2");
            var x3 = MathNet.Symbolics.Expression.Symbol("x3");
            var y3 = MathNet.Symbolics.Expression.Symbol("y3");
            var a = MathNet.Symbolics.Expression.Symbol("a");
            var b = MathNet.Symbolics.Expression.Symbol("b");

            var variables2 = new Dictionary<string, FloatingPoint>
            {
                { "x1", -1 }, //直線を構成する点1
                { "y1", 3 }, //直線を構成する点1
                { "x2", 2 }, //直線を構成する点2
                { "y2", 3 }, //直線を構成する点2
                { "x3", -2 }, //曲線上の点
                { "y3", 0 }, //曲線上の点
                { "a", 2 }, //楕円の短径
                { "b", 3 }, //楕円の長径
            };

            MathNet.Symbolics.Expression left_line = y - y1;
            MathNet.Symbolics.Expression right_line = (y2 - y1) / (x2 - x1) * (x - x1);

            MathNet.Symbolics.Expression left_tangent = x * x3 / MathNet.Symbolics.Expression.Pow(a, 2) + y * y3 / MathNet.Symbolics.Expression.Pow(b, 2);
            MathNet.Symbolics.Expression right_tangent = 1;
            Expr line_x = SolveSimpleRoot(x, left_line - right_line);
            Expr tangent_x = SolveSimpleRoot(x, left_tangent - right_tangent);
            Expr cy = SolveSimpleRoot(y, line_x - tangent_x);
            Expr cx = Algebraic.Expand(Structure.Substitute(y, cy, line_x));
            Console.WriteLine(Infix.Format(cx));
            Console.WriteLine(Infix.Format(cy));
        }

        Expr SolveSimpleRoot(Expr variable, Expr expr)
        {
            // try to bring expression into polynomial form
            Expr simple = Algebraic.Expand(Rational.Numerator(Rational.Simplify(variable, expr)));

            // extract coefficients, solve known forms of order up to 1
            Expr[] coeff = Polynomial.Coefficients(variable, simple);
            switch (coeff.Length)
            {
                case 1: return Expr.Zero.Equals(coeff[0]) ? variable : Expr.Undefined;
                case 2: return Rational.Simplify(variable, Algebraic.Expand(-coeff[0] / coeff[1])); //このコードを処理するのにとても時間がかかる
                default: return Expr.Undefined;
            }
        }
    }
}

0Like

Comments

Your answer might help someone💌