Pixel flow teleporting test object

Can someone tell me why sometimes it looks like the circle will teleport?

import com.thomasdiewald.pixelflow.java.DwPixelFlow;
import com.thomasdiewald.pixelflow.java.dwgl.DwGLTexture;
import com.thomasdiewald.pixelflow.java.fluid.DwFluid2D;
import com.thomasdiewald.pixelflow.java.imageprocessing.filter.DwFilter;
import processing.core.*;
import processing.opengl.PGraphics2D;
 
DwPixelFlow context;  
DwFluid2D fluid;  
PGraphics2D pg_fluid;
  
PVector pos = new PVector(300,300);  
  
  void setup() {
    size(600, 600, P2D);
       
    context = new DwPixelFlow(this);
    
    fluid = new DwFluid2D(context, width, height, 1);
    
    fluid.param.dissipation_velocity = .5f;
    fluid.param.dissipation_density  = 1f;

    fluid.addCallback_FluiData(new  DwFluid2D.FluidData(){
      public void update(DwFluid2D fluid) {
        if(mousePressed){
          float px     = mouseX;
          float py     = height-mouseY;
          float vx     = (mouseX - pmouseX) * +15;
          float vy     = (mouseY - pmouseY) * -15;
          fluid.addVelocity(px, py, 14, vx, vy);
          fluid.addDensity (px, py, 20, 0.0f, 0.4f, 1.0f, 1.0f);
          fluid.addDensity (px, py,  8, 1.0f, 1.0f, 1.0f, 1.0f);
        }       
      }
    });

    pg_fluid = (PGraphics2D) createGraphics(width, height, P2D);

  }
  
  
  DwGLTexture tex_vel_small;
  float[] data_vel;

  void draw() {    
    fluid.update();
        
    if(tex_vel_small == null){
      tex_vel_small = fluid.tex_velocity.src.createEmtpyCopy();
      tex_vel_small.resize(context, width, height);
    }
    
    DwFilter.get(context).copy.apply(fluid.tex_velocity.src, tex_vel_small);
    
    context.begin();
    data_vel = tex_vel_small.getFloatTextureData(data_vel);
    context.end();
    
    for(int gy = 0; gy < height; gy++){
      for(int gx = 0; gx < width; gx++){       

        int gid_fluid = (height - 1 - gy) * tex_vel_small.w + gx;
        float vel_x = data_vel[gid_fluid * 2 + 0];
        float vel_y = data_vel[gid_fluid * 2 + 1];
        if(gy == round(pos.y) && gx == round(pos.x)){
         pos.x += vel_x;
         pos.y += -vel_y;
        }
      }
    }
    
    pg_fluid.beginDraw();
    pg_fluid.background(0);
    pg_fluid.endDraw();
    fluid.renderFluidTextures(pg_fluid, 0);

    image(pg_fluid, 0, 0);
    ellipse(pos.x,pos.y,10,10);
  }
  
2 Likes

Don’t know what you mean by teleporting, but I like the sketch’s result.:wink:

Your problem occurs where you update pos. Try adding println logging to see the actual values that you are adding to pos.

  for (int gy = 0; gy < height; gy++) {
    for (int gx = 0; gx < width; gx++) {       
      int gid_fluid = (height - 1 - gy) * tex_vel_small.w + gx;
      float vel_x = data_vel[gid_fluid * 2 + 0];
      float vel_y = data_vel[gid_fluid * 2 + 1];
      if (gy == round(pos.y) && gx == round(pos.x)) {
        pos.x += vel_x;
        pos.y += -vel_y;
        if (abs(vel_x) > 1 || abs(vel_y) > 1) {
          println(frameCount, vel_x, -vel_y);
        }
      }
    }
  }

From you code it looks like you think that this if statement will a) only match once, and b) be a relatively small value. As you can see, this isn’t what is happening, On frame 32 pos is updated 7 times – and some of those updates individually are 14 pixels, so it jumps over 30 pixels.

27 -1.2929688 0.16247559
28 -1.3359375 0.091796875
29 -1.15625 5.493164E-4
32 -0.28125 1.3925781
32 -0.22729492 2.1660156
32 -0.1977539 3.2734375
32 -0.6201172 5.4375
32 -2.9589844 10.3515625
32 -14.828125 14.2734375
32 -12.5 -2.0800781
33 -10.4140625 14.65625

A key issue here is that you are checking pos in loop while you are changing it. So you can check a pixel, then bump pos to another pixel, then check that pixel, then bump pos to another pixel, then check that pixel, then bump pos to another pixel, then check that pixel…

Here is one way around that –

  PVector newpos = pos.copy();
  for (int gy = 0; gy < height; gy++) {
    for (int gx = 0; gx < width; gx++) {       
      if (gy == round(pos.y) && gx == round(pos.x)) {
        int gid_fluid = (height - 1 - gy) * tex_vel_small.w + gx;
        float vel_x = data_vel[gid_fluid * 2 + 0];
        float vel_y = data_vel[gid_fluid * 2 + 1];
        newpos.x += vel_x;
        newpos.y += -vel_y;
        if (abs(vel_x) > 1 || abs(vel_y) > 1) {
          println(frameCount, vel_x, -vel_y);
        }
      }
    }
  }
  pos = newpos;

another way if you only want to check one pixel is don’t loop at all – just look up the pixel that you want with no loops:

  int gx = round(pos.x));
  int gy = round(pos.y);
  int gid_fluid = (height - 1 - gy) * tex_vel_small.w + gx;
  float vel_x = data_vel[gid_fluid * 2 + 0];
  float vel_y = data_vel[gid_fluid * 2 + 1];
  pos.x += vel_x;
  pos.y += -vel_y;
  if (abs(vel_x) > 1 || abs(vel_y) > 1) {
    println(frameCount, vel_x, -vel_y);
  }

Now that you aren’t multi-updating, if you still don’t like how jumpy things are consider either altering the total velocity energy of your fluid – or setting a hard limit on how far the ellipse can move in a frame. For example:

vel_x = constrain(vel_x, -vel_max, vel_max);
vel_y = constrain(vel_y, -vel_max, vel_max);

Yet another option is to use easing with PVector.lerp().