OpenLayer4——贝塞尔曲线插值算法
主要涉及2阶贝塞尔曲线的应用,为了方便维护,牺牲了一部分性能,如果有能力可以进行调优。
功能描述:将尖锐的角变成圆角
使用场景:
比如,你手头有3个点,(0,0),(5,0),(5,5),连在一起显然是一条折线,通过贝塞尔曲线算法,可以算出(0,0)到(5,5)中间的折线,这些折线连起来看起来像是一个曲线。
当然,上面3个点,如果从(0,0)直接开始画曲线,画到(5,5),最终就变成了1/4圆,这可能不是很多人想要的,需求通常只是想要一个圆角,而不是一个圆,
因此,下面算法,允许从(0,0)到(5,0)选一个点作为起始点,开始绘制圆角。
JavaScript版本
/** * 贝塞尔曲线插值点 */ function BezierLine(){ //阶乘 function factorial(num) { if (num <= 1) { return 1; } else if(num > 5){ //减少递归 return num * (num - 1) * (num - 2) * (num - 3) * (num - 4) * factorial(num - 5); }else { return num * factorial(num - 1); } } /** * 生成贝塞尔曲线插值点 * @param n * @param arrPoints * @param res * @returns {Array} */ function createBezierCurvePoints(n, arrPoints, res) { var Ptx = 0, Pty = 0; //曲线精细程度参数 //这里写死0.2,取值越小,曲线越精细,产生5 (1/0.2)个计算结果,写0.01,则产生100个结果 for (var t = 0; t < 1; t = t + 0.2) { Ptx = 0; Pty = 0; for (var i = 0; i <= n; i++) { Ptx += (factorial(n) / (factorial(i) * factorial(n - i))) * Math.pow((1 - t), n - i) * Math.pow(t, i) * arrPoints[i][0]; Pty += (factorial(n) / (factorial(i) * factorial(n - i))) * Math.pow((1 - t), n - i) * Math.pow(t, i) * arrPoints[i][1]; } res.push([Ptx, Pty]); } } /** * * p1到p2取n等分,取第m个点 * * @param n, * @param m, * @param p1 * @param p2 */ function dividedPoint(n, m, p1, p2) { return [(p2[0] - p1[0])/n * m + p1[0], (p2[1] - p1[1])/n * m + p1[1]] } /** * 因为实际业务场景中,往往不需要从最开始的点绘制曲线,只需要把角变得圆滑即可 * * 比如: * 原始数据:(0,0),(5,0),(5,5) * 计算结果:(4,0),(5,0),(5,1) * 不需要从(0,0)点开始绘制曲线,只需要从(4,0)开始绘制曲线,结束于(5,1)点。 * * 当dividedPart为5时,取其中的五等分点,即可满足上述需求 * dividedPart值越大,平滑曲线越小,看起来越尖锐, */ var dividedPart = 5; /** * 2阶贝塞尔曲线插值点计算 * @param p1 * @param p2 * @param p3 * @param res */ function bezierCurvePoints3(p1, p2, p3, res) { //计算五分之4等分点 var point1 = dividedPoint(dividedPart,dividedPart -1 , p1, p2); var point2 = p2; //计算五分之1等分点 var point3 = dividedPoint(dividedPart,1, p2, p3); //开始点 res.push(point1); //计算曲线坐标点 createBezierCurvePoints(2, [point1, point2, point3], res); //结束点 res.push(point3); } return { getPolygonLine: function (Polygon) { var line = Polygon[0]; if(!Array.isArray(line)){ throw '地图数据不是数组!'; } else if(line.length < 3){ throw '图形点的数量不允许小于3:' + line; } else { var n = line.length - 2, res = []; for (var i = 0; i < n; i++) { bezierCurvePoints3(line[i], line[i + 1], line[i + 2], res); } //倒数第一个点 bezierCurvePoints3(line[n], line[0], line[1], res); return res; } } } }
Java版本
import com.sun.prism.impl.shape.ShapeUtil; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @Author Mr.css * @Date 2020/11/5 */ public class BezierLine { private static Logger log = LogManager.getLogger(ShapeUtil.class); private static GeometryFactory geometryFactory = new GeometryFactory(); private static int dividedPart = 5; /** * 阶乘 * * @param n 阶层数量 * @return 阶乘结果 */ private static double factorial(int n) { if (n == 2) { return n * (n - 1); } else if (n <= 1) { return 1; } else if (n > 5) { return n * (n - 1) * (n - 2) * (n - 3) * (n - 4) * factorial(n - 5); } else { return n * factorial(n - 1); } } /** * 生成n阶贝塞尔曲线插值点 * * @param n 阶乘数 * @param arrPoints 数组 * @param res 结果 */ private static void createBezierCurvePoints(int n, Coordinate[] arrPoints, List<Coordinate> res) { double Ptx = 0, Pty = 0; //常量0.2,取值越小,曲线越精细,产生5(1/0.2)个计算结果 for (double t = 0; t < 1; t = t + 0.2) { Ptx = 0; Pty = 0; for (int i = 0; i <= n; i++) { Ptx += (factorial(n) / (factorial(i) * factorial(n - i))) * Math.pow((1 - t), n - i) * Math.pow(t, i) * arrPoints[i].x; Pty += (factorial(n) / (factorial(i) * factorial(n - i))) * Math.pow((1 - t), n - i) * Math.pow(t, i) * arrPoints[i].y; } res.add(new Coordinate(Ptx, Pty)); } } /** * p1到p2取n等分,取第m个点 * * @param n 点p1 到 p2 做n等分, * @param m 取第m个等分点, * @param p1 点 p1 * @param p2 点 p2 */ private static Coordinate dividedPoint(double n, double m, Coordinate p1, Coordinate p2) { return new Coordinate((p2.x - p1.x) / n * m + p1.x, (p2.y - p1.y) / n * m + p1.y); } /** * 2阶贝塞尔曲线插值点计算 * * @param p1 * @param p2 * @param p3 * @param res */ private static List<Coordinate> bezierCurvePoints3(Coordinate p1, Coordinate p2, Coordinate p3, List<Coordinate> res) { //计算五分之4等分点 Coordinate point1 = dividedPoint(dividedPart, dividedPart - 1, p1, p2); Coordinate point2 = p2; //计算五分之1等分点 Coordinate point3 = dividedPoint(dividedPart, 1, p2, p3); //开始点 res.add(point1); //计算曲线坐标点 createBezierCurvePoints(2, new Coordinate[]{point1, point2, point3}, res); //结束点 res.add(point3); return res; } /** * 计算一个多边形平滑之后的曲线 * // * @param polygon 多边形 * @return 数组 */ public static Coordinate[] getLine(Coordinate[] line ) { if (line.length < 3) { log.error("图形点的数量不允许小于3:" + Arrays.toString(line)); return line; } else { int n = line.length - 2; List<Coordinate> list = new ArrayList<>(line.length * 5); for (int i = 0; i < n; i++) { bezierCurvePoints3(line[i], line[i + 1], line[i + 2], list); } //倒数第一个点 bezierCurvePoints3(line[n], line[0], line[1], list); list.add(list.get(0)); return list.toArray(new Coordinate[]{}); } } public static void main(String[] args) { Coordinate[] line = new Coordinate[]{ new Coordinate(1, 1), new Coordinate(1, 5), new Coordinate(5, 5), new Coordinate(5, 1), new Coordinate(1, 1) }; Coordinate[] res = getLine(line); Polygon polygon = geometryFactory.createPolygon(line); System.out.println(Arrays.toString(res)); } }
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!
分类:
openlayer4
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2020-05-20 Security——helloworld