因爲需要使用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);
}
}
}
}