• 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));
        }
    }
  • 相关阅读:
    二叉查找树(二)
    白话红黑树系列之一——初识红黑树
    二叉查找树(三)
    白话红黑树系列之二——红黑树的构建
    二叉查找树(一)
    二叉查找树(五)
    选择实现—简单工厂
    嵌入式开发er的C语言能力自测(面试)题top 16
    AOP探索笔记
    浅谈.net插件式编程
  • 原文地址:https://www.cnblogs.com/chenss15060100790/p/16291590.html
Copyright © 2020-2023  润新知