private static void MergeClosePoints(IList <Vector3> source, CornerRounderContext context) { CornerRounderConfig config = context.config; PointMergeStrategy strategy = config.closePointMergeStrategy; float threshold = config.mergeThreshold; LinkedList <CornerInfo> cornerInfo = context.cornerInfo; if (source.Count <= 2 || strategy == PointMergeStrategy.Never) { return; } float thresholdSqr = threshold * threshold; switch (strategy) { case PointMergeStrategy.Forward: MergeForward(cornerInfo, thresholdSqr); break; case PointMergeStrategy.MiddlePoint: MergeMiddlePoint(cornerInfo, thresholdSqr); break; case PointMergeStrategy.Backward: MergeBackward(cornerInfo, thresholdSqr); break; case PointMergeStrategy.LeastMove: MergeLeastMove(cornerInfo, thresholdSqr); break; } }
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 RoundCorner(IList <Vector3> source, IList <Vector3> target, CornerRounderConfig config) { CornerRounderContext context = new CornerRounderContext(config, source); RemoveAcuteAngle(context); MergeClosePoints(source, context); AnglesToArcs(context); target.Clear(); ArcsToPoints(context, target); }
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; } }
private static void ArcsToPoints(CornerRounderContext context, IList <Vector3> target) { CornerRounderConfig config = context.config; ResolutionStrategy strategy = config.cornerResolutionStrategy; LinkedList <CornerInfo> cornerInfo = context.cornerInfo; float stepFactor = 1.0f / config.cornerResolution; target.Clear(); foreach (var corner in cornerInfo) { if (!corner.isRounded) { target.Add(corner.vertex); } else { switch (strategy) { case ResolutionStrategy.Directly: target.Add(corner.arcPoint1); target.Add(corner.arcPoint2); break; case ResolutionStrategy.ByAngle: MathUtils.ArcToPoints(corner, stepFactor, target); break; case ResolutionStrategy.ByArc: MathUtils.ArcToPoints(corner, stepFactor * (corner.arcCenter - corner.arcPoint1).magnitude, target); 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; } }