// Curve.java package kovey.book99.geometry.curves; import java.awt.*; import kovey.book99.geometry.basicElements.*; import kovey.book99.matrix.*; import kovey.book99.util.IC; import java.io.FileWriter; import java.io.IOException; public abstract class Curve { VertexList cp = null; // control points int degree = 0; Color color = Color.magenta; public void setControlPoints( VertexList vertices ) { cp = vertices; } static public final int SPLINE = 0; static public final int PHANTOM = 1; static public final int CLOSED = 2; int curveType = SPLINE; public void setCurveType( int curveEnumValue ) throws java.lang.NoSuchFieldError { if ( curveEnumValue > CLOSED || curveEnumValue < SPLINE ) throw new java.lang.NoSuchFieldError( "No such curve type." ); curveType = curveEnumValue; } double uIncrement = 0.1; public void setIncrementU( double accuracy ) throws Curve.Error { if ( (accuracy >= 1) || (accuracy <= 0) ) throw new Error( "Accuracy is not within 0 and 1." ); uIncrement = accuracy; } boolean arcLength = false; public void enableArcLength( boolean enable ) { arcLength = enable; } boolean ease = false; public void enableEase( boolean enable ) { ease = enable; } int currCurve = 0; public VertexList getPoints() { // points that define the curve VertexList vertices = null; double accuracy = arcLength? 0.005 : uIncrement; // Sampling accuracy vertices = samplePoints( accuracy ); if ( arcLength ) { ArcLengthTable table = new ArcLengthTable( vertices, accuracy, this.getSegmentCount() ); vertices = postProcessForArcLength( table ); } return vertices; } protected VertexList samplePoints( double accuracy ) { // Get vertices at constant u. May not generate equal distances // between vertices. VertexList vertices = new VertexList(); int totalCurves = this.getSegmentCount(); currCurve = 0; while ( currCurve < totalCurves ) { boolean magenta = false; try { Matrix MP_x = getMatrixMP( Position.elementX ); Matrix MP_y = getMatrixMP( Position.elementY ); Matrix MP_z = getMatrixMP( Position.elementZ ); for ( double u = 0.0; u <= 1.0; u += accuracy ) { double realU = ease? IC.ease(u) : u; Matrix U = getMatrixU( realU ); Vertex v = new Vertex(); v.setRealX( getPointValue( U, MP_x ) ); v.setRealY( getPointValue( U, MP_y ) ); v.setRealZ( getPointValue( U, MP_z ) ); vertices.addTail( v ); } } catch( Throwable ee ) { System.out.println( "Unable to draw curve " + (currCurve+1) + " " + ee.getMessage() + "\n" ); ee.printStackTrace(); } currCurve++; } return vertices; } protected VertexList postProcessForArcLength( ArcLengthTable table ) { // Get equal distanced vertices. double curveLength = table.getTotalLength(); VertexList vertices = new VertexList(); int totalCurves = this.getSegmentCount(); currCurve = 0; while ( currCurve < totalCurves ) { boolean magenta = false; try { Matrix MP_x = getMatrixMP( Position.elementX ); Matrix MP_y = getMatrixMP( Position.elementY ); Matrix MP_z = getMatrixMP( Position.elementZ ); for ( double s = 0.0; s <= 1.0; s += uIncrement ) { double realS = ease? IC.ease(s) : s; Matrix U = getMatrixU( table.getU( curveLength*realS ) ); Vertex v = new Vertex(); v.setRealX( getPointValue( U, MP_x ) ); v.setRealY( getPointValue( U, MP_y ) ); v.setRealZ( getPointValue( U, MP_z ) ); vertices.addTail( v ); } } catch( Throwable ee ) { System.out.println( "Unable to draw curve " + (currCurve+1) + " " + ee.getMessage() + "\n" ); ee.printStackTrace(); } currCurve++; } return vertices; } /** * Get the number of curves to draw. */ abstract protected int getSegmentCount(); /** * Returns the value Pi(u), where Pi(u) = UMP. */ protected double getPointValue( Matrix U, Matrix MP ) throws Throwable { int currRow = MP.getRowCount(); int order = (degree+1); if ( order != currRow ) throw new Error( "Order doesn't match MP Matrix row count." ); Matrix result = U.multiply( MP ); return result.getElement( 0, 0 ); } /** * Returns the MP matrix, where Pi(u) = UMP. */ protected Matrix getMatrixMP( byte elementType ) throws Throwable { Matrix M = this.getBasisMatrix(); Matrix P = this.getControlPointMatrix( elementType ); return M.multiply( P ); } /** * Returns the Matrix U, where Pi(u) = UMP. */ protected Matrix getMatrixU( double u ) throws Throwable { double tmp; int order = (degree + 1); Matrix U = new Matrix( 1, order ); int currPower = degree; for ( int col = 0; col < order; col++ ) { tmp = 1; for ( int idx = 0; idx < currPower; idx++ ) tmp *= u; U.setElement( 0, col, tmp ); currPower--; } return U; } /** * Returns the matrix M for the B-Spline form Pi(u) = UMP. */ abstract protected Matrix getBasisMatrix(); /** * Returns the matrix M for the B-Spline form Pi(u) = UMP. */ abstract protected Matrix getControlPointMatrix( byte elementType ) throws Throwable; protected class ArcLengthTable { double table[]; int TABLE_MAX_ELEM=0; double deltaU; int segmentCount=0; public ArcLengthTable( VertexList samples, double uIncrement, int totalCurves ) { TABLE_MAX_ELEM = samples.getCount(); table = new double[ TABLE_MAX_ELEM ]; deltaU = uIncrement; segmentCount = totalCurves; setupTable( samples ); } protected void setupTable( VertexList samples ) { table[0] = 0.0; Vertex prev = samples.getAt(0); Vertex delta = new Vertex(0,0,0); for ( int idx=1; idx < TABLE_MAX_ELEM; idx++ ) { Vertex curr = samples.getAt(idx); delta.setRealX( curr.getRealX() - prev.getRealX() ); delta.setRealY( curr.getRealY() - prev.getRealY() ); delta.setRealZ( curr.getRealZ() - prev.getRealZ() ); // take the linear distance double distance = Math.sqrt( delta.getRealX()*delta.getRealX() + delta.getRealY()*delta.getRealY() + delta.getRealZ()*delta.getRealZ() ); table[idx] = table[idx-1] + distance; prev = curr; try { file.write( "Table[" + idx + "] = " + table[idx] + ".\n" ); } catch( IOException e ) {} } try { file.close(); } catch( IOException e ) {} } public double getTotalLength() { return table[TABLE_MAX_ELEM-1]; } public double getU( double length ) { double u = 0.0; FileWriter file = null; try { file = new FileWriter( "ALT_getU.txt", true ); } catch( IOException e ) {} if ( 0 != length ) { // Find the closest length from the table. int idx=1; boolean found = false; double fractionU = 0.0; while ( (idx < TABLE_MAX_ELEM) && (! found) ) { if ( table[idx] > length ) { found = true; fractionU = (length - table[idx-1]); } else if ( table[idx] == length ) found = true; else idx++; } if ( 0.0 != fractionU ) fractionU /= getTotalLength(); u = ((idx*deltaU) + fractionU)/segmentCount; try { file.write( "[" + length + "] = " + u + ". index = " + idx + " deltaU = " + deltaU + ".\n" ); file.close(); } catch( IOException e ) {} } return u; } } public class Error extends Throwable { public Error( String message ) { super( message ); } } }