Exemple #1
0
        /// <summary>
        ///     Solves a general equation \f$a_0 + a_1 x + a_2 x^2 = 0\f$, returning all real values of
        ///		\f$x\f$ for which the equation is true using the 'abc-formula'.
        /// </summary>
        public static IEnumerable <double> Solve(double a0, double a1, double a2)
        {
            DebugUtil.AssertFinite(a0, nameof(a0));
            DebugUtil.AssertFinite(a1, nameof(a1));
            DebugUtil.AssertFinite(a2, nameof(a2));
            if (Math.Abs(a2) <= 0.005)
            {
                if (Math.Abs(a1) <= 0.005)
                {
                    // There are no roots found:
                    yield break;
                }
                else
                {
                    // There is a single root, found from solving the linear equation with a1=0:
                    yield return(-a0 / a1);
                }
                yield break;
            }

            double x1 = 0.5 * (-a1 + Math.Sqrt(a1 * a1 - 4.0 * a0 * a2)) / a2;
            double x2 = 0.5 * (-a1 - Math.Sqrt(a1 * a1 - 4.0 * a0 * a2)) / a2;

            if (Double.IsNaN(x1) == false)
            {
                yield return(x1);
            }

            if (Double.IsNaN(x2) == false)
            {
                yield return(x2);
            }
        }
Exemple #2
0
 /// <summary>
 ///     Construct a new <c>Cylinder</c> around a central curve, the <c>centerCurve</c>.
 ///     The radius at each point on the central axis is defined by a one-dimensional function <c>radius</c>.
 ///     The cylinder is cut in a pie-slice along the length of the shaft, defined by the angles
 ///     <c>startAngle</c> and <c>endAngle</c>.
 ///
 ///     The surface is parametrized with the coordinates \f$t \in [0, 1]\f$ (along the length of the shaft) and
 ///     \f$\phi \in [0, 2 \pi]\f$ (along the radial coordinate).
 ///
 ///     This gives a pie-sliced cylindrical surface that is defined only between the angles <c>startAngle</c>
 ///     and <c>endAngle</c>. For example, if <c>startAngle = 0.0f</c> and <c>endAngle = Math.PI</c>, one would
 ///     get a cylinder that is sliced in half.
 /// </summary>
 /// <param name="centerCurve">
 ///     <inheritdoc cref="Cylinder.CenterCurve"/>
 /// </param>
 /// <param name="radius">
 ///     <inheritdoc cref="Cylinder.Radius"/>
 /// </param>
 public Cylinder(Curve centerCurve, double radius)
 {
     DebugUtil.AssertFinite(radius, nameof(radius));
     this.CenterCurve = centerCurve;
     this.Radius      = new ConstantFunction <dvec2, double>(radius);
     this.StartAngle  = 0.0;
     this.EndAngle    = 2.0 * Math.PI;
 }
 /// <summary>
 ///     A quadratic function defined by \f$q(x) = a_0 + a_1 x + a_2 x^2\f$.
 /// </summary>
 public QuadraticFunction(double a0, double a1, double a2)
 {
     DebugUtil.AssertFinite(a0, nameof(a0));
     DebugUtil.AssertFinite(a1, nameof(a1));
     DebugUtil.AssertFinite(a2, nameof(a2));
     _a0 = a0;
     _a1 = a1;
     _a2 = a2;
 }
Exemple #4
0
        /// <summary>
        ///     Get the value of this function \f$q(x)\f$ at the given x-position, or the value of the
        ///     <c>derivative</c>th derivative of this function. Mathematically, this gives \f$q^{(n)}(x)\f$, where
        ///     \f$n\f$ is equal to the <c>derivative</c> parameter.
        /// </summary>
        /// <param name="x">The x-coordinate at which the function is sampled.</param>
        /// <param name="derivative">
        ///     The derivative level that must be taken of the function. If <c>derivative</c> is <c>0</c>, this means
        ///     no derivative is taken. If it has a value of <c>1</c>, the first derivative is taken, with a value of
        ///     <c>2</c> the second derivative is taken and so forth. This allows you to take any derivative level
        ///     of the function.
        /// </param>
        /// <exception cref="ArgumentOutOfRangeException">
        ///     The value that is sampled must lie between the outermost points on which the spline is defined. If
        ///     <c>x</c> is outside that domain, an <c>ArgumentOutOfRangeException</c> is thrown.
        /// </exception>
        public override double GetNthDerivativeAt(double x, uint derivative)
        {
            // The input parameter must lie between the outer points, and must not be NaN:
            if (!(x >= Points.Key[0] && x <= Points.Key[Points.Count - 1]))
            {
                throw new ArgumentOutOfRangeException(nameof(x), $"Cannot interpolate at {x}, which is outside the interval given by the spline points.");
            }

            // Find the index `i` of the closest point to the right of the input `x` parameter, which is the right point
            // used to interpolate between. Therefore, `i-1` indicates the left point of the interval.
            int i = Points.Key.BinarySearch(x);

            // BinarySearch returns a bitwise complement of the index if the point is not exactly in the list, such as
            // when interpolating. To turn it into a valid index, we take the bitwise complement again if it is negative:
            if (i < 0)
            {
                i = ~i;
            }

            // If the index is zero, we are exactly on the first point in the list. We increment by one to get the value
            // of the first spline segment, to avoid an IndexOutOfRangeException later on:
            if (i == 0)
            {
                i++;
            }

            double x1 = Points.Key[i - 1];
            double x2 = Points.Key[i];
            double y1 = Points.Value[i - 1];
            double y2 = Points.Value[i];

            // Calculate and return the interpolated value:
            double dx = x2 - x1;
            double dy = y2 - y1;

            double slope = dy / dx;

            double local_x = x - x1;

            double local_y  = local_x * slope;
            double global_y = local_y + y1;

            // Return the result of evaluating a derivative function, depending on the derivative level:
            switch (derivative)
            {
            case 0:
                DebugUtil.AssertFinite(global_y, nameof(global_y));
                return(global_y);

            case 1:
                DebugUtil.AssertFinite(slope, nameof(slope));
                return(slope);

            default: return(0.0);
            }
        }
Exemple #5
0
        /// <summary>
        ///     Solves the equation \f$a0 + a1 x + a2 x^2 + a3 x^3 + a4 x^4 = 0\f$, returning all real values of
        ///		\f$x\f$ for which the equation is true. See https://en.wikipedia.org/wiki/Quartic_function for the
        ///		algorithm used.
        /// </summary>
        public static IEnumerable <double> Solve(double a0, double a1, double a2, double a3, double a4)
        {
            DebugUtil.AssertAllFinite(new double[] { a0, a1, a2, a3, a4 }, "a");
            if (Math.Abs(a4) <= 0.005)
            {
                foreach (double v in CubicFunction.Solve(a0, a1, a2, a3))
                {
                    QuarticFunction f = new QuarticFunction(a0, a1, a2, a3, a4);
                    yield return(f.NewtonRaphson(v));
                }
                yield break;
            }

            double ba = a3 / a4;
            double ca = a2 / a4;
            double da = a1 / a4;
            // double ea = a0/a4;

            double p1 = a2 * a2 * a2 - 4.5 * a3 * a2 * a1 + 13.5 * a4 * a1 * a1 + 13.5 * a3 * a3 * a0 - 36.0 * a4 * a2 * a0;

            double q = a2 * a2 - 3.0 * a3 * a1 + 12.0 * a4 * a0;

            Complex p2 = p1 + Complex.Sqrt(-q * q * q + p1 * p1);

            Complex pow = Complex.Pow(p2, (1.0 / 3.0));

            Complex p3 = q / (3.0 * a4 * pow) + pow / (3.0 * a4);

            Complex p4 = Complex.Sqrt(ba * ba / 4.0 - 2.0 * ca / (3.0) + p3);

            Complex p5 = a3 * a3 / (2.0 * a4 * a4) - 4.0 * a2 / (3.0 * a4) - p3;

            Complex p6 = (-ba * ba * ba + 4.0 * ba * ca - 8.0 * da) / (4.0 * p4);

            List <Complex> roots = new List <Complex>
            {
                -ba / (4.0) - p4 / 2.0 - 0.5 * Complex.Sqrt(p5 - p6),
                -ba / (4.0) - p4 / 2.0 + 0.5 * Complex.Sqrt(p5 - p6),
                -ba / (4.0) + p4 / 2.0 - 0.5 * Complex.Sqrt(p5 + p6),
                -ba / (4.0) + p4 / 2.0 + 0.5 * Complex.Sqrt(p5 + p6),
            };

            foreach (Complex root in roots)
            {
                if (Math.Abs(root.Imaginary) <= 0.005)
                {
                    DebugUtil.AssertFinite(root.Real, nameof(root.Real));
                    yield return(root.Real);
                }
            }
        }
        /// <inheritdoc />
        public override double GetNthDerivativeAt(double x, uint derivative)
        {
            DebugUtil.AssertFinite(x, nameof(x));
            // Return a different function depending on the derivative level:
            switch (derivative)
            {
            case 0: return(_a0 + _a1 * x + _a2 * x * x);

            case 1: return(_a1 + 2.0f * _a2 * x);

            case 2: return(2.0f * _a2);

            default: return(0.0f);
            }
        }
Exemple #7
0
        /// <summary>
        ///     Solves the equation \f$a0 + a1 x + a2 x^2 + a3 x^3 = 0\f$, returning all real values of
        ///		\f$x\f$ for which the equation is true. See https://en.wikipedia.org/wiki/Cubic_equation for the
        ///		algorithm used.
        /// </summary>
        public static IEnumerable <double> Solve(double a0, double a1, double a2, double a3)
        {
            DebugUtil.AssertFinite(a3, nameof(a3));
            DebugUtil.AssertFinite(a2, nameof(a2));
            DebugUtil.AssertFinite(a1, nameof(a1));
            DebugUtil.AssertFinite(a0, nameof(a0));
            if (Math.Abs(a3) <= 0.005)
            {
                foreach (double v in QuadraticFunction.Solve(a0, a1, a2))
                {
                    CubicFunction f = new CubicFunction(a0, a1, a2, a3);
                    yield return(f.NewtonRaphson(v));
                }
                yield break;
            }

            double delta0 = a2 * a2 - 3.0 * a3 * a1;
            double delta1 = 2.0 * a2 * a2 * a2 - 9.0 * a3 * a2 * a1 + 27.0 * a3 * a3 * a0;

            Complex p1 = Complex.Sqrt(delta1 * delta1 - 4.0 * delta0 * delta0 * delta0);

            // The sign we choose in the next equation is arbitrary. To prevent a divide-by-zero down the line, if p2 is
            // zero, we must choose the opposite sign to make it nonzero:
            Complex p2 = delta1 + p1;

            Complex c = Complex.Pow(0.5 * p2, (1.0 / 3.0));

            Complex xi = -0.5 + 0.5 * Complex.Sqrt(-3.0);

            List <Complex> roots = new List <Complex>
            {
                -1.0 / (3.0 * a3) * (a2 + c + delta0 / c),
                -1.0 / (3.0 * a3) * (a2 + xi * c + delta0 / (xi * c)),
                -1.0 / (3.0 * a3) * (a2 + xi * xi * c + delta0 / (xi * xi * c)),
            };

            foreach (Complex root in roots)
            {
                if (Math.Abs(root.Imaginary) <= 0.05)
                {
                    DebugUtil.AssertFinite(root.Real, nameof(root.Real));
                    yield return(root.Real);
                }
            }
        }
Exemple #8
0
        /// <summary>
        ///     Solves the equation \f$d + c x + b x^2 + a x^3 = 0\f$, returning all real values of
        ///		\f$x\f$ for which the equation is true. See https://en.wikipedia.org/wiki/Cubic_equation for the
        ///		algorithm used.
        /// </summary>
        public static IEnumerable <double> Solve(double d, double c, double b, double a)
        {
            DebugUtil.AssertFinite(a, nameof(a));
            DebugUtil.AssertFinite(b, nameof(b));
            DebugUtil.AssertFinite(c, nameof(c));
            DebugUtil.AssertFinite(d, nameof(d));
            if (Math.Abs(a) <= 0.005f)
            {
                foreach (double v in QuadraticFunction.Solve(d, c, b))
                {
                    yield return(v);
                }
                yield break;
            }

            double delta0 = b * b - 3.0 * a * c;
            double delta1 = 2.0 * b * b * b - 9.0 * a * b * c + 27.0 * a * a * d;

            Complex p1 = Complex.Sqrt(delta1 * delta1 - 4.0 * delta0 * delta0 * delta0);

            // The sign we choose in the next equation is arbitrary. To prevent a divide-by-zero down the line, if p2 is
            // zero, we must choose the opposite sign to make it nonzero:
            Complex p2 = delta1 + p1;

            Complex C = Complex.Pow(0.5 * p2, (1.0 / 3.0));

            Complex xi = -0.5 + 0.5 * Complex.Sqrt(-3.0);

            List <Complex> roots = new List <Complex>
            {
                -1.0 / (3.0 * a) * (b + C + delta0 / C),
                -1.0 / (3.0 * a) * (b + xi * C + delta0 / (xi * C)),
                -1.0 / (3.0 * a) * (b + xi * xi * C + delta0 / (xi * xi * C)),
            };

            foreach (Complex root in roots)
            {
                if (Math.Abs(root.Imaginary) <= 0.05)
                {
                    DebugUtil.AssertFinite(root.Real, nameof(root.Real));
                    yield return(root.Real);
                }
            }
        }
Exemple #9
0
        public override double GetNthDerivativeAt(double x, uint derivative)
        {
            DebugUtil.AssertFinite(x, nameof(x));
            // Return a different function depending on the derivative level:
            switch (derivative)
            {
            case 0: return(A0 + A1 * x + A2 * x * x + A3 * x * x * x + A4 * x * x * x * x);

            case 1: return(A1 + 2.0 * A2 * x + 3.0 * A3 * x * x + 4.0 * A4 * x * x * x);

            case 2: return(2.0 * A2 + 6.0 * A3 * x + 12.0 * A4 * x * x);

            case 3: return(6.0 * A3 + 24.0 * A4 * x);

            case 4: return(24.0 * A4);

            default: return(0.0);
            }
        }
Exemple #10
0
        public double GetNthDerivativeAt(double x, uint derivative)
        {
            DebugUtil.AssertFinite(x, nameof(x));
            // Return a different function depending on the derivative level:
            switch (derivative)
            {
            case 0: return(a0 + a1 * x + a2 * x * x + a3 * x * x * x + a4 * x * x * x * x);

            case 1: return(a1 + 2.0 * a2 * x + 3.0 * a3 * x * x + 4.0 * a4 * x * x * x);

            case 2: return(2.0 * a2 + 6.0 * a3 * x + 12.0 * a4 * x * x);

            case 3: return(6.0 * a3 + 24.0 * a4 * x);

            case 4: return(24.0 * a4);

            default: return(0.0);
            }
        }
        /// <summary>
        ///     Solves a general equation \f$a_0 + a_1 x + a_2 x^2 = 0\f$, returning all real values of
        ///		\f$x\f$ for which the equation is true using the 'abc-formula'.
        /// </summary>
        public static IEnumerable <double> Solve(double a0, double a1, double a2)
        {
            DebugUtil.AssertFinite(a0, nameof(a0));
            DebugUtil.AssertFinite(a1, nameof(a1));
            DebugUtil.AssertFinite(a2, nameof(a2));
            if (Math.Abs(a2) <= 0.005)
            {
                if (Math.Abs(a1) <= 0.005)
                {
                    // There are no roots found:
                    yield break;
                }
                else
                {
                    // There is a single root, found from solving the linear equation with a1=0. We find a point close
                    // to the root using the linear approximation, after which we approach the true solution using the
                    // Newton-Raphson method:
                    QuadraticFunction f = new QuadraticFunction(a0, a1, a2);
                    yield return(f.NewtonRaphson(-a0 / a1));
                }
                yield break;
            }

            double x1 = 0.5 * (-a1 + Math.Sqrt(a1 * a1 - 4.0 * a0 * a2)) / a2;
            double x2 = 0.5 * (-a1 - Math.Sqrt(a1 * a1 - 4.0 * a0 * a2)) / a2;

            if (Double.IsNaN(x1) == false)
            {
                yield return(x1);
            }

            if (Double.IsNaN(x2) == false)
            {
                yield return(x2);
            }
        }
Exemple #12
0
        /// <inheritdoc />
        public double RayIntersect(Ray ray)
        {
            dvec3 rayStart     = ray.StartPosition;
            dvec3 rayDirection = ray.Direction;

            DebugUtil.AssertAllFinite(rayStart, nameof(rayStart));
            DebugUtil.AssertAllFinite(rayDirection, nameof(rayDirection));

            // Since we raytrace only using a cylindrical surface that is horizontal and at the origin, we
            // first shift and rotate the ray such that we get the right orientation:
            dvec3 start = CenterCurve.GetStartPosition();
            dvec3 end   = CenterCurve.GetEndPosition();

            DebugUtil.AssertAllFinite(start, nameof(start));
            DebugUtil.AssertAllFinite(end, nameof(end));

            dvec3 tangent  = CenterCurve.GetTangentAt(0.0).Normalized;
            dvec3 normal   = CenterCurve.GetNormalAt(0.0).Normalized;
            dvec3 binormal = CenterCurve.GetBinormalAt(0.0).Normalized;

            DebugUtil.AssertAllFinite(tangent, nameof(tangent));
            DebugUtil.AssertAllFinite(normal, nameof(normal));
            DebugUtil.AssertAllFinite(binormal, nameof(binormal));

            double length = dvec3.Distance(start, end);

            DebugUtil.AssertFinite(length, nameof(length));

            // CenterCurve is guaranteed to be a LineSegment, since the base property CenterCurve is masked by this
            // class' CenterCurve property that only accepts a LineSegment, and similarly this class' constructor only
            // accepts a LineSegment. The following mathematics, which assumes that the central axis is a line segment,
            // is therefore valid.

            dmat3 rotationMatrix = new dmat3(normal, binormal, tangent / length).Transposed;

            dvec3 rescaledRay  = rotationMatrix * (rayStart - start);
            dvec3 newDirection = rotationMatrix * rayDirection.Normalized;


            double x0 = rescaledRay.x;
            double y0 = rescaledRay.y;
            double z0 = rescaledRay.z;

            double a = newDirection.x;
            double b = newDirection.y;
            double c = newDirection.z;

            // Raytrace using a cylindrical surface equation x^2 + y^2. The parameters in the following line
            // represent the coefficients of the expanded cylindrical surface equation, after the substitution
            // x = x_0 + a t and y = y_0 + b t:
            QuarticFunction surfaceFunction = new QuarticFunction(x0 * x0 + y0 * y0, 2.0 * (x0 * a + y0 * b), a * a + b * b, 0.0, 0.0);

            IEnumerable <double> intersections = Radius.SolveRaytrace(surfaceFunction, z0, c);

            // The previous function returns a list of intersection distances. The value closest to 0.0f represents the
            // closest intersection point.
            double minimum = Single.PositiveInfinity;

            foreach (double i in intersections)
            {
                // Calculate the 3d point at which the ray intersects the cylinder:
                dvec3 intersectionPoint = rayStart + i * rayDirection;

                // Find the closest point to the intersectionPoint on the centerLine.
                // Get the vector v from the start of the cylinder to the intersection point:
                dvec3 v = intersectionPoint - start;

                // ...And project this vector onto the center line:
                double t = -dvec3.Dot(intersectionPoint, tangent * length) / (length * length);

                // Now we have the parameter t on the surface of the SymmetricCylinder at which the ray intersects.

                // Find the angle to the normal of the centerLine, so that we can determine whether the
                // angle is within the bound of the pie-slice at position t:
                dvec3  centerLineNormal   = CenterCurve.GetNormalAt(t);
                dvec3  centerLineBinormal = CenterCurve.GetBinormalAt(t);
                dvec3  d = intersectionPoint - CenterCurve.GetPositionAt(t);
                double correctionShift = Math.Sign(dvec3.Dot(d, centerLineBinormal));
                double phi             = (correctionShift * Math.Acos(dvec3.Dot(d, centerLineNormal))) % (2.0 * Math.PI);

                // Determine if the ray is inside the pie-slice of the cylinder that is being displayed,
                // otherwise discard:
                if (phi > StartAngle.GetValueAt(t) && phi < EndAngle.GetValueAt(t) && i >= 0.0)
                {
                    minimum = Math.Sign(i) * Math.Min(Math.Abs(minimum), Math.Abs(i));
                }
            }

            return(minimum);
        }