Guest User

Катмулл-Рома

a guest
Jan 27th, 2015
488
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.42 KB | None | 0 0
  1. /// <summary>
  2. /// Сплайн Катмулл-Рома.
  3. /// http://www.gamedev.ru/code/tip/catmull_rom
  4. /// </summary>
  5. private class CatmullRomCurve
  6. {
  7.     private struct CurveSegment
  8.     {
  9.         public Vector2 A;
  10.         public Vector2 B;
  11.         public Vector2 C;
  12.         public Vector2 D;
  13.  
  14.         public CurveSegment(Vector2 M0, Vector2 P0, Vector2 P1, Vector2 M1)
  15.         {
  16.             Vector2 V0 = Vector2.Zero;
  17.             this.A = V0;
  18.             this.B = V0;
  19.             this.C = V0;
  20.             this.D = V0;
  21.  
  22.             RecalculateFactors(M0, P0, P1, M1);
  23.         }
  24.  
  25.         public void RecalculateFactors(Vector2 M0, Vector2 P0, Vector2 P1, Vector2 M1)
  26.         {
  27.             this.A = ((P0 - P1) * 1.5f) + ((M1 - M0) * 0.5f);
  28.             this.B = (P1 * 2.0f) - (P0 * 2.5f) - (M1 * 0.5f) + M0;
  29.             this.C = (P1 - M0) * 0.5f;
  30.             this.D = P0;
  31.         }
  32.         public Vector2 CurveValue(float t)
  33.         {
  34.             return ((this.A * t + this.B) * t + this.C) * t + this.D;
  35.         }
  36.         public Vector2 CurveVelocity(float t)
  37.         {
  38.             return (this.A * 3 * t + this.B * 2) * t + this.C;
  39.         }
  40.     }
  41.  
  42.     private List<Vector2> mPoints = new List<Vector2>();
  43.     private List<CurveSegment> mSeg = new List<CurveSegment>();
  44.  
  45.     public CatmullRomCurve(Vector2 P0, Vector2 P1)
  46.     {
  47.         mPoints.Add(P0);
  48.         mPoints.Add(P1);
  49.  
  50.         RecalculateSegments();
  51.     }
  52.     public CatmullRomCurve(Vector2 P0, Vector2 P1, Vector2 P2, Vector2 P3)
  53.     {
  54.         mPoints.Add(P0);
  55.         mPoints.Add(P1);
  56.         mPoints.Add(P2);
  57.         mPoints.Add(P3);
  58.  
  59.         RecalculateSegments();
  60.     }
  61.  
  62.     private void RecalculateSegments()
  63.     {
  64.         mSeg.Clear();
  65.  
  66.         for (int i = 0; i < mPoints.Count - 1; i++)
  67.         {
  68.             mSeg.Add(new CurveSegment(PrevPoint(i), mPoints[i], NextPoint(i), NextPoint(i + 1)));
  69.         }
  70.  
  71.         mNeedRecalcCurveLength = true;
  72.     }
  73.  
  74.     private Vector2 NextPoint(int i)
  75.     {
  76.         if ((i + 1) >= mPoints.Count) return mPoints[mPoints.Count - 1];
  77.         return mPoints[i + 1];
  78.     }
  79.     private Vector2 PrevPoint(int i)
  80.     {
  81.         if ((i - 1) < 0) return mPoints[0];
  82.         return mPoints[i - 1];
  83.     }
  84.  
  85.     private bool mNeedRecalcCurveLength = true;
  86.     private float mCurveLength = 0f;
  87.     private void RecalcCurveLength()
  88.     {
  89.         mCurveLength = 0;
  90.         for (int i = 0; i < mPoints.Count - 1 ; i++)
  91.         {
  92.             mCurveLength += (mPoints[i] - mPoints[i + 1]).Length();
  93.         }
  94.         mNeedRecalcCurveLength = false;
  95.     }
  96.  
  97.     /// <summary>
  98.     /// Длина линии в сегментах.
  99.     /// </summary>
  100.     public float CurveLengthInSeg
  101.     {
  102.         get { return (float)(mSeg.Count); }
  103.     }
  104.     /// <summary>
  105.     /// Линейная длина кривой.
  106.     /// </summary>
  107.     /// <returns>Длина.</returns>
  108.     public float CurveLengthLin
  109.     {
  110.         get
  111.         {
  112.             if (mNeedRecalcCurveLength) RecalcCurveLength();
  113.             return mCurveLength;
  114.         }
  115.     }
  116.     /// <summary>
  117.     /// Длина кривой по точкам заданым с шагом аргумента.
  118.     /// </summary>
  119.     /// <param name="Step">Шаг аргумента.</param>
  120.     /// <returns>Длина.</returns>
  121.     public float CurveLength(float Step)
  122.     {
  123.         float CurveLength = 0;
  124.         float t = 0;
  125.         Vector2 PreviosPoint = Evalute(t);
  126.  
  127.         while (t < CurveLengthInSeg)
  128.         {
  129.             t += Step;
  130.             Vector2 CurrentPoint = Evalute(t);
  131.  
  132.             CurveLength += (PreviosPoint - CurrentPoint).Length();
  133.             PreviosPoint = CurrentPoint;
  134.         }
  135.  
  136.         return CurveLength;
  137.     }
  138.  
  139.     private int mSegIndex = -1;
  140.     private float mSegRem = -1;
  141.     private void SetSegCurve(float t)
  142.     {
  143.         // обрезать t до допустимого диапазона
  144.         t = Math.Min(t, mSeg.Count);
  145.         t = Math.Max(t, 0);
  146.  
  147.         // кривоватэ
  148.         if (t == mSeg.Count)
  149.         {
  150.             mSegIndex = mSeg.Count -1;
  151.             mSegRem = 1;
  152.             return;
  153.         }
  154.  
  155.         mSegIndex = (int)Math.Floor(t);
  156.         mSegRem = t - mSegIndex;
  157.     }
  158.    
  159.     /// <summary>
  160.     /// Значение кривой для t.
  161.     /// </summary>
  162.     /// <param name="t">t - от 0 до CurveLength(Point.Count-1)</param>
  163.     /// <returns>Значение в аргументе.</returns>
  164.     public Vector2 Evalute(float t)
  165.     {
  166.         SetSegCurve(t);
  167.         return mSeg[mSegIndex].CurveValue(mSegRem);
  168.     }
  169.     /// <summary>
  170.     /// Найти приращение t так чтобы идти по кривой с равномерной скоростью Vel.
  171.     /// </summary>
  172.     /// <param name="t">Исходный параметр t.</param>
  173.     /// <param name="Vel">Требуемая скорость.</param>
  174.     /// <returns>Приращение t для Vel.</returns>
  175.     public float EvaluteParamUniform(float t, float Vel)
  176.     {
  177.         SetSegCurve(t);
  178.         Vector2 CurveVel = mSeg[mSegIndex].CurveVelocity(mSegRem);
  179.         // иногда скорость кривой считается неправильно
  180.         // тогда можно проскочить очень много шагов
  181.         // для устойчивости нужен минимальный шаг.
  182.         return Math.Min(0.05f, Vel / CurveVel.Length());
  183.     }
  184.  
  185.     #region Точки
  186.     /// <summary>
  187.     /// Кол-во точек в кривой.
  188.     /// </summary>
  189.     public int PointsCount
  190.     {
  191.         get { return mPoints.Count; }
  192.     }
  193.     /// <summary>
  194.     /// Добавить точку в конец кривой.
  195.     /// </summary>
  196.     /// <param name="P">Новая точка</param>
  197.     public void AddPoint(Vector2 P)
  198.     {
  199.         mPoints.Add(P);
  200.         RecalculateSegments();
  201.     }
  202.     /// <summary>
  203.     /// Добавить точку в произвольное место от 0 до (PointsCount - 1).
  204.     /// </summary>
  205.     /// <param name="Index">Индекс куда добавлять.</param>
  206.     /// <param name="P">Новая точка</param>
  207.     public void InsertPoint(int Index, Vector2 P)
  208.     {
  209.         mPoints.Insert(Index, P);
  210.         RecalculateSegments();
  211.     }
  212.     /// <summary>
  213.     /// Удалить точку по заданому индексу от 0 до (PointsCount - 1).
  214.     /// Последние две точки НЕ УДОЛЯТСЯ.
  215.     /// </summary>
  216.     /// <param name="Index">Индекс.</param>
  217.     public void RemovePoint(int Index)
  218.     {
  219.         if (mPoints.Count == 2) return;
  220.  
  221.         mPoints.RemoveAt(Index);
  222.         RecalculateSegments();
  223.     }
  224.     /// <summary>
  225.     /// Удолить все точки кроме первых двух.
  226.     /// </summary>
  227.     public void ClearPoints()
  228.     {
  229.         Vector2 P0 = mPoints[0];
  230.         Vector2 P1 = mPoints[1];
  231.  
  232.         mPoints.Clear();
  233.  
  234.         mPoints.Add(P0);
  235.         mPoints.Add(P1);
  236.  
  237.         RecalculateSegments();
  238.     }
  239.     /// <summary>
  240.     /// Удолить все точки, первые две точки станут заданными.
  241.     /// </summary>
  242.     /// <param name="P0">Первая.</param>
  243.     /// <param name="P1">Вторая.</param>
  244.     public void ClearPoints(Vector2 P0, Vector2 P1)
  245.     {
  246.         mPoints.Clear();
  247.  
  248.         mPoints.Add(P0);
  249.         mPoints.Add(P1);
  250.  
  251.         RecalculateSegments();
  252.     }
  253.     #endregion
  254. }
  255.  
  256. --------------------------------------------------------------------------------------------------------------------------------------
  257.  
  258. /// <summary>
  259. /// ХАВ ТУ ЮЗ? Прост)
  260. /// </summary>
  261. public TestCurve(Vector2 P0, Vector2 P1, float Vel)
  262. {
  263.     CatmullRomCurve CRC = new CatmullRomCurve(P0, P1);
  264.     CRC.InsertPoint(1, new Vector2(40, 0));
  265.  
  266.     Console.WriteLine("CurveLen: " + CRC.CurveLengthLin.ToString());
  267.     Console.WriteLine("CurveLen: " + CRC.CurveLength(0.1f).ToString() + " Step 0.1");
  268.     Console.WriteLine("CurveLen: " + CRC.CurveLength(0.05f).ToString() + " Step 0.05");
  269.  
  270.     float t = 0;
  271.     int InputKey = 0;
  272.     while (t < CRC.CurveLengthInSeg)
  273.     {
  274.         Console.WriteLine("t=" + t.ToString() + " V=" + CRC.Evalute(t).ToString());
  275.         t += CRC.EvaluteParamUniform(t, Vel);
  276.  
  277.         InputKey++;
  278.         if (InputKey > 30)
  279.         {
  280.             Console.ReadKey();
  281.             InputKey = 0;
  282.         }
  283.     }
  284. }
Advertisement
Add Comment
Please, Sign In to add comment