如何实现无第三方依赖的n点B-spline曲线插值功能
三次均匀B样条曲线实现(无第三方依赖)
你需要的效果为三次均匀B样条插值效果,以下实现直接使用已推导完成的固定基函数系数,无需你掌握额外的微积分知识,直接调用即可。
Python版本实现
def spline(x_points, y_points, step=0.01): # 输入:x_points、y_points为控制点坐标列表,step为插值点密度,值越小曲线越平滑 n = len(x_points) inter_points = [] # 控制点少于3个直接返回原坐标 if n < 3: return list(zip(x_points, y_points)) # 三次B样条固定基函数,为已推导完成的系数,直接使用即可 def calc_base(t): t2 = t * t t3 = t2 * t b0 = (-t3 + 3 * t2 - 3 * t + 1) / 6.0 b1 = (3 * t3 - 6 * t2 + 4) / 6.0 b2 = (-3 * t3 + 3 * t2 + 3 * t + 1) / 6.0 b3 = t3 / 6.0 return b0, b1, b2, b3 # 扩展首尾虚拟控制点,保证曲线经过所有输入的控制点 x_ext = [x_points[0]] * 2 + x_points + [x_points[-1]] * 2 y_ext = [y_points[0]] * 2 + y_points + [y_points[-1]] * 2 # 逐段计算插值点 for i in range(len(x_ext) - 3): t = 0.0 while t <= 1.0: b0, b1, b2, b3 = calc_base(t) x = b0 * x_ext[i] + b1 * x_ext[i+1] + b2 * x_ext[i+2] + b3 * x_ext[i+3] y = b0 * y_ext[i] + b1 * y_ext[i+1] + b2 * y_ext[i+2] + b3 * y_ext[i+3] inter_points.append((x, y)) t += step return inter_points # 调用示例 if __name__ == "__main__": # 替换为你自己的控制点坐标 x_list = [10, 50, 100, 150, 200] y_list = [20, 80, 30, 90, 40] result_points = spline(x_list, y_list) # 调用你自己的绘制函数逐点绘制即可 # for p in result_points: # draw_ellipse(p[0], p[1], 2, 2)
Java版本实现
import java.util.ArrayList; import java.util.List; public class SplineGenerator { public static class Point { public float x; public float y; public Point(float x, float y) { this.x = x; this.y = y; } } public static List<Point> spline(float[] xPoints, float[] yPoints, float step) { int n = xPoints.length; List<Point> interPoints = new ArrayList<>(); // 控制点少于3个直接返回原坐标 if (n < 3) { for (int i = 0; i < n; i++) { interPoints.add(new Point(xPoints[i], yPoints[i])); } return interPoints; } // 扩展首尾虚拟控制点,保证曲线经过所有输入的控制点 float[] xExt = new float[n + 4]; float[] yExt = new float[n + 4]; xExt[0] = xExt[1] = xPoints[0]; yExt[0] = yExt[1] = yPoints[0]; System.arraycopy(xPoints, 0, xExt, 2, n); System.arraycopy(yPoints, 0, yExt, 2, n); xExt[n+2] = xExt[n+3] = xPoints[n-1]; yExt[n+2] = yExt[n+3] = yPoints[n-1]; // 逐段计算插值点 for (int i = 0; i < xExt.length - 3; i++) { float t = 0f; while (t <= 1f) { float t2 = t * t; float t3 = t2 * t; float b0 = (-t3 + 3*t2 - 3*t + 1) / 6f; float b1 = (3*t3 - 6*t2 + 4) / 6f; float b2 = (-3*t3 + 3*t2 + 3*t + 1) / 6f; float b3 = t3 / 6f; float x = b0 * xExt[i] + b1 * xExt[i+1] + b2 * xExt[i+2] + b3 * xExt[i+3]; float y = b0 * yExt[i] + b1 * yExt[i+1] + b2 * yExt[i+2] + b3 * yExt[i+3]; interPoints.add(new Point(x, y)); t += step; } } return interPoints; } // 调用示例 public static void main(String[] args) { float[] x = {10, 50, 100, 150, 200}; float[] y = {20, 80, 30, 90, 40}; List<Point> resultPoints = spline(x, y, 0.01f); // 调用你自己的绘制函数逐点绘制即可 // for (Point p : resultPoints) { // draw_ellipse(p.x, p.y, 2, 2); // } } }
注意事项
- 所有代码均使用基础语法实现,没有依赖任何第三方库或封装好的样条相关函数
- step参数可根据需求调整:如果需要更高的绘制性能,可将step调大到0.05~0.1;如果需要更平滑的曲线效果,可调小到0.005
- 生成的插值点列表直接遍历调用你已有的点绘制函数,即可得到和示例一致的效果
内容的提问来源于stack exchange,提问作者molo32
相关产品推荐
相关产品推荐

