Constraining Image and Shape

I need help with my JavaScript code in p5.js.
I am struggling to figure out how to constrain the large image, so when you pan the screen at the max zoomed-out position (starting point), the image does not move off the screen and display any gray areas. In addition, the small image on the top-right corner, making it to where the orange rectangle stays only within the smaller image.

See the link to my code in the p5.js editor.

https://editor.p5js.org/ccharlesnoriega/sketches/ozRhJPpWJ

var w, h, tow, toh;
var x, y, tox, toy;
var zoom = 0.01; //zoom step per mouse tick
var easing = 0.1;
maxMag = 5;

overviewScale = 1 / 3; //inset map size

let chkbox1;
let aerialImage = true;

function preload() {
  img = loadImage("data/Campus100dpi.png");
  sdsu = loadImage("data/sdsu.png");
  imgBldgs = loadImage("data/Buildings100dpi.png");
  tblBldgs = loadTable("data/Building_Codes_updated.csv", "csv", "header");
}

function setup() {
  createCanvas(800, 450);
  initImage();
  chkbox1 = createCheckbox('Campus aerial photo', true);
  chkbox1.position(620, 250);
  chkbox1.changed(changeAerial);
  
  
  overW = img.width * overviewScale;
  overH = img.height * overviewScale;
  overX = 600;
  overY = 0;
  overCX = 700;
  overCY = overH / 2;
}

function changeAerial(){
  if (this.checked()){
    aerialImage = true;
  }
  else{
    aerialImage = false;
  }
}

function initImage() {
  w = tow = img.width;
  h = toh = img.height;
  x = tox = w / 2;
  y = toy = h / 2;
}

function draw() {
  background(120);
  imageMode(CENTER);
  //tween/smooth motion
  x = lerp(x, tox, easing);
  y = lerp(y, toy, easing);
  w = lerp(w, tow, easing);
  h = lerp(h, toh, easing);


  
  bldgImg = image(imgBldgs, x, y, w, h); //building image parameters
  bldgcolorVal = get(mouseX, mouseY); // get underlying color from imgBldgs grey color map
  if (aerialImage){
    mainImg = image(img, x, y, w, h); //main image parameters
  }
 // noFill();
 // strokeWeight(5);
 // stroke(0);
 // rect(x-img.width/2, y-img.height/2,img.width,img.height);
  drawOverviewImg();
  drawLocator();

  tname = getFeatureName(red(bldgcolorVal), tblBldgs); //label display
  hoverBox(tname);

  introScreen(tname); //display label in legend
}

function hoverBox(_name) {
  if (_name != "") {
    strokeWeight(3);
    fill(220);
    stroke(0);
    rect(mouseX, mouseY, textWidth(_name) + 8, -20);
    noStroke();
    fill(0);
    text(_name, mouseX + 3, mouseY - 3);
  }
}

function drawLocator() {
  fill(255, 0, 0);
  noStroke();
  // calculate current magnification of image
  magn = tow / img.width;

  // calculate where, on the overview image, the center of the actual image is
  dx = ((img.width / 2 - x) * overviewScale) / magn;
  dy = ((img.height / 2 - y) * overviewScale) / magn;
  circle(overCX + dx, overCY + dy, 5); // center of image

  // calculate the width and height of the windowed image, dependent on magnification
  dw = (img.width * overviewScale) / magn;
  dh = (img.height * overviewScale) / magn;

  // draw adjusted rectangle to show where windowed image is (upper left corner in dw/2 and dh/2 from center
  stroke(255, 100, 0);
  noFill();
  rect(overCX + dx - dw / 2, overCY + dy - dh / 2, dw, dh);
}

function drawOverviewImg() {
  imageMode(CORNER); //draws inset
  overviewImg = image(img, overX, overY, overW, overH); //inset image parameters (corner)
  noFill();
  strokeWeight(3);
  stroke(0);
  rect(overX, overY, overW, overH);
}

function introScreen(_name) {
  //legend

  fill(255);
  stroke(0);
  rect1 = rect(600, 150, 200, 450); //legend size
  fill(0);
  rect(600,150,200,80);
  image(sdsu,610,160,170,50);
  noStroke();
  fill(0);
  textSize(15);
  textFont("Source Serif Pro");
  text("", 610, 160, 170, 170);
  text("Responsive Design", 610, 240, 170, 170);
  text(_name, 610, 320, 170, 170);
}

function getFeatureName(grayVal, tbl) {
  //building data implementation
  name = "";
  //iterate over entries in this table given
  for (var i = 1; i < tbl.getRowCount(); i++) {
    var code = tbl.get(i, "Pixel_Val");
    if (grayVal == code) {
      name = tbl.get(i, "Name");
      //console.log(i, name);
      return name;
    }
  }
  return name;
}

function mousePressed() {
  if (mouseX > 600 && mouseX < width && mouseY > 0 && mouseY < overH) {
    initImage();
  }


}

/*function mouseClicked(){
   
  if (mouseX > 0 && mouseX < img.width && mouseY > 0 && mouseY < img.height) {
    if (tow > maxMag * img.width) return;
     tox -= maxMag * (mouseX - tox);
      toy -= maxMag * (mouseY - toy);
      tow *= maxMag + 1;
      toh *= maxMag + 1; 
      }
}*/

function mouseDragged() {
  tox += mouseX - pmouseX;
  toy += mouseY - pmouseY;
}

function keyPressed() {
  if (key == "+") {
    //zoom in
  }

  if (key == "-") {
    //zoom out
  }

  //reset to original size by clicking a button
  if (key == "r") {
    initImage();
  }
}


function mouseWheel(event) {
  var e = -event.delta;

  if (e > 0) {
    //zoom in
    for (var i = 0; i < e; i++) {
      if (tow > maxMag * img.width) return; //max zoom
      tox -= zoom * (mouseX - tox);
      toy -= zoom * (mouseY - toy);
      tow *= zoom + 1;
      toh *= zoom + 1;
    }
  }

  if (e < 0) {
    //zoom out
    for (var i = 0; i < -e; i++) {
      if (tow < img.width) return; //min zoom
      tox += (zoom / (zoom + 1)) * (mouseX - tox);
      toy += (zoom / (zoom + 1)) * (mouseY - toy);
      toh /= zoom + 1;
      tow /= zoom + 1;
    }
  }
}

function windowResize() {
  resizeCanvas(windowWidth, windowHeight);
}

https://editor.p5js.org/Kumu-Paul/sketches/A3nhCT3G6

It’s a little tricky to reason about with imageMode(CENTER). But here’s the logic:

  • In order to ensure that the left edge of the image is no further to the right than the left edge of the view area, the x position where the image is displayed cannot be any greater than the width of the image divided by two (this assumes that the left edge of the viewing area is at position 0)
  • In order to ensure that the right edge of the image is no further to the left than the right edge of the view area, the x position where the image is displayed cannot be any less than the width of the view area minus half the width of the image.
  • The logic for the vertical axis is the same. The y position of the image must be between the view height minus half the height of the image and half the width of the image.

The best way to apply these constraints is to the tox/toy variables any time tox, toy, tow, or toh change:

  tox = constrain(tox, 600 - tow / 2, tow / 2);
  toy = constrain(toy, 450 - toh / 2, toh / 2);

Oh, by the way, I know your StackOverflow question got closed (because the stack overflow community does not like links to external code, they always want the code embedded), but you should generally mention that you’ve cross posted your question in both places (that way before somebody answers on one site they know to check the other site in case it has already been answered/discussed).

1 Like