cardinal曲線工具類

因爲需要使用java根據點擊的關鍵點座標,生成平滑曲線圖片,從網上找了許久,只找到了一個js版的Cardinal曲線算法。沒辦法自己轉換成了Java版的。方便以後朋友們使用。
至於cardinal曲線是什麼,我沒學過高數,也不解釋了。反正這個工具類實現的效果就是:
根據你的關鍵座標點,生成一條連接關鍵座標點的平滑曲線,可以收尾相連組成環形。

package project.util;

import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * cardinal曲線生成類
 * @author aofavx
 */
public class CradinalLineUtil {

    public static void main(String[] args) {
        Float[] f=new Float[]{12.0f,14.1f,20.3f,30.4f,50.2f,50.3f,100.2f,80.4f};
        float slack=0.6f;
        boolean closed=true;
        Float[] news=cardinalSplinePoint(f,slack,closed);
        System.out.println(news.length);

    }

    /**
     * 
     * @param pts 關鍵座標集合[x1,y1,x2,y2,x3,y3...xn,yn]或 [y1,x1,y2,x2.....yn,xn]
     * @param slack 平滑度   0.0 ~ 1.0
     * @param closed 是否收尾閉合組成環形
     * @return
     */
    public static Float[] cardinalSplinePoint(Float pts[], float slack, boolean closed){
         Shape sb = cardinalSpline(pts,slack,closed);
            Area areab = new Area(sb);
            AffineTransform iterator = new AffineTransform();
            PathIterator p = areab.getPathIterator(iterator);
            List<Float> coords = new ArrayList<Float>();
            while (!p.isDone()) {
                float[] coord = new float[6];
                p.currentSegment(coord);
                for (int j = 0; j < coord.length; j++) {
                    if (coord[j] != 0.0) {
                        coords.add(coord[j]);
                    }
                }
                p.next();
            }
            return coords.toArray(new Float[]{});

    }

    /**
     * JS轉java的Cardinal畫線
     * @param pts  關鍵座標集合[x1,y1,x2,y2,x3,y3...xn,yn]或 [y1,x1,y2,x2.....yn,xn]
     * @param slack 平滑度   0.0 ~ 1.0
     * @param close 是否收尾閉合組成環形
     * @return
     */
    public static GeneralPath cardinalSpline(Float pts[], float slack, boolean closed) {
        GeneralPath path = new GeneralPath();
        path.moveTo(pts[0], pts[1]);
        return cardinalSpline(path, pts, slack, closed, 0f, 0f);
    }

    /**
     * 將生成平滑曲線進行xy方向平移tx,ty的距離
     * @param p 
     * @param pts 關鍵座標集合[x1,y1,x2,y2,x3,y3...xn,yn]
     * @param slack  平滑度   0.0 ~ 1.0
     * @param closed 是否收尾閉合組成環形
     * @param tx 
     * @param ty
     * @return
     */
    public static GeneralPath cardinalSpline(GeneralPath p, Float pts[], float slack, boolean closed, float tx, float ty){
        int npoints = 0;
        for ( ; npoints<pts.length; ++npoints )
            if ( Float.isNaN(pts[npoints]) ) break;
        return cardinalSpline(p, pts, 0, npoints/2, slack, closed, tx, ty);
    }

    public static GeneralPath cardinalSpline(GeneralPath p, Float pts[], int start, int npoints,float slack, boolean closed, float tx, float ty){
        // compute the size of the path
        int len = 2*npoints;
        int end = start+len;

        if ( len < 6 ) {
            throw new IllegalArgumentException(
                    "To create spline requires at least 3 points");
        }

        float dx1, dy1, dx2, dy2;

        // compute first control point
        if ( closed ) {
            dx2 = pts[start+2]-pts[end-2];
            dy2 = pts[start+3]-pts[end-1];
        } else {
            dx2 = pts[start+4]-pts[start];
            dy2 = pts[start+5]-pts[start+1];
        }

        // repeatedly compute next control point and append curve
        int i;
        for ( i=start+2; i<end-2; i+=2 ) {
            dx1 = dx2; dy1 = dy2;
            dx2 = pts[i+2]-pts[i-2];
            dy2 = pts[i+3]-pts[i-1];
            p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1,
                      tx+pts[i]  -slack*dx2, ty+pts[i+1]-slack*dy2,
                      tx+pts[i],             ty+pts[i+1]);
        }

        // compute last control point
        if ( closed ) {
            dx1 = dx2; dy1 = dy2;
            dx2 = pts[start]-pts[i-2];
            dy2 = pts[start+1]-pts[i-1];
            p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1,
                      tx+pts[i]  -slack*dx2, ty+pts[i+1]-slack*dy2,
                      tx+pts[i],             ty+pts[i+1]);

            dx1 = dx2; dy1 = dy2;
            dx2 = pts[start+2]-pts[end-2];
            dy2 = pts[start+3]-pts[end-1];
            p.curveTo(tx+pts[end-2]+slack*dx1, ty+pts[end-1]+slack*dy1,
                      tx+pts[0]    -slack*dx2, ty+pts[1]    -slack*dy2,
                      tx+pts[0],               ty+pts[1]);
            p.closePath();
        } else {
            p.curveTo(tx+pts[i-2]+slack*dx2, ty+pts[i-1]+slack*dy2,
                      tx+pts[i]  -slack*dx2, ty+pts[i+1]-slack*dy2,
                      tx+pts[i],             ty+pts[i+1]);
        }
        return p;
    }

    /**
     * JS轉java的Cardinal畫線
     * @param points  關鍵座標集合[x1,y1,x2,y2,x3,y3...xn,yn]或 [y1,x1,y2,x2.....yn,xn]
     * @param tension 平滑度   0.0 ~ 1.0
     * @param numOfSeg 兩個關鍵點之間取到的自動生成點各數
     * @param close 是否收尾閉合組成環形
     * @return
     */
    public static List curve(List<Float> points,Float tension,Integer numOfSeg,boolean close){
        if(tension==null){ tension=0.5f; }
        if(numOfSeg==null){numOfSeg=20;}
        List<Float> pts=new ArrayList<Float>();
        List<Float> res=new ArrayList<Float>();
        int length=points.size();
        float[] cache=new float[(numOfSeg+2)*4];
        //List cache=new ArrayList();
        int cachePtr=4;
//      Collections.copy(pts, points);  //將points中的數據拷貝到pts中
        for (Float f : points) {
            pts.add(f);
        }

        if(close){
            pts.add(0, points.get(length-1));
            pts.add(0,points.get(length-2));
            pts.add(points.get(0));
            pts.add(points.get(1));
        }else{
            pts.add(0, points.get(1));
            pts.add(0,points.get(0));
            pts.add(points.get(length-2));
            pts.add(points.get(length-1));
        }
        cache[0]=1;
        for (int i = 0; i < numOfSeg; i++) {
            float st=BigDecimal.valueOf((i*1.0) / numOfSeg).floatValue();
            float st2=st * st;
            float st3=st2 * st;
            float st23=st3 * 2;
            float st32=st2 * 3;

            cache[cachePtr++] = st23 - st32 + 1;    // c1
            cache[cachePtr++] = st32 - st23;        // c2
            cache[cachePtr++] = st3 - 2 * st2 + st; // c3
            cache[cachePtr++] = st3 - st2;          // c4       
        }
        cache[++cachePtr] = 1;
        parse(res,pts, cache,tension, length,numOfSeg);
        if(close){
            pts.clear();
            pts.add(points.get(length-4));
            pts.add(points.get(length-3));
            pts.add(points.get(length-2));
            pts.add(points.get(length-1));

            pts.add(points.get(0));
            pts.add(points.get(1));
            pts.add(points.get(2));
            pts.add(points.get(3));
            parse(res,pts, cache,tension, 4,numOfSeg);
        }
        return res;
    }

    private  static void parse(List res,List<Float> pts, float[] cache,float tension, int length,int numOfSeg) {
        for (int i = 2; i < length; i += 2) {
            float pt1 = pts.get(i);
            float   pt2 = pts.get(i+1);
            float   pt3 = pts.get(i+2);
            float   pt4 = pts.get(i+3);


            float t1x = (pt3 - pts.get(i-2)) * tension;
            float t1y = (pt4 - pts.get(i-1)) * tension;
            float t2x = (pts.get(i+4) - pt1) * tension;
            float t2y = (pts.get(i+5) - pt2) * tension;

            for (int t = 0; t < numOfSeg; t++) {
                int c=t*4;
                res.add(cache[c] * pt1 + cache[c+1] * pt3 + cache[c+2] * t1x + cache[c+3] * t2x);
                res.add( cache[c] * pt2 + cache[c+1] * pt4 + cache[c+2] * t1y + cache[c+3] * t2y);
            }
        }
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章