예제 #1
0
        /// <summary>Clones the TrackFollower</summary>
        public TrackFollower Clone()
        {
            TrackFollower t = new TrackFollower(currentHost, Train, Car);

            t.LastTrackElement = LastTrackElement;
            t.TrackPosition    = TrackPosition;
            t.WorldPosition    = new Vector3(WorldPosition);
            t.WorldDirection   = new Vector3(WorldDirection);
            t.WorldUp          = new Vector3(WorldUp);
            t.WorldSide        = new Vector3(WorldSide);
            t.TriggerType      = TriggerType;
            return(t);
        }
예제 #2
0
파일: Track.cs 프로젝트: zbx1425/OpenBVE
        /// <summary>Smooths all curves / turns</summary>
        public void SmoothTurns(int subdivisions, HostInterface currentHost)
        {
            if (subdivisions < 2)
            {
                throw new InvalidOperationException();
            }

            // subdivide track
            int length    = Elements.Length;
            int newLength = (length - 1) * subdivisions + 1;

            double[]      midpointsTrackPositions  = new double[newLength];
            Vector3[]     midpointsWorldPositions  = new Vector3[newLength];
            Vector3[]     midpointsWorldDirections = new Vector3[newLength];
            Vector3[]     midpointsWorldUps        = new Vector3[newLength];
            Vector3[]     midpointsWorldSides      = new Vector3[newLength];
            double[]      midpointsCant            = new double[newLength];
            TrackFollower follower = new TrackFollower(currentHost);

            for (int i = 0; i < newLength; i++)
            {
                int m = i % subdivisions;
                if (m != 0)
                {
                    int q = i / subdivisions;

                    double r = (double)m / (double)subdivisions;
                    double p = (1.0 - r) * Elements[q].StartingTrackPosition + r * Elements[q + 1].StartingTrackPosition;
                    follower.UpdateAbsolute(-1.0, true, false);
                    follower.UpdateAbsolute(p, true, false);
                    midpointsTrackPositions[i]  = p;
                    midpointsWorldPositions[i]  = follower.WorldPosition;
                    midpointsWorldDirections[i] = follower.WorldDirection;
                    midpointsWorldUps[i]        = follower.WorldUp;
                    midpointsWorldSides[i]      = follower.WorldSide;
                    midpointsCant[i]            = follower.CurveCant;
                }
            }

            Array.Resize(ref Elements, newLength);
            for (int i = length - 1; i >= 1; i--)
            {
                Elements[subdivisions * i] = Elements[i];
            }

            for (int i = 0; i < Elements.Length; i++)
            {
                int m = i % subdivisions;
                if (m != 0)
                {
                    int q = i / subdivisions;
                    int j = q * subdivisions;
                    Elements[i]        = Elements[j];
                    Elements[i].Events = new GeneralEvent[] { };
                    Elements[i].StartingTrackPosition = midpointsTrackPositions[i];
                    Elements[i].WorldPosition         = midpointsWorldPositions[i];
                    Elements[i].WorldDirection        = midpointsWorldDirections[i];
                    Elements[i].WorldUp          = midpointsWorldUps[i];
                    Elements[i].WorldSide        = midpointsWorldSides[i];
                    Elements[i].CurveCant        = midpointsCant[i];
                    Elements[i].CurveCantTangent = 0.0;
                }
            }

            // find turns
            bool[] isTurn = new bool[Elements.Length];
            {
                for (int i = 1; i < Elements.Length - 1; i++)
                {
                    int m = i % subdivisions;
                    if (m == 0)
                    {
                        double p = 0.00000001 * Elements[i - 1].StartingTrackPosition + 0.99999999 * Elements[i].StartingTrackPosition;
                        follower.UpdateAbsolute(p, true, false);
                        Vector3      d1 = Elements[i].WorldDirection;
                        Vector3      d2 = follower.WorldDirection;
                        Vector3      d  = d1 - d2;
                        double       t  = d.X * d.X + d.Z * d.Z;
                        const double e  = 0.0001;
                        if (t > e)
                        {
                            isTurn[i] = true;
                        }
                    }
                }
            }
            // replace turns by curves
            for (int i = 0; i < Elements.Length; i++)
            {
                if (isTurn[i])
                {
                    // estimate radius
                    Vector3 AP = Elements[i - 1].WorldPosition;
                    Vector3 BP = Elements[i + 1].WorldPosition;
                    Vector3 S  = Elements[i - 1].WorldSide - Elements[i + 1].WorldSide;
                    double  rx;
                    if (S.X * S.X > 0.000001)
                    {
                        rx = (BP.X - AP.X) / S.X;
                    }
                    else
                    {
                        rx = 0.0;
                    }

                    double rz;
                    if (S.Z * S.Z > 0.000001)
                    {
                        rz = (BP.Z - AP.Z) / S.Z;
                    }
                    else
                    {
                        rz = 0.0;
                    }

                    if (rx != 0.0 | rz != 0.0)
                    {
                        double r;
                        if (rx != 0.0 & rz != 0.0)
                        {
                            if (System.Math.Sign(rx) == System.Math.Sign(rz))
                            {
                                double f = rx / rz;
                                if (f > -1.1 & f <-0.9 | f> 0.9 & f < 1.1)
                                {
                                    r = System.Math.Sqrt(System.Math.Abs(rx * rz)) * System.Math.Sign(rx);
                                }
                                else
                                {
                                    r = 0.0;
                                }
                            }
                            else
                            {
                                r = 0.0;
                            }
                        }
                        else if (rx != 0.0)
                        {
                            r = rx;
                        }
                        else
                        {
                            r = rz;
                        }

                        if (r * r > 1.0)
                        {
                            // apply radius
                            Elements[i - 1].CurveRadius = r;
                            double p = 0.00000001 * Elements[i - 1].StartingTrackPosition + 0.99999999 * Elements[i].StartingTrackPosition;
                            follower.UpdateAbsolute(p - 1.0, true, false);
                            follower.UpdateAbsolute(p, true, false);
                            Elements[i].CurveRadius    = r;
                            Elements[i].WorldPosition  = follower.WorldPosition;
                            Elements[i].WorldDirection = follower.WorldDirection;
                            Elements[i].WorldUp        = follower.WorldUp;
                            Elements[i].WorldSide      = follower.WorldSide;
                            // iterate to shorten track element length
                            p = 0.00000001 * Elements[i].StartingTrackPosition + 0.99999999 * Elements[i + 1].StartingTrackPosition;
                            follower.UpdateAbsolute(p - 1.0, true, false);
                            follower.UpdateAbsolute(p, true, false);
                            Vector3 d     = Elements[i + 1].WorldPosition - follower.WorldPosition;
                            double  bestT = d.NormSquared();
                            int     bestJ = 0;
                            int     n     = 1000;
                            double  a     = 1.0 / (double)n * (Elements[i + 1].StartingTrackPosition - Elements[i].StartingTrackPosition);
                            for (int j = 1; j < n - 1; j++)
                            {
                                follower.UpdateAbsolute(Elements[i + 1].StartingTrackPosition - (double)j * a, true, false);
                                d = Elements[i + 1].WorldPosition - follower.WorldPosition;
                                double t = d.NormSquared();
                                if (t < bestT)
                                {
                                    bestT = t;
                                    bestJ = j;
                                }
                                else
                                {
                                    break;
                                }
                            }

                            double s = (double)bestJ * a;
                            for (int j = i + 1; j < Elements.Length; j++)
                            {
                                Elements[j].StartingTrackPosition -= s;
                            }

                            // introduce turn to compensate for curve
                            p = 0.00000001 * Elements[i].StartingTrackPosition + 0.99999999 * Elements[i + 1].StartingTrackPosition;
                            follower.UpdateAbsolute(p - 1.0, true, false);
                            follower.UpdateAbsolute(p, true, false);
                            Vector3 AB          = Elements[i + 1].WorldPosition - follower.WorldPosition;
                            Vector3 AC          = Elements[i + 1].WorldPosition - Elements[i].WorldPosition;
                            Vector3 BC          = follower.WorldPosition - Elements[i].WorldPosition;
                            double  sa          = System.Math.Sqrt(BC.X * BC.X + BC.Z * BC.Z);
                            double  sb          = System.Math.Sqrt(AC.X * AC.X + AC.Z * AC.Z);
                            double  sc          = System.Math.Sqrt(AB.X * AB.X + AB.Z * AB.Z);
                            double  denominator = 2.0 * sa * sb;
                            if (denominator != 0.0)
                            {
                                double originalAngle;
                                {
                                    double value = (sa * sa + sb * sb - sc * sc) / denominator;
                                    if (value < -1.0)
                                    {
                                        originalAngle = System.Math.PI;
                                    }
                                    else if (value > 1.0)
                                    {
                                        originalAngle = 0;
                                    }
                                    else
                                    {
                                        originalAngle = System.Math.Acos(value);
                                    }
                                }
                                TrackElement originalTrackElement = Elements[i];
                                bestT = double.MaxValue;
                                bestJ = 0;
                                for (int j = -1; j <= 1; j++)
                                {
                                    double g = (double)j * originalAngle;
                                    Elements[i] = originalTrackElement;
                                    Elements[i].WorldDirection.Rotate(Vector3.Down, g);
                                    Elements[i].WorldUp.Rotate(Vector3.Down, g);
                                    Elements[i].WorldSide.Rotate(Vector3.Down, g);
                                    p = 0.00000001 * Elements[i].StartingTrackPosition + 0.99999999 * Elements[i + 1].StartingTrackPosition;
                                    follower.UpdateAbsolute(p - 1.0, true, false);
                                    follower.UpdateAbsolute(p, true, false);
                                    d = Elements[i + 1].WorldPosition - follower.WorldPosition;
                                    double t = d.NormSquared();
                                    if (t < bestT)
                                    {
                                        bestT = t;
                                        bestJ = j;
                                    }
                                }

                                {
                                    double newAngle = (double)bestJ * originalAngle;
                                    Elements[i] = originalTrackElement;
                                    Elements[i].WorldDirection.Rotate(Vector3.Down, newAngle);
                                    Elements[i].WorldUp.Rotate(Vector3.Down, newAngle);
                                    Elements[i].WorldSide.Rotate(Vector3.Down, newAngle);
                                }
                                // iterate again to further shorten track element length
                                p = 0.00000001 * Elements[i].StartingTrackPosition + 0.99999999 * Elements[i + 1].StartingTrackPosition;
                                follower.UpdateAbsolute(p - 1.0, true, false);
                                follower.UpdateAbsolute(p, true, false);
                                d     = Elements[i + 1].WorldPosition - follower.WorldPosition;
                                bestT = d.NormSquared();
                                bestJ = 0;
                                n     = 1000;
                                a     = 1.0 / (double)n * (Elements[i + 1].StartingTrackPosition - Elements[i].StartingTrackPosition);
                                for (int j = 1; j < n - 1; j++)
                                {
                                    follower.UpdateAbsolute(Elements[i + 1].StartingTrackPosition - (double)j * a, true, false);
                                    d = Elements[i + 1].WorldPosition - follower.WorldPosition;
                                    double t = d.NormSquared();
                                    if (t < bestT)
                                    {
                                        bestT = t;
                                        bestJ = j;
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }

                                s = (double)bestJ * a;
                                for (int j = i + 1; j < Elements.Length; j++)
                                {
                                    Elements[j].StartingTrackPosition -= s;
                                }
                            }

                            // compensate for height difference
                            p = 0.00000001 * Elements[i].StartingTrackPosition + 0.99999999 * Elements[i + 1].StartingTrackPosition;
                            follower.UpdateAbsolute(p - 1.0, true, false);
                            follower.UpdateAbsolute(p, true, false);
                            Vector3 d1 = Elements[i + 1].WorldPosition - Elements[i].WorldPosition;
                            double  a1 = System.Math.Atan(d1.Y / System.Math.Sqrt(d1.X * d1.X + d1.Z * d1.Z));
                            Vector3 d2 = follower.WorldPosition - Elements[i].WorldPosition;
                            double  a2 = System.Math.Atan(d2.Y / System.Math.Sqrt(d2.X * d2.X + d2.Z * d2.Z));
                            double  b  = a2 - a1;
                            if (b * b > 0.00000001)
                            {
                                Elements[i].WorldDirection.Rotate(Elements[i].WorldSide, b);
                                Elements[i].WorldUp.Rotate(Elements[i].WorldSide, b);
                            }
                        }
                    }
                }
            }

            // correct events
            for (int i = 0; i < Elements.Length - 1; i++)
            {
                double startingTrackPosition = Elements[i].StartingTrackPosition;
                double endingTrackPosition   = Elements[i + 1].StartingTrackPosition;
                for (int j = 0; j < Elements[i].Events.Length; j++)
                {
                    GeneralEvent e = Elements[i].Events[j];
                    double       p = startingTrackPosition + e.TrackPositionDelta;
                    if (p >= endingTrackPosition)
                    {
                        int len = Elements[i + 1].Events.Length;
                        Array.Resize(ref Elements[i + 1].Events, len + 1);
                        Elements[i + 1].Events[len] = Elements[i].Events[j];
                        e = Elements[i + 1].Events[len];
                        e.TrackPositionDelta += startingTrackPosition - endingTrackPosition;
                        for (int k = j; k < Elements[i].Events.Length - 1; k++)
                        {
                            Elements[i].Events[k] = Elements[i].Events[k + 1];
                        }

                        len = Elements[i].Events.Length;
                        Array.Resize(ref Elements[i].Events, len - 1);
                        j--;
                    }
                }
            }
        }