# Zoom/Pan constraints

**URL:** <https://discourse.processing.org/t/zoom-pan-constraints/20420>\
**Category:** Coding Questions\
**Created:** [May 3, 2020, 8:52am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420 "2020-05-03T08:52:10Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![royschn](https://avatars.discourse-cdn.com/v4/letter/r/439d5e/32.png) [@royschn](https://discourse.processing.org/u/royschn)\
**Post date:** [May 3, 2020, 8:52am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/1 "2020-05-03T08:52:10Z")

</div>

hi all,

I have a simple code that allows drawing in the Processing canvas. Scrolling the mouse allows you to zoom in and out while press and drag the right button moves the drawing accordingly (PAN).  
What I’m trying to do now is define the borders of the canvas. the zoom and pan actions move the origin point everywhere without limits and thus in fact I can never return to the starting point.

I want the first frame to be the absolute limits of the drawing space where it can’t be moved at all. And when I’m zooming in, I can’t Pan over the certain limit. A good example of this would be navigating in Google maps that zoom out always brings you back to same origin point on the X axis.

The code is attached and I would like to hear if anyone has any idea how I should do it.  
Thanks!

```auto
ArrayList<line> All_draw;

float scale=1;
float translateX = 0.0;
float translateY = 0.0;

void setup(){
// fullScreen();
  size(1500,800);
  background(51);
  All_draw = new ArrayList<line>();
}
  
void draw(){
  background(51);
 
  pushMatrix();
  
  translate(translateX,translateY);
  stroke(255);
  if (mousePressed == true && scale>=0 && mouseButton!=39) {
    All_draw.add(new line((-translateX+mouseX)/scale, (-translateY+mouseY)/scale, (-translateX+pmouseX)/scale, (-translateY+pmouseY)/scale));}
  scale (scale);
  for (line l : All_draw){
      l.display();}
  
  popMatrix();
}

void mouseDragged(MouseEvent e) {
    if (e.getButton()==39){

      translateX += mouseX - pmouseX;
      translateY += mouseY - pmouseY;}
}

void mouseWheel(MouseEvent e) {

    float p = -e.getCount()/10.0;
    float delta = pow(2, p);
    translateX -= mouseX;
    translateY -= mouseY;
    scale *= delta;
    translateX *= delta;
    translateY *= delta;
    translateX += mouseX;
    translateY += mouseY;

}

class line {
  float x1,y1,x2,y2;
  line (float x1_temp,float y1_temp,float x2_temp,float y2_temp){
    x1=x1_temp;
    y1=y1_temp;
    x2=x2_temp;
    y2=y2_temp;
  }
  
  void display(){
    line(x1, y1, x2, y2);}

}

```

---

<div class="post-metadata">

**Author:** ![Schred](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/schred/32/13146_2.png) [@Schred](https://discourse.processing.org/u/Schred)\
**Post date:** [May 3, 2020, 9:09am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/2 "2020-05-03T09:09:17Z")

</div>

Just checking I understand correctly: you want to stop the user from zooming out if they have zoomed out a certain amount?

---

<div class="post-metadata">

**Author:** ![royschn](https://avatars.discourse-cdn.com/v4/letter/r/439d5e/32.png) [@royschn](https://discourse.processing.org/u/royschn)\
**Post date:** [May 3, 2020, 9:28am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/3 "2020-05-03T09:28:42Z")

</div>

Hi Schred, yes. But more than that, because the zoom is set to the position of the mouse, I want to find a way to limit it to a certain range so that it always returns to the same starting point - the original initial frame.

---

<div class="post-metadata">

**Author:** ![Schred](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/schred/32/13146_2.png) [@Schred](https://discourse.processing.org/u/Schred)\
**Post date:** [May 3, 2020, 9:48am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/4 "2020-05-03T09:48:55Z")

</div>

Then it would probably be best just not to make the zooming relative to the mouse, but relative to the center of the window instead.

So, instead of adding `mouseX` and `mouseY` to `translateX` and `translateY`, add `width / 2` and `height / 2`. Also, to limit your zooming, introduce the variables `ZOOM_MN` and `ZOOM_MAX` and set them to whatever value you like:

```auto
final float ZOOM_MIN = 0.2, ZOOM_MAX = 5;

```

Finally, perform some tests in `mouseWheel` to see if the user has zoomed out to far. This is how the method should look like:

```auto
void mouseWheel(MouseEvent e) {
  float p = -e.getCount()/10.0;

  if (p < 0) {
    if (scale <= ZOOM_MIN)
      return;
  } else if (scale >= ZOOM_MAX)
    return;

  float delta = pow(2, p), w = width /2, h = height / 2;
  translateX -= w;
  translateY -= h;
  scale *= delta;
  translateX *= delta;
  translateY *= delta;
  translateX += w;
  translateY += h;
}

```

---

<div class="post-metadata">

**Author:** ![royschn](https://avatars.discourse-cdn.com/v4/letter/r/439d5e/32.png) [@royschn](https://discourse.processing.org/u/royschn)\
**Post date:** [May 3, 2020, 9:59am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/5 "2020-05-03T09:59:11Z")

</div>

Thanks so much for your reply. But that still doesn’t solve the problem. You can still make an unlimited pan. I want to limit the drawing boundaries to the initial frame. And the zoom and pan operations will only be within those limits.

---

<div class="post-metadata">

**Author:** ![Schred](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/schred/32/13146_2.png) [@Schred](https://discourse.processing.org/u/Schred)\
**Post date:** [May 3, 2020, 10:01am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/6 "2020-05-03T10:01:28Z")

</div>

Then set `ZOOM_MIN` to `1`.

---

<div class="post-metadata">

**Author:** ![royschn](https://avatars.discourse-cdn.com/v4/letter/r/439d5e/32.png) [@royschn](https://discourse.processing.org/u/royschn)\
**Post date:** [May 3, 2020, 10:08am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/7 "2020-05-03T10:08:09Z")

</div>

Yes. This limits the zoom level. But what about the Pan? The main question is how to avoid exceeding the boundary of the frame when you have zoom in. and how to return back to the initial frame when you zoom out.

---

<div class="post-metadata">

**Author:** ![Schred](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/schred/32/13146_2.png) [@Schred](https://discourse.processing.org/u/Schred)\
**Post date:** [May 3, 2020, 10:13am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/8 "2020-05-03T10:13:50Z")

</div>

Hmm, okay. Try putting this at the end of `mouseWheel`:

```auto
translateX = max(0, min(translateX, width * scale));
translateY = max(0, min(translateY, height * scale));

```

---

<div class="post-metadata">

**Author:** ![royschn](https://avatars.discourse-cdn.com/v4/letter/r/439d5e/32.png) [@royschn](https://discourse.processing.org/u/royschn)\
**Post date:** [May 3, 2020, 10:30am UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/9 "2020-05-03T10:30:52Z")

</div>

This allows you to zoom in only to a certain point. But yes, the end result is good. The question now is how to allow free zoom within the borders and then return to the original. - The code is currently:

```auto
ArrayList<line> All_draw;

float scale=1;
float translateX = 0.0;
float translateY = 0.0;
final float ZOOM_MIN = 1, ZOOM_MAX = 5;
final float pan_MIN = 1, pan_MAX = 5;

void setup(){
// fullScreen();
  size(1500,800);
  background(51);
  All_draw = new ArrayList<line>();
}
  
void draw(){
  background(51);
 
  pushMatrix();
  
  translate(translateX,translateY);
  stroke(255);
  if (mousePressed == true && scale>=0 && mouseButton!=39) {
    All_draw.add(new line((-translateX+mouseX)/scale, (-translateY+mouseY)/scale, (-translateX+pmouseX)/scale, (-translateY+pmouseY)/scale));}
  scale (scale);
  for (line l : All_draw){
      l.display();}
  
  popMatrix();
}

void mouseDragged(MouseEvent e) {
    if (e.getButton()==39){

      translateX += mouseX - pmouseX;
      translateY += mouseY - pmouseY;}
}

void mouseWheel(MouseEvent e) {
  float p = -e.getCount()/10.0;

  if (p < 0) {
    if (scale <= ZOOM_MIN)
      return;
  } else if (scale >= ZOOM_MAX)
    return;

  float delta = pow(2, p), w = width /2, h = height / 2;
  translateX -= w;
  translateY -= h;
  scale *= delta;
  translateX *= delta;
  translateY *= delta;
  translateX += w;
  translateY += h;
  
  translateX = min(0, max(translateX, width * scale));
  translateY = min(0, max(translateY, height * scale));
}

class line {
  float x1,y1,x2,y2;
  line (float x1_temp,float y1_temp,float x2_temp,float y2_temp){
    x1=x1_temp;
    y1=y1_temp;
    x2=x2_temp;
    y2=y2_temp;
  }
  
  void display(){
    line(x1, y1, x2, y2);}

}

```

In addition, I attach a code that does exactly what I’m looking for but works with image and texture. I don’t know how to implement these navigation actions that will work in my code.  
([How to make a 2d Zoom and Pan](https://discourse.processing.org/t/how-to-make-a-2d-zoom-and-pan/1179/9))

```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:** ![Schred](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/schred/32/13146_2.png) [@Schred](https://discourse.processing.org/u/Schred)\
**Post date:** [May 3, 2020, 12:12pm UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/10 "2020-05-03T12:12:59Z")

</div>

> [@royschn](#):
>
> This allows you to zoom in only to a certain point

If you don’t want that, you can just remove that `else if(scale >= ZOOM_MAX) return;` part.

---

<div class="post-metadata">

**Author:** ![royschn](https://avatars.discourse-cdn.com/v4/letter/r/439d5e/32.png) [@royschn](https://discourse.processing.org/u/royschn)\
**Post date:** [May 3, 2020, 12:19pm UTC](https://discourse.processing.org/t/zoom-pan-constraints/20420/11 "2020-05-03T12:19:25Z")

</div>

It seems that you do not run the code or you do not understand my intent at all. I thank you for the help but I’ll wait for someone else maybe know how to help me.
