# How to make a 2d Zoom and Pan

**URL:** <https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179>\
**Category:** Coding Questions\
**Created:** [June 23, 2018, 9:38am UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179 "2018-06-23T09:38:00Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![imaginativeCODE](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/imaginativecode/32/4220_2.png) [@imaginativeCODE](https://discourse.processing.org/u/imaginativeCODE)\
**Post date:** [June 23, 2018, 9:38am UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/1 "2018-06-23T09:38:00Z")

</div>

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));.

 ![Pic1](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/1X/4c2b4cee0a12aea77405794df615608a9c0e9ecf.png)

```auto
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;
  }
}

```

---

<div class="post-metadata">

**Author:** ![kfrajer](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/kfrajer/32/196_2.png) [@kfrajer](https://discourse.processing.org/u/kfrajer)\
**Post date:** [June 23, 2018, 10:53am UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/2 "2018-06-23T10:53:18Z")

</div>

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

> [How to pan an image](https://discourse.processing.org/t/how-to-pan-an-image/1002)  
> [Need Help: Zoomable interface for a procedural drawing program](https://discourse.processing.org/t/need-help-zoomable-interface-for-a-procedural-drawing-program/243)

The post of interest for your topic:

[https://forum.processing.org/two/discussion/20813/zooming-and-panning-headache#latest](https://forum.processing.org/two/discussion/20813/zooming-and-panning-headache#latest)[Scale Translate AB comparission - Processing 2.x and 3.x Forum](https://forum.processing.org/two/discussion/20853/scale-translate-ab-comparission#latest)

Kf

---

<div class="post-metadata">

**Author:** ![imaginativeCODE](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/imaginativecode/32/4220_2.png) [@imaginativeCODE](https://discourse.processing.org/u/imaginativeCODE)\
**Post date:** [June 23, 2018, 11:52am UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/3 "2018-06-23T11:52:57Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![imaginativeCODE](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/imaginativecode/32/4220_2.png) [@imaginativeCODE](https://discourse.processing.org/u/imaginativeCODE)\
**Post date:** [June 23, 2018, 3:07pm UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/4 "2018-06-23T15:07:19Z")

</div>

Thanks for your help i think i have figured it out. 🕶

---

<div class="post-metadata">

**Author:** ![imaginativeCODE](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/imaginativecode/32/4220_2.png) [@imaginativeCODE](https://discourse.processing.org/u/imaginativeCODE)\
**Post date:** [June 24, 2018, 12:51pm UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/5 "2018-06-24T12:51:34Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![kfrajer](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/kfrajer/32/196_2.png) [@kfrajer](https://discourse.processing.org/u/kfrajer)\
**Post date:** [June 24, 2018, 1:21pm UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/6 "2018-06-24T13:21:34Z")

</div>

You need to provide your code.

Kf

---

<div class="post-metadata">

**Author:** ![imaginativeCODE](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/imaginativecode/32/4220_2.png) [@imaginativeCODE](https://discourse.processing.org/u/imaginativeCODE)\
**Post date:** [June 24, 2018, 2:05pm UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/7 "2018-06-24T14:05:33Z")

</div>

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.

```auto
// 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);
}

```

---

<div class="post-metadata">

**Author:** ![imaginativeCODE](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/imaginativecode/32/4220_2.png) [@imaginativeCODE](https://discourse.processing.org/u/imaginativeCODE)\
**Post date:** [June 24, 2018, 7:26pm UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/8 "2018-06-24T19:26:30Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![companje](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/companje/32/2418_2.png) [@companje](https://discourse.processing.org/u/companje)\
**Post date:** [February 11, 2019, 10:15pm UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/9 "2019-02-11T22:15:33Z")

</div>

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](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/f/f03b5f26a12cda64c27ec3d5d6f832d7110a84de.gif)

```auto
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;
}

```

---

<div class="post-metadata">

**Author:** ![scudly](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/scudly/32/5597_2.png) [@scudly](https://discourse.processing.org/u/scudly)\
**Post date:** [February 15, 2019, 5:02am UTC](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/10 "2019-02-15T05:02:40Z")

</div>

See [https://redd.it/aecgdp](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.
