public float GetDistance(DrawablePoint p) { float dx = p.X - X; float dy = p.Y - Y; return((float)Math.Sqrt(dx * dx + dy * dy)); }
// 곡선에 추가 포인트를 넣는 함수 // 세 점을 기준으로 각도가 t_angle보다 작으면 가운데 점의 좌우 t만큼의 지점에 추가로 Point를 넣는다. // 급격하게 선이 꺾일 때 두께가 이상하게 나오는 현상을 감소시킬 수 있다. private void GetSplitPoints(List <DrawablePoint> pts, float t, float t_angle) { int count = pts.Count; if (count < 3) { return; } float t2 = 1.0f - t; int i = 1; // 첫번째 가운데 점 while (i < pts.Count - 1) { float angle = GetAngle(pts[i - 1], pts[i], pts[i + 1]); if (angle < t_angle) { { // 앞쪽에 추가되는 컨트롤 포인트 DrawablePoint point12 = GetPoint12(pts[i - 1], t2); DrawablePoint point34 = GetPoint34(pts[i], t2); DrawablePoint splitPoint = GetSplitPoint(pts[i - 1], pts[i], t2); float d12 = splitPoint.GetDistance(pts[i - 1]); float d23 = splitPoint.GetDistance(pts[i]); float d123 = d12 + d23; float force = pts[i - 1].Force + (pts[i].Force - pts[i - 1].Force) * (d12 / d123); splitPoint.Force = force; pts[i - 1].SetOut(point12.X, point12.Y); pts.Insert(i, splitPoint); pts[i + 1].SetIn(point34.X, point34.Y); } { // 뒤쪽에 추가되는 컨트롤 포인트 DrawablePoint point12 = GetPoint12(pts[i + 1], t); DrawablePoint point34 = GetPoint34(pts[i + 2], t); DrawablePoint splitPoint = GetSplitPoint(pts[i + 1], pts[i + 2], t); float d12 = splitPoint.GetDistance(pts[i + 1]); float d23 = splitPoint.GetDistance(pts[i + 2]); float d123 = d12 + d23; float force = pts[i + 1].Force + (pts[i + 2].Force - pts[i + 1].Force) * (d12 / d123); splitPoint.Force = force; pts[i + 1].SetOut(point12.X, point12.Y); pts.Insert(i + 2, splitPoint); pts[i + 3].SetIn(point34.X, point34.Y); } i += 2; } else { i++; } } }
private DrawablePoint GetPoint34(DrawablePoint pts2, float t) { float x3 = pts2.InX; float y3 = pts2.InY; float x4 = pts2.X; float y4 = pts2.Y; float x34 = (x4 - x3) * t + x3; float y34 = (y4 - y3) * t + y3; return(new DrawablePoint(x34, y34)); }
// 이하 splitPoints를 얻기 위한 private 함수 private DrawablePoint GetPoint12(DrawablePoint pts1, float t) { float x1 = pts1.X; float y1 = pts1.Y; float x2 = pts1.OutX; float y2 = pts1.OutY; float x12 = (x2 - x1) * t + x1; float y12 = (y2 - y1) * t + y1; return(new DrawablePoint(x12, y12)); }
// 가까운 점을 제거하고 곡선이 아닌 라인상 거리를 재서 점을 없앨지 판단하는 로직 // O(n)으로 처리하기 위해 복잡한 계산을 피하고 그 대신 결과물이 완벽하지 않을 때도 있다. private int IsNear(DrawablePoint p1, DrawablePoint p2, DrawablePoint p3, float tp, float tl) { float l12 = p1.GetDistance(p2); float l13 = p1.GetDistance(p3); float l23 = p2.GetDistance(p3); if (l12 < tp) { return(2); } if (l13 < tp) { return(3); } if (l23 < tp) { return(2); } float prj = ((p3.X - p1.X) * (p2.X - p1.X) + (p3.Y - p1.Y) * (p2.Y - p1.Y)) / l12; float d = 0; if (prj < 0) { d = p1.GetDistance(p3); if (d < tp) { return(1); // remove p1 } } else if (prj > l12) { d = p2.GetDistance(p3); if (d < tp) { return(2); // remove p2 } } else { float area = Math.Abs((p1.X - p3.X) * (p2.Y - p3.Y) - (p1.Y - p3.Y) * (p2.X - p3.X)); d = area / l12; if (p1.GetDistance(p3) < tl || p2.GetDistance(p3) < tl || d < tl / 2) { return(3); // remove p3 } } return(0); }
// 세 점의 사이 각을 구하는 함수 // 최적화 필요 private float GetAngle(DrawablePoint p1, DrawablePoint p2, DrawablePoint p3) { float a, b, c; float angle, temp; a = (float)Math.Sqrt(Math.Pow(p1.X - p3.X, 2) + Math.Pow(p1.Y - p3.Y, 2)); b = (float)Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2)); c = (float)Math.Sqrt(Math.Pow(p2.X - p3.X, 2) + Math.Pow(p2.Y - p3.Y, 2)); temp = (float)(Math.Pow(b, 2) + Math.Pow(c, 2) - Math.Pow(a, 2)) / (2 * b * c); angle = (float)Math.Acos(temp); angle *= (float)(180 / Math.PI); return(angle); }
private DrawablePoint GetSplitPoint(DrawablePoint pts1, DrawablePoint pts2, float t) { float x1 = pts1.X; float y1 = pts1.Y; float x2 = pts1.OutX; float y2 = pts1.OutY; float x3 = pts2.InX; float y3 = pts2.InY; float x4 = pts2.X; float y4 = pts2.Y; float x12 = (x2 - x1) * t + x1; float y12 = (y2 - y1) * t + y1; float x23 = (x3 - x2) * t + x2; float y23 = (y3 - y2) * t + y2; float x34 = (x4 - x3) * t + x3; float y34 = (y4 - y3) * t + y3; float x123 = (x23 - x12) * t + x12; float y123 = (y23 - y12) * t + y12; float x234 = (x34 - x23) * t + x23; float y234 = (y34 - y23) * t + y23; float x1234 = (x234 - x123) * t + x123; float y1234 = (y234 - y123) * t + y123; DrawablePoint splitPoint = new DrawablePoint(x1234, y1234); splitPoint.SetIn(x123, y123); splitPoint.SetOut(x234, y234); return(splitPoint); }
private void GetControlPoint(List <DrawablePoint> p, int count, bool closePath, float factor, InterpolationType type = InterpolationType.Catmullrom) { DrawablePoint p0 = new DrawablePoint(); // prev point DrawablePoint p1 = new DrawablePoint(); // current point DrawablePoint p2 = new DrawablePoint(); // next point for (int i = 0; i < count; i++) { p1.Set(p[i]); if (i == 0) { if (closePath) { p0.Set(p[count - 1]); } else { p0.Set(p1); } } else { p0.Set(p[i - 1]); } if (i == count - 1) { if (closePath) { p2.Set(p[0]); } else { p2.Set(p1); } } else { p2.Set(p[i + 1]); } float d1 = p0.GetDistance(p1); float d2 = p1.GetDistance(p2); if (type == InterpolationType.Catmullrom) { float d1_a = (float)Math.Pow(d1, factor); // factor 기본 값 : 0.5 float d1_2a = d1_a * d1_a; float d2_a = (float)Math.Pow(d2, factor); float d2_2a = d2_a * d2_a; if (i != 0 || closePath) { float A = 2 * d2_2a + 3 * d2_a * d1_a + d1_2a; float N = 3 * d2_a * (d2_a + d1_a); if (N != 0) { p[i].SetIn((d2_2a * p0.X + A * p1.X - d1_2a * p2.X) / N, (d2_2a * p0.Y + A * p1.Y - d1_2a * p2.Y) / N); } else { p[i].SetIn(p1.X, p1.Y); } } else { p[i].SetIn(p1.X, p1.Y); } if (i != count - 1 || closePath) { float A = 2 * d1_2a + 3 * d1_a * d2_a + d2_2a; float N = 3 * d1_a * (d1_a + d2_a); if (N != 0) { p[i].SetOut((d1_2a * p2.X + A * p1.X - d2_2a * p0.X) / N, (d1_2a * p2.Y + A * p1.Y - d2_2a * p0.Y) / N); } else { p[i].SetOut(p1.X, p1.Y); } } else { p[i].SetOut(p1.X, p1.Y); } } else { float vx = p0.X - p2.X; float vy = p0.Y - p2.Y; float t = factor; // factor 기본값 : 0.4 float k = t * d1 / (d1 + d2); if (i != 0 || closePath) { p[i].SetIn(p1.X + vx * k, p1.Y + vy * k); } else { p[i].SetIn(p1.X, p1.Y); } if (i != count - 1 || closePath) { p[i].SetOut(p1.X + vx * (k - t), p1.Y + vy * (k - t)); } else { p[i].SetOut(p1.X, p1.Y); } } } }
public void Set(DrawablePoint p) { X = p.X; Y = p.Y; Force = p.Force; }
public DrawablePoint(DrawablePoint p, float scale) : this(p) { SetScale(scale); }
public DrawablePoint(DrawablePoint p) { X = p.X; Y = p.Y; Force = p.Force; }