示例#1
0
        /// <summary>
        ///     Construct a linear spline using a set of input points.
        ///     <example>For example:
        ///     <code>
        ///         SortedList<double, double> splinePoints = new SortedList<double, double>();
        ///         splinePoints.Add(0.0f, 1.1f);
        ///         splinePoints.Add(0.3f, 0.4f);
        ///         splinePoints.Add(1.0f, 2.0f);
        ///         LinearSpline1D spline = new LinearSpline1D(splinePoints);
        ///     </code>
        ///     creates a linear spline that passes through three points: (0.0, 1.1), (0.3, 0.4) and (1.0, 2.0).
        ///     </example>
        /// </summary>
        /// <param name="points">A list of points that is sorted by the x-coordinate. This collection is copied.</param>
        /// <exception cref="ArgumentException">
        ///     A linear spline must have at least two points to be properly defined. If <c>points</c> contains less than
        ///     two points, the spline is undefined, so an <c>ArgumentException</c> is thrown.
        /// </exception>
        public LinearSpline1D(SortedList <double, double> points)
        {
            if (points.Count < 2)
            {
                if (points.Count == 1)
                {
                    throw new ArgumentException("List contains only a single point. A spline must have at least two points.", nameof(points));
                }
                else
                {
                    throw new ArgumentException("List is empty. A spline must have at least two points.", nameof(points));
                }
            }

            Points = new SortedPointsList <double>(points);
            DebugUtil.AssertAllFinite(Points, nameof(Points));

            // Calculate the coefficients for each segment of the spline:
            _parameters = new double[points.Count];

            // Recursively find the _parameters:
            for (int i = 1; i < points.Count; i++)
            {
                double dx = Points.Key[i] - Points.Key[i - 1];
                double dy = Points.Value[i] - Points.Value[i - 1];

                _parameters[i] = dy / dx;
            }
            DebugUtil.AssertAllFinite(_parameters, nameof(_parameters));
        }
示例#2
0
        /// <summary>
        ///     Find the solution to a tridiagonal matrix linear system Ax = d using the Thomas algorithm.
        /// </summary>
        private static double[] ThomasAlgorithm(double[] a, double[] b, double[] c, double[] d)
        {
            DebugUtil.AssertAllFinite(a, nameof(a));
            DebugUtil.AssertAllFinite(b, nameof(b));
            DebugUtil.AssertAllFinite(c, nameof(c));
            DebugUtil.AssertAllFinite(d, nameof(d));

            int size = d.Count();

            // Perform forward sweep:
            double[] newC = new double[size];
            double[] newD = new double[size];

            newC[0] = c[0] / b[0];
            newD[0] = d[0] / b[0];
            for (int i = 1; i < size; i++)
            {
                newC[i] = c[i] / (b[i] - a[i] * newC[i - 1]);
                newD[i] = (d[i] - a[i] * newD[i - 1]) / (b[i] - a[i] * newC[i - 1]);
            }
            DebugUtil.AssertAllFinite(newC, nameof(newC));
            DebugUtil.AssertAllFinite(newD, nameof(newD));

            // Perform back substitution:
            double[] x = new double[size];

            x[size - 1] = newD[size - 1];
            for (int i = (size - 2); i >= 0; i--)
            {
                x[i] = newD[i] - newC[i] * x[i + 1];
            }

            DebugUtil.AssertAllFinite(x, nameof(x));
            return(x);
        }
示例#3
0
 /// <summary>
 ///		Constructs a ShiftedMap2D whose input is shifted by a dvec2 and stretched by another dvec2.
 /// </summary>
 /// <param name="shift">
 ///     The vector by which the 2D map is shifted.
 /// </param>
 /// <param name="stretch">
 ///     The vector by which the 2D map's coordinates are multiplied.
 /// </param>
 /// <param name="function">
 ///     The ContinuousMap that is shifted and stretched.
 /// </param>
 public ShiftedMap2D(dvec2 shift, dvec2 stretch, ContinuousMap <dvec2, TOut> function)
 {
     DebugUtil.AssertAllFinite(shift, nameof(shift));
     DebugUtil.AssertAllFinite(stretch, nameof(stretch));
     this.Shift    = shift;
     this.Stretch  = stretch;
     this.Function = function;
 }
示例#4
0
 /// <summary>
 ///     A quartic function defined by \f$q(x) = a_0 + a_1 x + a_2 x^2 + a_3 x^3 + a_4 x^4\f$.
 ///		See https://en.wikipedia.org/wiki/Quartic_function for more information.
 /// </summary>
 public QuarticFunction(double a0, double a1, double a2, double a3, double a4)
 {
     DebugUtil.AssertAllFinite(new double[] { a0, a1, a2, a3, a4 }, "a");
     this.A0 = a0;
     this.A1 = a1;
     this.A2 = a2;
     this.A3 = a3;
     this.A4 = a4;
 }
示例#5
0
        /// <inheritdoc />
        public override dvec3 GetPositionAt(dvec2 uv)
        {
            DebugUtil.AssertAllFinite(uv, nameof(uv));
            double v = uv.y;

            dvec3  translation = CenterCurve.GetPositionAt(v);
            double radius      = Radius.GetValueAt(uv);

            return(translation + radius * GetNormalAt(uv));
        }
示例#6
0
 /// <summary>
 ///     Construct a new <c>Hemisphere</c> at the point <c>center</c>, pointing in the direction of
 ///     <c>direction</c>. The radius is defined by a two-dimensional function <c>radius</c>.
 ///
 ///     The surface is parametrized with the coordinates \f$u \in [0, 2\pi]\f$ (the azimuthal angle) and
 ///     \f$v \in [0, \frac{1}{2} \pi]\f$ (the inclination angle).
 /// </summary>
 /// <param name="center">
 ///     The position of the hemisphere.
 /// </param>
 /// <param name="direction">
 ///     The vector that defines which way is 'forward' for the object. Will be normalized on initialization.
 /// </param>
 /// <param name="normal">
 ///     The vector that defines which way is 'up' for the object. Should be perpendicular to
 ///     <c>direction</c> and <c>binormal</c>. Will be normalized on initialization.
 /// </param>
 /// <param name="binormal">
 ///     The vector that defines which way is 'left' for the object. Should be perpendicular to
 ///     <c>direction</c> and <c>normal</c>. Will be normalized on initialization.
 /// </param>
 public Hemisphere(ContinuousMap <dvec2, double> radius, dvec3 center, dvec3 direction, dvec3 normal, dvec3 binormal)
 {
     DebugUtil.AssertAllFinite(center, nameof(center));
     DebugUtil.AssertAllFinite(direction, nameof(direction));
     DebugUtil.AssertAllFinite(normal, nameof(normal));
     DebugUtil.AssertAllFinite(binormal, nameof(binormal));
     this.Radius    = radius;
     this.Center    = center;
     this.Direction = direction;
     this.Normal    = normal;
     this.Binormal  = binormal;
 }
示例#7
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);
                }
            }
        }
示例#8
0
        /// <inheritdoc />
        public override dvec3 GetNormalAt(dvec2 uv)
        {
            DebugUtil.AssertAllFinite(uv, nameof(uv));
            double u = uv.x;
            double v = uv.y;

            dvec3 curveTangent  = CenterCurve.GetTangentAt(v).Normalized;
            dvec3 curveNormal   = CenterCurve.GetNormalAt(v).Normalized;
            dvec3 curveBinormal = dvec3.Cross(curveTangent, curveNormal);

            double startAngle = StartAngle.GetValueAt(v);
            double endAngle   = EndAngle.GetValueAt(v);

            return(Math.Cos(u) * curveNormal + Math.Sin(u) * curveBinormal);
        }
示例#9
0
        /// <inheritdoc />
        public override double GetValueAt(dvec2 uv)
        {
            DebugUtil.AssertAllFinite(uv, nameof(uv));
            double directionSign = (direction == RayCastDirection.Outwards) ? 1.0 : -1.0;

            Ray    ray = new Ray(raycastSurface.GetPositionAt(uv), directionSign * raycastSurface.GetNormalAt(uv).Normalized);
            double intersectionRadius = moldSurface.RayIntersect(ray);

            if (Math.Abs(intersectionRadius) <= maxDistance)
            {
                return(intersectionRadius);
            }
            else
            {
                return(defaultRadius.GetValueAt(uv));
            }
        }
示例#10
0
        /// <summary>
        ///     Construct a linear spline using a set of input points.
        ///     <example>For example:
        ///     <code>
        ///         SortedList{double, double} splinePoints = new SortedList{double, double}();
        ///         splinePoints.Add(0.0f, 1.1f);
        ///         splinePoints.Add(0.3f, 0.4f);
        ///         splinePoints.Add(1.0f, 2.0f);
        ///         LinearSpline1D spline = new LinearSpline1D(splinePoints);
        ///     </code>
        ///     creates a linear spline that passes through three points: (0.0, 1.1), (0.3, 0.4) and (1.0, 2.0).
        ///     </example>
        /// </summary>
        /// <param name="points">A list of points that is sorted by the x-coordinate. This collection is copied.</param>
        /// <exception cref="ArgumentException">
        ///     A linear spline must have at least two points to be properly defined. If <c>points</c> contains less than
        ///     two points, the spline is undefined, so an <c>ArgumentException</c> is thrown.
        /// </exception>
        public LinearSpline1D(SortedList <double, double> points)
        {
            if (points.Count < 2)
            {
                if (points.Count == 1)
                {
                    throw new ArgumentException("List contains only a single point. A spline must have at least two points.", nameof(points));
                }
                else
                {
                    throw new ArgumentException("List is empty. A spline must have at least two points.", nameof(points));
                }
            }

            Points = new SortedPointsList <double>(points);
            DebugUtil.AssertAllFinite(Points, nameof(Points));
        }
示例#11
0
        /// <inheritdoc />
        public override dvec3 GetNormalAt(dvec2 uv)
        {
            DebugUtil.AssertAllFinite(uv, nameof(uv));
            double u = uv.x;
            double v = uv.y;

            if ((v >= -0.5 * Math.PI) && (v < 0.0))
            {
                return(startCap.GetNormalAt(new dvec2(u, v + 0.5 * Math.PI)));
            }
            else if ((v >= 0.0) && (v < 1.0))
            {
                return(shaft.GetNormalAt(new dvec2(u, v)));
            }
            else if ((v >= 1.0) && (v <= 1.0 + 0.5 * Math.PI))
            {
                return(endCap.GetNormalAt(new dvec2(u, 1.0 + 0.5 * Math.PI - v)));
            }
            else
            {
                throw new ArgumentOutOfRangeException("v", "'v' must be between [-0.5 pi] and [1.0 + 0.5 pi].");
            }
        }
示例#12
0
 /// <inheritdoc />
 public override TOut GetValueAt(dvec2 uv)
 {
     DebugUtil.AssertAllFinite(uv, nameof(uv));
     return(Function.GetValueAt(Shift + Stretch * uv));
 }
示例#13
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);
        }
示例#14
0
        /// <summary>
        ///     Construct a cubic spline using a set of input points.
        ///     <example>For example:
        ///     <code>
        ///         SortedList{double, double} splinePoints = new SortedList{double, double}();
        ///         splinePoints.Add(0.0f, 1.1f);
        ///         splinePoints.Add(0.3f, 0.4f);
        ///         splinePoints.Add(1.0f, 2.0f);
        ///         CubicSpline1D spline = new CubicSpline1D(splinePoints);
        ///     </code>
        ///     creates a cubic spline that passes through three points: (0.0, 1.1), (0.3, 0.4) and (1.0, 2.0).
        ///     </example>
        /// </summary>
        /// <param name="points">A list of points that is sorted by the x-coordinate. This collection is copied.</param>
        /// <exception cref="ArgumentException">
        ///     A cubic spline must have at least two points to be properly defined. If <c>points</c> contains less than
        ///     two points, the spline is undefined, so an <c>ArgumentException</c> is thrown.
        /// </exception>
        public CubicSpline1D(SortedList <double, double> points)
        {
            if (points.Count < 2)
            {
                if (points.Count == 1)
                {
                    throw new ArgumentException("List contains only a single point. A spline must have at least two points.", "points");
                }
                else
                {
                    throw new ArgumentException("List is empty. A spline must have at least two points.", "points");
                }
            }

            Points = new SortedPointsList <double>(points);
            DebugUtil.AssertAllFinite(Points, nameof(Points));

            // Calculate the coefficients of the spline:
            double[] a = new double[points.Count];
            double[] b = new double[points.Count];
            double[] c = new double[points.Count];
            double[] d = new double[points.Count];

            // Set up the boundary condition for a natural spline:
            {
                double x2 = 1.0 / (Points.Key[1] - Points.Key[0]);
                double y2 = Points.Value[1] - Points.Value[0];

                a[0] = 0.0;
                b[0] = 2.0 * x2;
                c[0] = x2;
                d[0] = 3.0 * (y2 * x2 * x2);
            }

            // Set up the tridiagonal matrix linear system:
            for (int i = 1; i < points.Count - 1; i++)
            {
                double x1 = 1.0 / (Points.Key[i] - Points.Key[i - 1]);
                double x2 = 1.0 / (Points.Key[i + 1] - Points.Key[i]);

                double y1 = Points.Value[i] - Points.Value[i - 1];
                double y2 = Points.Value[i + 1] - Points.Value[i];

                a[i] = x1;
                b[i] = 2.0 * (x1 + x2);
                c[i] = x2;
                d[i] = 3.0 * (y1 * x1 * x1 + y2 * x2 * x2);
            }

            // Set up the boundary condition for a natural spline:
            {
                double x1 = 1.0 / (Points.Key[points.Count - 1] - Points.Key[points.Count - 2]);
                double y1 = (Points.Value[points.Count - 1] - Points.Value[points.Count - 2]);

                a[points.Count - 1] = x1;
                b[points.Count - 1] = 2.0 * x1;
                c[points.Count - 1] = 0.0;
                d[points.Count - 1] = 3.0 * (y1 * x1 * x1);
            }

            // Solve the linear system using the Thomas algorithm:
            this._parameters = ThomasAlgorithm(a, b, c, d);
        }