package com.axe.path;
import com.axe.core.Attribute;
import com.axe.io.InputModel;
import com.axe.io.OutputModel;
import com.axe.math.Scalari;
public class KramerPath<T> implements Path<T>
{
public static final float DEFAULT_LOOSENESS = 0.0575f;
protected Attribute<T>[] points;
protected Attribute<T> temp0;
protected Attribute<T> temp1;
protected int depth;
protected float looseness;
protected boolean loops;
public KramerPath()
{
}
public KramerPath( int depth, boolean loops, Attribute<T> ... points )
{
this( depth, loops, DEFAULT_LOOSENESS, points );
}
public KramerPath( int depth, boolean loops, float looseness, Attribute<T> ... points )
{
this.depth = depth;
this.loops = loops;
this.looseness = looseness;
this.points = points;
this.temp0 = points[0].create();
this.temp1 = points[0].create();
}
@Override
public T set(Attribute<T> subject, float delta)
{
final int n = points.length;
final float a = delta * n;
final int i = Scalari.clamp( (int)a, 0, n - 1 );
float d = a - i;
// v0 and v5 are used to calculate the next v1 or v4, at the next level.
T v0 = points[ getActualIndex( i - 2 ) ].get();
T v1 = points[ getActualIndex( i - 1 ) ].get();
T v2 = points[ getActualIndex( i ) ].get();
T v3 = points[ getActualIndex( i + 1 ) ].get();
T v4 = points[ getActualIndex( i + 2 ) ].get();
T v5 = points[ getActualIndex( i + 3 ) ].get();
for (int k = 0; k < depth; k++)
{
// Get mid point
T mid = getPoint( v1, v2, v3, v4 );
// If the desired point is closer to v2...
if (d < 0.5f)
{
// shift all surrounding points one-level closer to v2
T newEnd = v1;
v5 = v4;
v4 = v3;
v3 = mid;
v1 = getPoint( v0, v1, v2, v3 );
v0 = newEnd;
// adjust d so it's between 0.0 an 1.0
d = d * 2.0f;
}
// else, the desired point is closer to v3...
else
{
// shift all surrounding points one-level closer to v3
T newEnd = v4;
v0 = v1;
v1 = v2;
v2 = mid;
v4 = getPoint( v2, v3, v4, v5 );
v5 = newEnd;
// adjust d so it's between 0.0 an 1.0
d = (d - 0.5f) * 2.0f;
}
}
// subject = (v3 - v2) * d + v2
subject.interpolate( v2, v3, d );
return subject.get();
}
public T getPoint( T v1, T v2, T v3, T v4 )
{
// p = (0.5f + looseness) * (v2 + v3) - looseness * (v1 + v4)
temp0.set( v2 );
temp0.add( v3, 1f );
temp0.scale( 0.5f + looseness );
temp1.set( v1 );
temp1.add( v4, 1f );
temp0.add( temp1.get(), -looseness );
return temp0.clone();
}
public int getActualIndex( int index )
{
final int n = points.length;
return ( loops ? (index + n) % n : Scalari.clamp( index, 0, n - 1 ) );
}
@Override
public int getAttributeCount()
{
return points.length;
}
@Override
public Attribute<T> getAttribute(int index)
{
return points[ index ];
}
@Override
public T get(int index)
{
return points[ index ].get();
}
public Attribute<T>[] points()
{
return points;
}
@Override
public void read( InputModel input )
{
depth = input.readInt( "depth" );
loops = input.readBoolean( "loops" );
looseness = input.readFloat( "looseness" );
points = input.readModelArray( "point", "point-type" );
temp0 = points[0].create();
temp1 = points[0].create();
}
@Override
public void write( OutputModel output )
{
output.write( "depth", depth );
output.write( "loops", loops );
output.write( "looseness", looseness );
output.writeModelArray( "point", points, "point-type" );
}
}