/* * Copyright (C) 2013-2015 F(X)yz, * Sean Phillips, Jason Pollastrini and Jose Pereda * All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.fxyz.shapes.primitives.helper; import java.util.ArrayList; import java.util.List; import org.fxyz.geometry.Point3D; /** Given N 3D points (knots), N-1 Bezier cubic curves will be generated, * passing through these knots, by adding 2(N-1) control points * Conditions if spline is open: * - C2 in all the inner knots (2 ecuations x N-2 knots) * - On external knots r''=0 (2 ecuations) * Conditions if spline is closed (knot[0]==knot[N]: * - C2 in all the inner knots (2 ecuations x N-2 knots) * Tridiagonal linear system -> Thomas algorithm * See: http://www.particleincell.com/blog/2012/bezier-splines/ * * @author jpereda */ public class InterpolateBezier { private final List<Point3D> knots; private final int numSplines; private final boolean isClosed; private final List<BezierHelper> splines; public InterpolateBezier(List<Point3D> knots){ this.knots=knots; numSplines = knots.size()-1; isClosed=knots.get(0).equals(knots.get(numSplines)); splines = new ArrayList<>(numSplines); calculateControlPoints(); } public List<BezierHelper> getSplines() { return splines; } private void calculateControlPoints(){ if(isClosed){ List<Point3D> p1=new ArrayList<>(numSplines); for(int j=0; j<numSplines; j++){ p1.add(knots.get(j+1).substract(j==0?knots.get(numSplines-1):knots.get(j-1)).multiply(1/3f).add(knots.get(j))); } for(int j=0; j<numSplines; j++){ Point3D p2=j==numSplines-1?knots.get(0).multiply(2f).substract(p1.get(0)): knots.get(j+1).multiply(2f).substract(p1.get(j+1)); splines.add(new BezierHelper(knots.get(j),p1.get(j),p2,knots.get(j+1))); } } else { for(int j=0; j<numSplines; j++){ splines.add(new BezierHelper(knots.get(j),new Point3D(0f, 0f, 0f),new Point3D(0f, 0f, 0f),knots.get(j+1))); } for(int i=0; i<3; i++){ double[] p1=new double[numSplines]; double[] p2=new double[numSplines]; double[] ca=new double[numSplines]; double[] cb=new double[numSplines]; double[] cc=new double[numSplines]; double[] cr=new double[numSplines]; cb[0] = 2d; cc[0] = 1d; cr[0] = i==0?knots.get(0).x + 2d*knots.get(1).x:i==1?knots.get(0).y + 2d*knots.get(1).y:knots.get(0).z + 2d*knots.get(1).z; for(int j=1; j<numSplines-1; j++){ ca[j]=1d; cb[j]=4d; cc[j]=1d; cr[j]=i==0?4d*knots.get(j).x + 2d*knots.get(j+1).x:i==1?4d*knots.get(j).y + 2d*knots.get(j+1).y:4d*knots.get(j).z + 2d*knots.get(j+1).z; } ca[numSplines-1]=2d; cb[numSplines-1]=7d; cr[numSplines-1]=i==0?8d*knots.get(numSplines-1).x + knots.get(numSplines).x:i==1?8d*knots.get(numSplines-1).y + knots.get(numSplines).y:8d*knots.get(numSplines-1).z + knots.get(numSplines).z; for(int j = 1; j<numSplines; j++){ double m=ca[j]/cb[j-1]; cb[j]-=m*cc[j-1]; cr[j]-=m*cr[j-1]; } p1[numSplines-1]=cr[numSplines-1]/cb[numSplines-1]; for(int j=numSplines-2; j>=0; j--){ p1[j] = (cr[j]-cc[j]*p1[j+1])/cb[j]; } for(int j=0; j<numSplines-1; j++){ p2[j]=(i==0?2d*knots.get(j+1).x:i==1?2d*knots.get(j+1).y:2d*knots.get(j+1).z)-p1[j+1]; } p2[numSplines-1]=((i==0?knots.get(numSplines).x:i==1?knots.get(numSplines).y:knots.get(numSplines).z)+p1[numSplines-1])/2d; for(int j=0; j<numSplines; j++){ BezierHelper bez = splines.get(j); if(i==0){ bez.getPoints().get(1).x=(float)p1[j]; bez.getPoints().get(2).x=(float)p2[j]; } else if(i==1){ bez.getPoints().get(1).y=(float)p1[j]; bez.getPoints().get(2).y=(float)p2[j]; } else if(i==2){ bez.getPoints().get(1).z=(float)p1[j]; bez.getPoints().get(2).z=(float)p2[j]; } splines.set(j,bez); } } } // splines.forEach(System.out::println); } }