/// <summary> /// ベジエ曲線の長さを得る /// </summary> /// <param name="bcp1">一つ目のベジエ頂点</param> /// <param name="bcp2">二つ目のベジエ頂点</param> /// <returns></returns> public static float GetBezeierLength(BezierControlPoint bcp1, BezierControlPoint bcp2) { if (bcp1.ValidThird) { if (!bcp2.ValidFirst) { //second bezier return(GetBezeierLength(bcp1.Second, bcp1.Third, bcp2.Second, bcp2.Second)); } else { //third bezier return(GetBezeierLength(bcp1.Second, bcp1.Third, bcp2.First, bcp2.Second)); } } else { if (!bcp2.ValidFirst) { //first bezier return(GetLength(new PointF(bcp1.Second.X - bcp2.Second.X, bcp1.Second.Y - bcp2.Second.Y))); } else { //second bezier return(GetBezeierLength(bcp1.Second, bcp1.Second, bcp2.First, bcp2.Second)); } } }
/// <summary> /// 指定された割合分割されたベジエを得るメソッド /// </summary> /// <param name="num">最大、最小はMaxRatio,MinRatioから</param> /// <param name="bcps1">分割1</param> /// <param name="bcps2">分割2</param> public void GetDevidedBeziers(float num, out BezierControlPoint[] bcps1, out BezierControlPoint[] bcps2) { bcps1 = null; bcps2 = null; if (bcps == null || bcps.Length == 0) { return; } if (num < MinRatio) { num = MinRatio; } else if (num > MaxRatio) { num = MaxRatio; } //find where float sumnum = 0; float rest = 0; BezierControlPoint previous = null, next = null; int foundindex = -1; for (int i = 0; i < bcps.Length; i++) { BezierControlPoint bcp = bcps[i]; if (previous != null) { float ratio = eachlength[i - 1] / length * MaxRatio; if (sumnum + ratio >= num || (sumnum + ratio) >= MaxRatio - 0.0009765625) { next = bcp; rest = num - sumnum; foundindex = i; break; } sumnum += ratio; } previous = bcp; } if (foundindex != -1) { float targetratio = 1 - rest / MaxRatio * length / eachlength[foundindex - 1]; BezierCaliculate.GetDevidedBeziers(previous, next, targetratio, out BezierControlPoint bcp1, out BezierControlPoint bcp2, out BezierControlPoint bcp3); bcps1 = new BezierControlPoint[foundindex + 1]; bcps2 = new BezierControlPoint[BCPS.Length - foundindex + 1]; for (int i = 0; i < foundindex - 1; i++) { bcps1[i] = BCPS[i].Clone(); } bcps1[foundindex - 1] = bcp1; bcps1[foundindex] = bcp2.Clone(); bcps2[0] = bcp2; bcps2[1] = bcp3; for (int i = foundindex + 1; i < BCPS.Length; i++) { bcps2[i - foundindex + 1] = BCPS[i].Clone(); } return; } }
private void Analyze() { eachlength = new float[bcps.Length - 1]; BezierControlPoint previous = null; for (int i = 0; i < bcps.Length; i++) { BezierControlPoint bcp = bcps[i]; if (previous != null) { eachlength[i - 1] += BezierCaliculate.GetBezeierLength(previous, bcp); length += eachlength[i - 1]; } previous = bcp; } }
/// <summary> /// クローンメソッド /// </summary> /// <returns></returns> public BezierControlPoint Clone() { var bcp = new BezierControlPoint { FirstDirection = FirstDirection, FirstLength = FirstLength, Second = Second, ThirdDirection = ThirdDirection, ThirdLength = ThirdLength, ValidFirst = ValidFirst, ValidSecond = ValidSecond, ValidThird = ValidThird }; return(bcp); }
/// <summary> /// 連結ベジエから特定の割合の場所を得るメソッド /// </summary> /// <param name="num">最大、最小はMaxRatio,MinRatioから</param> /// <param name="direction">位置</param> /// <param name="pos">向き</param> /// <returns></returns> public void GetPoint(float num, out PointF pos, out PointF direction) { pos = PointF.Empty; direction = new PointF(1, 0); if (bcps == null || bcps.Length == 0) { return; } if (num < MinRatio) { num = MinRatio; } else if (num > MaxRatio) { num = MaxRatio; } //find where float sumnum = 0; float rest = 0; BezierControlPoint previous = null, next = null; int foundindex = -1; for (int i = 0; i < bcps.Length; i++) { BezierControlPoint bcp = bcps[i]; if (previous != null) { float ratio = eachlength[i - 1] / length * MaxRatio; if (sumnum + ratio >= num || (sumnum + ratio) >= MaxRatio - 0.0009765625) { next = bcp; rest = num - sumnum; foundindex = i; break; } sumnum += ratio; } previous = bcp; } if (foundindex != -1) { float targetlength = length * rest / MaxRatio; BezierCaliculate.GetBezeirSplitPoint(previous, next, targetlength, out pos, out direction); return; } }
/// <summary> /// 分割されたベジエを得る /// </summary> /// <param name="bcp1">ベジエ頂点1</param> /// <param name="bcp2">ベジエ頂点2</param> /// <param name="t">割合(0~1)</param> /// <param name="obcp1">出力ベジエ頂点1</param> /// <param name="obcp2">出力ベジエ頂点2</param> /// <param name="obcp3">出力ベジエ頂点3</param> public static void GetDevidedBeziers(BezierControlPoint bcp1, BezierControlPoint bcp2, float t, out BezierControlPoint obcp1, out BezierControlPoint obcp2, out BezierControlPoint obcp3) { PointF p1, p2, p3, p4; p1 = bcp1.Second; if (bcp1.ValidThird) { p2 = bcp1.Third; } else { p2 = bcp1.Second; } if (bcp2.ValidFirst) { p3 = bcp2.First; } else { p3 = bcp2.Second; } p4 = bcp2.Second; GetFivePoint(p1, p2, p3, p4, out PointF outp1, out PointF outp2, out PointF outp3, out PointF outp4, out PointF outp5, t); obcp1 = bcp1.Clone(); obcp2 = new BezierControlPoint(); obcp3 = bcp2.Clone(); if (obcp1.ValidThird) { obcp1.Third = outp1; } obcp2.Second = outp3; obcp2.First = outp2; obcp2.Third = outp4; obcp2.ValidFirst = true; obcp2.ValidThird = true; if (obcp3.ValidFirst) { obcp3.First = outp5; } }
/// <summary> /// ベジエ曲線を囲む部分を得る /// </summary> /// <param name="bcp1">ベジエ頂点1</param> /// <param name="bcp2">ベジエ頂点2</param> /// <param name="minx">最小のx</param> /// <param name="maxx">最大のx</param> /// <param name="miny">最小のy</param> /// <param name="maxy">最大のy</param> public static void GetArea(BezierControlPoint bcp1, BezierControlPoint bcp2, out float minx, out float maxx, out float miny, out float maxy) { if (bcp1.ValidThird) { if (!bcp2.ValidFirst) { //second bezier GetArea(bcp1.Second, bcp1.Third, bcp2.Second, bcp2.Second, out minx, out maxx, out miny, out maxy); return; } else { //third bezier GetArea(bcp1.Second, bcp1.Third, bcp2.First, bcp2.Second, out minx, out maxx, out miny, out maxy); return; } } else { if (!bcp2.ValidFirst) { //first bezier maxx = Math.Max(bcp1.Second.X, bcp2.Second.X); minx = Math.Min(bcp1.Second.X, bcp2.Second.X); maxy = Math.Max(bcp1.Second.Y, bcp2.Second.Y); miny = Math.Min(bcp1.Second.Y, bcp2.Second.Y); return; } else { //second bezier GetArea(bcp1.Second, bcp1.Second, bcp2.First, bcp2.Second, out minx, out maxx, out miny, out maxy); return; } } }
/// <summary> /// ベジエ頂点の分割位置と角度を得る /// </summary> /// <param name="bcp1">ベジエ頂点1</param> /// <param name="bcp2">ベジエ頂点2</param> /// <param name="length">欲しい長さ</param> /// <param name="pos">位置</param> /// <param name="direction">向き</param> public static void GetBezeirSplitPoint(BezierControlPoint bcp1, BezierControlPoint bcp2, float length, out PointF pos, out PointF direction) { if (bcp1.ValidThird) { if (!bcp2.ValidFirst) { //second bezier GetBezeirSplitPoint(bcp1.Second, bcp1.Third, bcp2.Second, bcp2.Second, length, out pos, out direction); return; } else { //third bezier GetBezeirSplitPoint(bcp1.Second, bcp1.Third, bcp2.First, bcp2.Second, length, out pos, out direction); return; } } else { if (!bcp2.ValidFirst) { //first bezier var nv = GetNormalizePoint(new PointF(bcp2.Second.X - bcp1.Second.X, bcp2.Second.Y - bcp1.Second.Y)); var vec = new PointF(nv.X * length, nv.Y * length); pos = new PointF(bcp1.Second.X + vec.X, bcp1.Second.Y + vec.Y); direction = nv; return; } else { //second bezier GetBezeirSplitPoint(bcp1.Second, bcp1.Second, bcp2.First, bcp2.Second, length, out pos, out direction); return; } } }
/// <summary> /// デシリアライズメソッド /// </summary> /// <param name="str"></param> /// <returns></returns> public static BezierControlPoint Deserialize(string str) { var ret = new BezierControlPoint(); var fd = new PointF(1, 0); var sec = new PointF(0, 0); var td = new PointF(1, 0); foreach (string st in str.Split(' ')) { var content = st.Split('='); if (content.Length != 2) { throw new ArgumentException("Incorrect Format:" + st); } switch (content[0]) { case "VF": ret.ValidFirst = content[1] == "1"; break; case "VS": ret.ValidSecond = content[1] == "1"; break; case "VT": ret.ValidThird = content[1] == "1"; break; case "FDX": fd.X = float.Parse(content[1], CultureInfo.InvariantCulture); break; case "FDY": fd.Y = float.Parse(content[1], CultureInfo.InvariantCulture); break; case "SX": sec.X = float.Parse(content[1], CultureInfo.InvariantCulture); break; case "SY": sec.Y = float.Parse(content[1], CultureInfo.InvariantCulture); break; case "TDX": td.X = float.Parse(content[1], CultureInfo.InvariantCulture); break; case "TDY": td.Y = float.Parse(content[1], CultureInfo.InvariantCulture); break; case "FL": ret.FirstLength = float.Parse(content[1], CultureInfo.InvariantCulture); break; case "TL": ret.ThirdLength = float.Parse(content[1], CultureInfo.InvariantCulture); break; default: throw new ArgumentException("Incorrect Argument:" + content[0]); } } ret.FirstDirection = fd; ret.Second = sec; ret.ThirdDirection = td; return(ret); }