How to make a better zoom with orbitControl()?

I have been trying to figure out a way to adjust the sensitivity of the scroll wheel in WebGL mode, because zooming in and out on my sketch is extremely difficult and you have to be almost violent with your scroll wheel to get it to adjust.

I tried implementing the easycam library (https://github.com/diwi/p5.EasyCam/) but it seems to be incompatible with the current version of p5.js. Also, I’d like to avoid adding more libraries to my program.

I would like to figure out how to edit the sensitivity directly in the orbitControl() prototype (https://github.com/processing/p5.js/blob/master/src/webgl/interaction.js) but I can’t figure out how to do that … any help?

1 Like

First off you might want to check the sensitivity on you scroll wheel because hard coding a higher value might mean that other people find it moves way too fast and is out of control. So if you want to share the sketch that might be a better solution.

But if you want to change the function you can just copy and paste it in to the top of your file and it will overwrite the one that’s in the core library.

Unfortunately I’m on my laptop and it doesn’t have a scroll wheel but I can use multi touch to scroll so I can’t really see if this works entirely but the following might give you an idea of how to approach the problem.

p5.prototype.orbitControl = function(sensitivityX, sensitivityY) {
  this._assert3d('orbitControl');
  p5._validateParameters('orbitControl', arguments);

  // If the mouse is not in bounds of the canvas, disable all behaviors:
  const mouseInCanvas =
    this.mouseX < this.width &&
    this.mouseX > 0 &&
    this.mouseY < this.height &&
    this.mouseY > 0;
  if (!mouseInCanvas) return;

  const cam = this._renderer._curCamera;

  if (typeof sensitivityX === 'undefined') {
    sensitivityX = 1;
  }
  if (typeof sensitivityY === 'undefined') {
    sensitivityY = sensitivityX;
  }

  // default right-mouse and mouse-wheel behaviors (context menu and scrolling,
  // respectively) are disabled here to allow use of those events for panning and
  // zooming

  // disable context menu for canvas element and add 'contextMenuDisabled'
  // flag to p5 instance
  if (this.contextMenuDisabled !== true) {
    this.canvas.oncontextmenu = () => false;
    this._setProperty('contextMenuDisabled', true);
  }

  // disable default scrolling behavior on the canvas element and add
  // 'wheelDefaultDisabled' flag to p5 instance
  if (this.wheelDefaultDisabled !== true) {
    this.canvas.onwheel = () => false;
    this._setProperty('wheelDefaultDisabled', true);
  }

  const scaleFactor = this.height < this.width ? this.height : this.width;

  // ZOOM if there is a change in mouseWheelDelta
  if (this._mouseWheelDeltaY !== this._pmouseWheelDeltaY) {
    // zoom according to direction of mouseWheelDeltaY rather than value
    if (this._mouseWheelDeltaY > 0) {
      this._renderer._curCamera._orbit(0, 0, sensitivityZoom * scaleFactor);
    } else {
      this._renderer._curCamera._orbit(0, 0, -sensitivityZoom * scaleFactor);
    }
  }

  if (this.mouseIsPressed) {
    // ORBIT BEHAVIOR
    if (this.mouseButton === this.LEFT) {
      const deltaTheta =
        -sensitivityX * (this.mouseX - this.pmouseX) / scaleFactor;
      const deltaPhi =
        sensitivityY * (this.mouseY - this.pmouseY) / scaleFactor;
      this._renderer._curCamera._orbit(deltaTheta, deltaPhi, 0);
    } else if (this.mouseButton === this.RIGHT) {
      // PANNING BEHAVIOR along X/Z camera axes and restricted to X/Z plane
      // in world space
      const local = cam._getLocalAxes();

      // normalize portions along X/Z axes
      const xmag = Math.sqrt(local.x[0] * local.x[0] + local.x[2] * local.x[2]);
      if (xmag !== 0) {
        local.x[0] /= xmag;
        local.x[2] /= xmag;
      }

      // normalize portions along X/Z axes
      const ymag = Math.sqrt(local.y[0] * local.y[0] + local.y[2] * local.y[2]);
      if (ymag !== 0) {
        local.y[0] /= ymag;
        local.y[2] /= ymag;
      }

      // move along those vectors by amount controlled by mouseX, pmouseY
      const dx = -1 * sensitivityX * (this.mouseX - this.pmouseX);
      const dz = -1 * sensitivityY * (this.mouseY - this.pmouseY);

      // restrict movement to XZ plane in world space
      cam.setPosition(
        cam.eyeX + dx * local.x[0] + dz * local.z[0],
        cam.eyeY,
        cam.eyeZ + dx * local.x[2] + dz * local.z[2]
      );
    }
  }
  return this;
};

// Sketch
let sensitivityZoom = 0.5;

function setup() {
  createCanvas(710, 400, WEBGL);
  slider = createSlider(0, 100, 50);
}

function draw() {
  sensitivityZoom = slider.value() / 100;
  
  background(250);
  let radius = width * 1.5;

  //drag to move the world.
  orbitControl();

  normalMaterial();
  translate(0, 0, -600);
  for (let i = 0; i <= 12; i++) {
    for (let j = 0; j <= 12; j++) {
      push();
      let a = (j / 12) * PI;
      let b = (i / 12) * PI;
      translate(
        sin(2 * a) * radius * sin(b),
        (cos(b) * radius) / 2,
        cos(2 * a) * radius * sin(b)
      );
      if (j % 2 === 0) {
        cone(30, 30);
      } else {
        box(30, 30, 30);
      }
      pop();
    }
  }
}

I just took the orbital control example and the method from the source file. Added a slider value for the 0.5 value in the function where it talks about scrolling. Hopefully that helps.

3 Likes

Thank you! I did not realize it would be this easy … Regarding my mouse having a hard-coded sensitivity value: it seems to be a persistent issue across various types of hardware, and it’s possible that the mousewheel is triggering another event somewhere within the document, causing conflicting commands to be sent to the queue. I still have more troubleshooting to do and unfortunately, the code in its entirety is way beyond the scope of what I can reasonably expect somebody in this forum to review :stuck_out_tongue:

Either way, this will help out a lot. Thanks :slight_smile:

2 Likes

Hi, I just encounter problem with orbitControl zoom and this code helped me a lot.
It seems there is a problem (bug ?) in this check :

if (this._mouseWheelDeltaY !== this._pmouseWheelDeltaY)

as it does not correcly detect when my scroll wheel is turned. After fixing that zoom works smoothly.

I should have followed up about this, but here is the solution I used. Some notes about my code:

  • Depending on whether the mouse wheel is rolled forwards or backwards, zoom in or out.
  • The rate depends on how large/small the canvas is, and also on a constant “sensitivityZoom”.
  • If the user barely touches the mouse wheel, it will zoom in/out at the same rate as if they violently spin it. This essentially normalizes the zoom rate across all devices, and worked like a charm for my project. Hope this helps anyone who had the same issue.

I’m not sure if directly modifying the private variables prefixed with “_” is good practice or not, somebody can chime in about that if they know.


cnv = createCanvas(pageWidth, pageHeight, WEBGL);
cnv.mouseWheel(zoom);

function zoom(event) {
    let sensitivityZoom = 0.05;
    let scaleFactor = cnv.height;
    if (event.deltaY > 0) {
        cnv._curCamera._orbit(0, 0, sensitivityZoom * scaleFactor);
    } else {
        cnv._curCamera._orbit(0, 0, -sensitivityZoom * scaleFactor);
    }
}
1 Like