1. package com.axe.path;
  2.  
  3. import com.axe.core.Attribute;
  4. import com.axe.io.InputModel;
  5. import com.axe.io.OutputModel;
  6. import com.axe.math.Scalari;
  7.  
  8. public class KramerPath<T> implements Path<T>
  9. {
  10.    
  11.     public static final float DEFAULT_LOOSENESS = 0.0575f;
  12.    
  13.     protected Attribute<T>[] points;
  14.     protected Attribute<T> temp0;
  15.     protected Attribute<T> temp1;
  16.     protected int depth;
  17.     protected float looseness;
  18.     protected boolean loops;
  19.    
  20.     public KramerPath()
  21.     {
  22.     }
  23.    
  24.     public KramerPath( int depth, boolean loops, Attribute<T> ... points )
  25.     {
  26.         this( depth, loops, DEFAULT_LOOSENESS, points );
  27.     }
  28.    
  29.     public KramerPath( int depth, boolean loops, float looseness, Attribute<T> ... points )
  30.     {
  31.         this.depth = depth;
  32.         this.loops = loops;
  33.         this.looseness = looseness;
  34.         this.points = points;
  35.         this.temp0 = points[0].create();
  36.         this.temp1 = points[0].create();
  37.     }
  38.    
  39.     @Override
  40.     public T set(Attribute<T> subject, float delta)
  41.     {
  42.         final int n = points.length;
  43.         final float a = delta * n;
  44.         final int i = Scalari.clamp( (int)a, 0, n - 1 );
  45.         float d = a - i;
  46.        
  47.         // v0 and v5 are used to calculate the next v1 or v4, at the next level.
  48.         T v0 = points[ getActualIndex( i - 2 ) ].get();
  49.         T v1 = points[ getActualIndex( i - 1 ) ].get();
  50.         T v2 = points[ getActualIndex( i ) ].get();
  51.         T v3 = points[ getActualIndex( i + 1 ) ].get();
  52.         T v4 = points[ getActualIndex( i + 2 ) ].get();
  53.         T v5 = points[ getActualIndex( i + 3 ) ].get();
  54.        
  55.         for (int k = 0; k < depth; k++)
  56.         {
  57.             // Get mid point
  58.             T mid = getPoint( v1, v2, v3, v4 );
  59.            
  60.             // If the desired point is closer to v2...
  61.             if (d < 0.5f)
  62.             {
  63.                 // shift all surrounding points one-level closer to v2
  64.                 T newEnd = v1;
  65.                 v5 = v4;
  66.                 v4 = v3;
  67.                 v3 = mid;
  68.                 v1 = getPoint( v0, v1, v2, v3 );
  69.                 v0 = newEnd;
  70.                 // adjust d so it's between 0.0 an 1.0
  71.                 d = d * 2.0f;
  72.             }
  73.             // else, the desired point is closer to v3...
  74.             else
  75.             {
  76.                 // shift all surrounding points one-level closer to v3
  77.                 T newEnd = v4;
  78.                 v0 = v1;
  79.                 v1 = v2;
  80.                 v2 = mid;
  81.                 v4 = getPoint( v2, v3, v4, v5 );
  82.                 v5 = newEnd;
  83.                 // adjust d so it's between 0.0 an 1.0
  84.                 d = (d - 0.5f) * 2.0f;
  85.             }
  86.         }
  87.        
  88.         // subject = (v3 - v2) * d + v2
  89.         subject.interpolate( v2, v3, d );
  90.        
  91.         return subject.get();
  92.     }
  93.    
  94.     public T getPoint( T v1, T v2, T v3, T v4 )
  95.     {
  96.         // p = (0.5f + looseness) * (v2 + v3) - looseness * (v1 + v4)
  97.        
  98.         temp0.set( v2 );
  99.         temp0.add( v3, 1f );
  100.         temp0.scale( 0.5f + looseness );
  101.        
  102.         temp1.set( v1 );
  103.         temp1.add( v4, 1f );
  104.         temp0.add( temp1.get(), -looseness );
  105.        
  106.         return temp0.clone();
  107.     }
  108.    
  109.     public int getActualIndex( int index )
  110.     {
  111.         final int n = points.length;
  112.        
  113.         return ( loops ? (index + n) % n : Scalari.clamp( index, 0, n - 1 ) );
  114.     }
  115.  
  116.     @Override
  117.     public int getAttributeCount()
  118.     {
  119.         return points.length;
  120.     }
  121.  
  122.     @Override
  123.     public Attribute<T> getAttribute(int index)
  124.     {
  125.         return points[ index ];
  126.     }
  127.  
  128.     @Override
  129.     public T get(int index)
  130.     {
  131.         return points[ index ].get();
  132.     }
  133.    
  134.     public Attribute<T>[] points()
  135.     {
  136.         return points;
  137.     }
  138.  
  139.     @Override
  140.     public void read( InputModel input )
  141.     {
  142.         depth = input.readInt( "depth" );
  143.         loops = input.readBoolean( "loops" );
  144.         looseness = input.readFloat( "looseness" );
  145.         points = input.readModelArray( "point", "point-type" );
  146.         temp0 = points[0].create();
  147.         temp1 = points[0].create();
  148.     }
  149.  
  150.     @Override
  151.     public void write( OutputModel output )
  152.     {
  153.         output.write( "depth", depth );
  154.         output.write( "loops", loops );
  155.         output.write( "looseness", looseness );
  156.         output.writeModelArray( "point", points, "point-type" );
  157.     }
  158.    
  159. }