addBall method

void addBall(
  1. double ballx,
  2. double bally,
  3. double ballz,
  4. double strength,
  5. int subtract, [
  6. dynamic colors,
])

////////////////////////////////// //////////////////////////////////

Implementation

// Metaballs
/////////////////////////////////////

// Adds a reciprocal ball (nice and blobby) that, to be fast, fades to zero after
// a fixed distance, determined by strength and subtract.

void addBall(double ballx,double bally,double ballz, double strength,int subtract, [dynamic colors]) {
  final sign = strength.sign;
  strength = strength.abs();
  bool userDefineColor = colors != null;
  Color ballColor = Color( ballx, bally, ballz );

  if ( userDefineColor ) {
    try {
      ballColor = colors is Color
          ? colors
          :(colors is List<double>)
            ?Color(
              math.min(colors[0].abs().toDouble(), 1),
              math.min(colors[1].abs().toDouble(), 1),
              math.min(colors[2].abs().toDouble(), 1)
            ):Color(colors);

    }
    catch(err){
      ballColor = Color( ballx, bally, ballz );
    }
  }

  // Let's solve the equation to find the radius:
  // 1.0 / (0.000001 + radius^2) * strength - subtract = 0
  // strength / (radius^2) = subtract
  // strength = subtract * radius^2
  // radius^2 = strength / subtract
  // radius = sqrt(strength / subtract)

  final radius = size * math.sqrt( strength / subtract );
  final zs = ballz * size;
  final ys = bally * size;
  final xs = ballx * size;

  int minZ = ( zs - radius ).floor();
  if ( minZ < 1 ) minZ = 1;
  int maxZ = ( zs + radius ).floor();
  if ( maxZ > size - 1 ) maxZ = size.toInt() - 1;
  int minY = ( ys - radius ).floor();
  if ( minY < 1 ) minY = 1;
  int maxY = ( ys + radius ).floor();
  if ( maxY > size - 1 ) maxY = size.toInt() - 1;
  int minX = ( xs - radius ).floor();
  if ( minX < 1 ) minX = 1;
  int maxX = (xs + radius).floor();
  if ( maxX > size - 1 ) maxX = size.toInt() - 1;

  // Don't polygonize in the outer layer because normals aren't
  // well-defined there.
  for (int z = minZ; z < maxZ; z ++ ) {
    double zOffset = size2 * z;
    double fz = z / size - ballz;
    double fz2 = fz * fz;

    for (int y = minY; y < maxY; y ++ ) {
      double yOffset = zOffset + size * y;
      double fy = y / size - bally;
      double fy2 = fy * fy;

      for (int x = minX; x < maxX; x ++ ) {
        double fx = x / size - ballx;
        double val = strength / ( 0.000001 + fx * fx + fy2 + fz2 ) - subtract;
        if ( val > 0.0 ) {
          field[ yOffset.toInt() + x ] += val * sign;

          // optimization
          // http://www.geisswerks.com/ryan/BLOBS/blobs.html
          final  ratio = math.sqrt( ( x - xs ) * ( x - xs ) + ( y - ys ) * ( y - ys ) + ( z - zs ) * ( z - zs ) ) / radius;
          final contrib = 1 - ratio * ratio * ratio * ( ratio * ( ratio * 6 - 15 ) + 10 );

          palette[(yOffset + x).toInt() * 3 + 0] += ballColor.red * contrib;
          palette[(yOffset + x).toInt() * 3 + 1] += ballColor.green * contrib;
          palette[(yOffset + x).toInt() * 3 + 2] += ballColor.blue * contrib;
        }
      }
    }
  }
}