/// Evolver Copyright 2000, Morgan McGuire
/// matrix@alum.mit.edu   http://graphics3d.com/matrix
public class Derivative extends OneArgumentOperator implements ELOperator
{
  private static final Blur blur = new Blur(0.015);

  public void apply(ELMatrix argument[], ELMatrix out, ELEnvironment environment)
  {
    checkArguments(argument);
    ELMatrix arg = argument[0];

    int width = out.getWidth();
    int height = out.getHeight();

    ELMatrix temp = ELMatrix.alloc(width, height);

    // Loop over all color channels, computing differences.
    for(int c = 0; c < 3; c++) {
      float inChannel[] = arg.channel[c];
      float outChannel[] = temp.channel[c];

      for(int y = height - 1; y >= 0; y--) {
        for(int x = width - 1; x >= 0; x--) {

          int x1 = (x + width - 1) % width;
          int z1 = ((y + height - 1) % height) * width;
          
          int x2 = x;
          int z2 = y * width;

          int x3 = (x + 1) % width;
          int z3 = ((y + 1) % height) * width;

          // Scale up to get more dramatic results
          float sum =        
            (-inChannel[x1 + z1] - inChannel[x2 + z1] 
             -inChannel[x1 + z2] + inChannel[x3 + z2] + 
             inChannel[x2 + z3] + inChannel[x3 + z3]) * 3.0f;

          if (sum < -1.0f) {
            sum = -1.0f;
          } else if (sum > 1.0f) {
            sum = 1.0f;
          }

          outChannel[x2 + z2] = sum;
        }
      }
    }

    // Blur the result slightly to make this more robust to scale
    // changes.
    blur.apply(new ELMatrix[]{temp}, out, environment);

  }

  public String name()
  {
    return "Derivative";
  }

  public String description()
  {
    return "Computes the derivative (gradient) of each channel independently.";
  }  

  protected String iconString()
  {
    return "dx";
  }  
}
