How to make a 2d Zoom and Pan

I am trying to make a zoom in and out that the circle stays in the same place but more or less squares are shown. My first assumption is to scale the rects by zoom and then translate(MX + (Zoom / 2), MY + (Zoom / 2));.

int TileSize = 6;
int ChunkSize = 32;
int TilesShown = 6;
int AmountOfChunksX = 1;
int AmountOfChunksY = 1;
int ChunkBiome;
float[][] ChunkCordsX;
float[][] ChunkCordsY;
int  [][] ChunkId;
boolean ChunkBorder;
float MoveX, MoveY, Zoom;
boolean[] Keys;

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

void setup() {
  
  fullScreen();
  
  MoveX = 739;
  MoveY = 446;
  Zoom = 4.899999;
  //Zoom = 0.9599996;
  
  Keys = new boolean[122];
  ChunkCordsX = new float[ChunkSize * AmountOfChunksX][ChunkSize * AmountOfChunksX];
  ChunkCordsY = new float[ChunkSize * AmountOfChunksY][ChunkSize * AmountOfChunksY];
  ChunkId     = new int  [ChunkSize * (AmountOfChunksY + AmountOfChunksX)][ChunkSize * (AmountOfChunksY + AmountOfChunksX)];
  
  ChunkGenerator();
  
}

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

void draw() {
    
  background(200);
  KeyComDraw();
  
  pushMatrix();
    translate(MoveX, MoveY);
    scale(Zoom);
    ChunkDisplay();
  popMatrix();
  fill(255, 0, 0);
  ellipse(width/2, height/2, 30, 30);
  
}

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

void keyPressed() {
  
  switch (key) {   
    case 'w' : 
      Keys[0] = true;
      break;
    case 'a' : 
      Keys[1] = true;
      break;
    case 's' : 
      Keys[2] = true;
      break;
    case 'd' :
      Keys[3] = true;
      break;
    case 'i' :
      Keys[4] = true;
      break;
    case 'o' :
      Keys[5] = true;
      break;
  }
  
}

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

void keyReleased() {
  
  switch (key) {   
    case 'w' : 
      Keys[0] = false;
      break;
    case 'a' : 
      Keys[1] = false;
      break;
    case 's' : 
      Keys[2] = false;
      break;
    case 'd' :
      Keys[3] = false;
      break;
    case 'i' :
      Keys[4] = false;
      break;
    case 'o' :
      Keys[5] = false;
      break;
  }
  
  if (key == 'r' || key == 'R') {
    ChunkGenerator();
  }
  if (key == 'p' || key == 'P') {
    println("Zoom: " +  Zoom);
    println("MoveX: " + MoveX);
    println("MoveY: " + MoveY);
  }
  if ((key == 'b' || key == 'B') && ChunkBorder == false) {
    ChunkBorder = true;
  } else if ((key == 'b' || key == 'B') && ChunkBorder == true) {
    ChunkBorder = false;
  }
  
}

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

void ChunkDisplay() {
                                                                                                                                                                                                                                                                                                       
  if (ChunkBorder == false) {
    for (int Row = 0; Row < ChunkSize * AmountOfChunksX; Row++) {
      for (int Colmn = 0; Colmn < ChunkSize * AmountOfChunksY; Colmn++) {
        //if ((ChunkCordsX[Row][Colmn] + TileSize) * Zoom + MoveX > 0 && (ChunkCordsX[Row][Colmn] - TileSize) * Zoom + MoveX < width &&
        //    (ChunkCordsY[Row][Colmn] + TileSize) * Zoom + MoveY > 0 && (ChunkCordsY[Row][Colmn] - TileSize) * Zoom + MoveY < height) {
        if ((ChunkCordsX[Row][Colmn]) * Zoom + MoveX > 0 - ((TileSize) * Zoom) && (ChunkCordsX[Row][Colmn]) * Zoom + MoveX < width  + ((TileSize) * Zoom) &&
            (ChunkCordsY[Row][Colmn]) * Zoom + MoveY > 0 - ((TileSize) * Zoom) && (ChunkCordsY[Row][Colmn]) * Zoom + MoveY < height + ((TileSize) * Zoom)) {
              
          if (mouseX > (ChunkCordsX[Row][Colmn]) * Zoom + MoveX && mouseX < (ChunkCordsX[Row][Colmn] + TileSize) * Zoom + MoveX &&
              mouseY > (ChunkCordsY[Row][Colmn]) * Zoom + MoveY && mouseY < (ChunkCordsY[Row][Colmn] + TileSize) * Zoom + MoveY) {
            if (mousePressed) {
              stroke(random(255), random(255), random(255));
              ChunkId[Row][Colmn] = 1;
            } else {
              stroke(random(255), random(255), random(255));
            }//else
          } else {
            noStroke();
          }//else
            
            if (ChunkId[Row][Colmn] == 1) {
              fill(0);
            } else if (ChunkId[Row][Colmn] == 2) {
              fill(0, 0, 255);
            } else if (ChunkId[Row][Colmn] == 3) {
              fill(255);
            } else {
              fill(0, 0, 255);
            }
            rect(ChunkCordsX[Row][Colmn], ChunkCordsY[Row][Colmn], TileSize, TileSize);
        }//if
      }//for
    }//for
  } else if (ChunkBorder == true) {
    for (int Row = 0; Row < AmountOfChunksX; Row++) {
      for (int Colmn = 0; Colmn < AmountOfChunksY; Colmn++) {
          fill(255);
          stroke(255, 0, 0);
          rect(Row * (ChunkSize * TileSize), Colmn * (ChunkSize * TileSize), ChunkSize * TileSize, ChunkSize * TileSize);
       }//for
    }//for
  }//if

}

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

void ChunkGenerator() {
  
  ChunkBiome = round(random(1, 3));
  
  for (int Row = 0; Row < ChunkSize * AmountOfChunksX; Row++) {
    for (int Colmn = 0; Colmn < ChunkSize * AmountOfChunksY; Colmn++) {
      ChunkCordsX[Row][Colmn] = 0 + Row   * TileSize;
      ChunkCordsY[Row][Colmn] = 0 + Colmn * TileSize;
      ChunkId[Row][Colmn] = ChunkBiome;
    }//for
  }//for
  
}

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

void KeyComDraw() {
  int   MoveSpeed = 10;
  float ZoomSpeed = 0.06;
  
  //Help here
  float MoveMathX = 0;
  float MoveMathY = 0;
  if (Keys[0] == true) {
    MoveY += MoveSpeed;
  } 
  //key A
  if (Keys[1] == true) {
    MoveX += MoveSpeed;
  } 
  //key S
  if (Keys[2] == true) {
    MoveY -= MoveSpeed;
  } 
  //key D
  if (Keys[3] == true) {
    MoveX -= MoveSpeed;
  } 
  //key i
  if (Keys[4] == true) {
    Zoom  += ZoomSpeed;
    MoveX -= MoveMathX;
    MoveY -= MoveMathY;
  }
  //key o
  if (Keys[5] == true) {
    //Zoom -= 0.02;
    Zoom  -= ZoomSpeed;
    MoveX += MoveMathX;
    MoveY += MoveMathY;
  }
}

If you check panning in the forum you will get some hits.

How to pan an image
Need Help: Zoomable interface for a procedural drawing program

The post of interest for your topic:

https://forum.processing.org/two/discussion/20813/zooming-and-panning-headache#latest Scale Translate AB comparission - Processing 2.x and 3.x Forum

Kf

This is good but i was trying to have it were the tile that under the ellipse stays under the ellipse when zoomed in and out.

Thanks for your help i think i have figured it out. :sunglasses:

I ran into a problem with the code links you gave me won’t allow you to change the speed of zoom in and out any suggestions on how to fix this.

You need to provide your code.

Kf

This is the code i was looking at but i was trying to figure out how to increase or decrease zoom speed. And how would you change the zoom in and out to were the zoom in is attached to the ‘i’ key and zoom out to the ‘o’ key.

// working code    
float mx,my,ratio,xpt,ypt,xzt,yzt,swt,zoom;
void setup() {
 size(600,600);
  zoom=1.0;
  mx=width/60;
  my=mx*height/width;
  ratio=mx/my;
}
void draw() {
  scale(zoom);
  translate(xpt-xzt,ypt-yzt);
  background(128);
  grid(60,60/ratio);
}
void mouseDragged(){
  xpt-=(pmouseX-mouseX)/zoom;
  ypt-=(pmouseY-mouseY)/zoom;
}
void mouseWheel(MouseEvent event) {
  swt-=event.getCount();
  if (swt==0) {
    zoom=1.0;
  } else if (swt>=1 && swt<=10) {
    zoom=pow(2, swt);
  } else if (swt<=-1 && swt>=-10) {
    zoom=1/pow(2, abs(swt));
  }
  xzt=((zoom-1)*width/2)/zoom;
  yzt=((zoom-1)*height/2)/zoom;
  if(event.getCount()<=-1){
  xpt-=(mouseX-width/2)/zoom;
  ypt-=(mouseY-height/2)/zoom;
  } else {
  xpt+=(mouseX-width/2)/(pow(2, swt+1));
  ypt+=(mouseY-height/2)/(pow(2, swt+1));
  }
}
void grid(float x, float y) {
  stroke(0);
  for(int i = 0; i < x; i++){line(i*width/x,0,i*width/x,height);}
  for(int i = 0; i < y; i++){line(0,i*height/y,width,i*height/y);}
fill(#ff0000);rect(mx*10,mx*10,mx,mx);
fill(#ffff00);rect(mx*49,mx*10,mx,mx);
fill(#00ff00);rect(mx*10,mx*49,mx,mx);
fill(#0000ff);rect(mx*49,mx*49,mx,mx);
}
1 Like

I think i just need help on the indirect variation that MoveX and MoveY have to zoom so when zoom increase or decrease it also does for moveX and MoveY.

Hi everyone,

I wrote this example to explain how to Zoom and Pan with the mouse in Processing. It uses texture coordinates (texcoords). Enjoy!

zoom-pan-with-mouse-processing-small

float tx, ty;
float scale;
PImage img;
float xratio, yratio;
float minScale, maxScale;

void setup() {
  //use P2D or P3D to be able to use texture() 
  size(450, 360, P2D);
  
  //load image and calculate window/image ratio
  img = loadImage("1337.jpg");
  xratio = float(width) / img.width;
  yratio = float(height) / img.height;
  
  //minumum scale is calculated to prevent texture wrapping
  minScale = max(xratio, yratio);
  maxScale = 5;
  
  //start zoomed out
  scale = minScale;
}

void draw() {
  
  //calculate width/height of the region of interest in the texture
  float tw = img.width / scale * xratio;
  float th = img.height / scale * yratio;
  
  //limit the left-top texcoords to prevent texture wrapping
  tx = constrain(tx, 0, img.width-tw);
  ty = constrain(ty, 0, img.height-th);
 
  //draw textured rectangle 
  beginShape();
  texture(img);
  vertex(0, 0, tx, ty);
  vertex(width, 0, tx+tw, ty);
  vertex(width, height, tx+tw, ty+th);
  vertex(0, height, tx, ty+th);
  endShape();
}

void mouseDragged() {
  //move left-top texcoord using scaled mouse delta
  tx -= (mouseX-pmouseX)/scale;
  ty -= (mouseY-pmouseY)/scale;
}

void mouseWheel(MouseEvent event) {
  //zoom factor needs to be between about 0.99 and 1.01 to be able to multiply so add 1
  float zoomFactor = -event.getAmount()*.01 + 1; 
  float newScale = constrain(scale * zoomFactor, minScale, maxScale);
  
  //next two lines are the most important lines of the code.
  //subtract mouse in 'old' scale from mouse in 'new' scale and apply that to position.
  tx -= (mouseX/newScale - mouseX/scale);
  ty -= (mouseY/newScale - mouseY/scale);
  scale = newScale;
}
2 Likes

See https://redd.it/aecgdp for another example that includes zooming, panning, and rotating. Drag up and down the left edge to zoom, and across the bottom of the window to rotate.

2 Likes