// 规范地讲 round时其实只需要一个半径 public static void Round(ref CornerInfo cornerInfo, Vector3 center, Vector3 arcPoint1, Vector3 arcPoint2) { cornerInfo.isRounded = true; cornerInfo.arcCenter = center; cornerInfo.arcPoint1 = arcPoint1; cornerInfo.arcPoint2 = arcPoint2; }
private static void AnglesToArcs(CornerRounderContext context) { PrepareRadiusInfo(context); CornerRounderConfig config = context.config; RadiusStrategy strategy = config.radiusStrategy; if (strategy == RadiusStrategy.Never) { return; } LinkedList <CornerInfo> cornerInfo = context.cornerInfo; LinkedListNode <CornerInfo> current = cornerInfo.First; while (current != null) { if (current.Previous != null && current.Next != null) { CornerInfo c0 = current.Previous.Value; CornerInfo c1 = current.Value; CornerInfo c2 = current.Next.Value; Vector3 p0 = c0.vertex; Vector3 p1 = c1.vertex; Vector3 p2 = c2.vertex; float radius = c1.radiusAdjusted; if (radius != 0) { float halfAngle = c1.angle * 0.5f; float tan = Mathf.Tan(halfAngle); float cutLen = radius / tan; // 截断点 Vector3 cut1 = p1 + (p0 - p1).normalized * cutLen; Vector3 cut2 = p1 + (p2 - p1).normalized * cutLen; // 顶点到圆心距离 float vertexToCenter = radius / Mathf.Sin(halfAngle); // 圆心 Vector3 center = ((cut1 - p1) + (cut2 - p1)).normalized * vertexToCenter + p1; CornerInfo value = current.Value; CornerInfo.Round(ref value, center, cut1, cut2); current.Value = value; } } current = current.Next; } }
public static void EnsureNeighbourInfo(ref CornerInfo first, ref CornerInfo second) { //if (first.toNext.dirty || second..toPrevious.dirty) { float dist = Vector3.Distance(first.vertex, second.vertex); first.toNext.distance = second.toPrevious.distance = dist; first.toNext.direction = second.vertex - first.vertex; second.toPrevious.direction = first.vertex - second.vertex; first.toNext.dirty = false; second.toPrevious.dirty = false; } }
private static bool RadiusAdjust(ref CornerInfo c0, ref CornerInfo c1, ref CornerInfo c2) { float radiusExpected = c1.radiusExpected; CornerInfo.EnsureNeighbourInfo(ref c0, ref c1); CornerInfo.EnsureNeighbourInfo(ref c1, ref c2); float angle = c1.angle; float tanHalf = Mathf.Tan(angle * 0.5f); float radiusAdjusted = Mathf.Min( radiusExpected, c1.toPrevious.distance * 0.5f * tanHalf, c1.toNext.distance * 0.5f * tanHalf); c1.radiusAdjusted = Mathf.Max(radiusAdjusted, 0); /* * * bool isEnough0 = c1.previous.distance > c0.radiusExpected + c1.radiusExpected; * bool isEnough2 = c1.next.distance > c2.radiusExpected + c1.radiusExpected; * * * // 两边的距离都充足 * if (isEnough0 && isEnough2) * { * c1.radiusAdjusted = c1.radiusExpected; * return false; * } * * float radiusCorner0 = (c0.radiusAdjusted == CornerInfo.Uninitiated) ? 0 : c0.radiusAdjusted; * float radiusCorner2 = (c2.radiusAdjusted == CornerInfo.Uninitiated) ? 0 : c2.radiusAdjusted; * * float radiusP = Mathf.Clamp(radiusExpected, 0, c1.previous.distance - radiusCorner0); * float radiusN = Mathf.Clamp(radiusExpected, 0, c1.next.distance - radiusCorner2); * * float radiusAdjusted = Mathf.Min(radiusP, radiusN); * c1.radiusAdjusted = radiusAdjusted; * c0.radiusAdjusted = (c0.radiusAdjusted == CornerInfo.Uninitiated) ? radiusAdjusted : c0.radiusAdjusted; * c2.radiusAdjusted = (c2.radiusAdjusted == CornerInfo.Uninitiated) ? radiusAdjusted : c2.radiusAdjusted; * */ return(false); }
private static void RemoveAcuteAngle(CornerRounderContext context) { CornerRounderConfig config = context.config; if (config.acuteAngleStrategy == AcuteAngleStrategy.Keep) { return; } LinkedList <CornerInfo> cornerInfo = context.cornerInfo; LinkedListNode <CornerInfo> current = cornerInfo.First; float acuteAngle = Mathf.Deg2Rad * config.acuteAngleThreshold; // 填充corner 同时填充angle while (current != null) { if (current.Previous == null || current.Next == null) { current = current.Next; continue; } CornerInfo corner = current.Value; corner.angle = MathUtils.Angle( current.Previous.Value.vertex, current.Value.vertex, current.Next.Value.vertex); current.Value = corner; if (corner.angle <= acuteAngle) { var toRemove = current; current = current.Previous; cornerInfo.Remove(toRemove); continue; } current = current.Next; } }
public static void Round(ref CornerInfo cornerInfo, float radius) { float angle = cornerInfo.angle; float halfAngle = angle * 0.5f; float tan = Mathf.Tan(halfAngle); float cutLen = radius / tan; /* * * // 截断点 * Vector3 cut1 = p1 + (p0 - p1).normalized * cutLen; * Vector3 cut2 = p1 + (p2 - p1).normalized * cutLen; * * // 顶点到圆心距离 * float vertexToCenter = radius / Mathf.Sin(halfAngle); * * // 圆心 * Vector3 center = ((cut1 - p1) + (cut2 - p1)).normalized * vertexToCenter + p1; * CornerInfo value = current.Value; * CornerInfo.Round(ref value, center, cut1, cut2); * */ }
public static void ArcToPoints(CornerInfo corner, float step, IList <Vector3> points) { Vector3 cut1 = corner.arcPoint1; Vector3 cut2 = corner.arcPoint2; Vector3 center = corner.arcCenter; Vector3 vec1 = cut1 - center; Vector3 vec2 = cut2 - center; step = Mathf.Clamp(step, 0.001f, 1); float t = 0; while (true) { points.Add(Vector3.Slerp(vec1, vec2, t) + center); t += step; if (t > 1) { points.Add(Vector3.Slerp(vec1, vec2, t) + center); break; } } }
private static void PrepareRadiusInfo(CornerRounderContext context) { CornerRounderConfig config = context.config; RadiusStrategy strategy = config.radiusStrategy; if (strategy == RadiusStrategy.Never) { return; } LinkedList <CornerInfo> cornerInfo = context.cornerInfo; LinkedListNode <CornerInfo> current = cornerInfo.First; while (current != null) { if (current.Previous != null && current.Next != null) { CornerInfo info = current.Value; float angle = MathUtils.Angle(current.Previous.Value, current.Value, current.Next.Value); info.angle = angle; switch (strategy) { case RadiusStrategy.Unified: info.radiusExpected = config.radiusTarget; break; case RadiusStrategy.Adaptive: info.radiusExpected = Mathf.Lerp(config.radiusMin, config.radiusMax, angle / 3.14f); break; } current.Value = info; } current = current.Next; } // 重置 再循环一遍 current = cornerInfo.First; while (current != null) { if (current.Previous != null && current.Next != null) { CornerInfo c0 = current.Previous.Value; CornerInfo c1 = current.Value; CornerInfo c2 = current.Next.Value; bool previousIsDirty = RadiusAdjust(ref c0, ref c1, ref c2); current.Previous.Value = c0; current.Value = c1; current.Next.Value = c2; if (previousIsDirty) { current = current.Previous; } } current = current.Next; } }