Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

three is - how to limit pan in OrbitControls for OrthographicCamera so that object (texture image) is always in the scene

Tags:

three.js

I am using OrbitControls to control an Orthographic camera. The scene has a 2d texture image.

I want to limit the pan of the camera, when reaching the edge of the image. For example, when the camera is panned to the left, and the image is shifted to the right in the view window, when the left edge of the image is to the right of the visible window, stop the left panning (see the attached diagram) enter image description here

Here and here the pan limit is hard coded, but in my case the limit depends on the zoom (and I assume that also on the size of the image). For example, when the image is shifted all the way to left,

when zoomed-in, scope.target.x ~= ~100

when zoomed-out, scope.target.x ~= ~800

How can I disable panning to the left when the left side of the image reaches the left edge of the visible window?

Thanks, Avner


EDIT:

@Rabbid76 thanks for your suggestions. With some modifications to the example code, I solved the problem, i.e. the image always covers the view window. See here for details

like image 533
Avner Moshkovitz Avatar asked Oct 18 '25 15:10

Avner Moshkovitz


1 Answers

You can manually limit the pan.

Consider you have an OrthographicCamera, which looks onto the xy-plane.

e.g.

camera = new THREE.OrthographicCamera(-5*aspect, 5*aspect, -5, 5, -100, 100);
camera.position.set(0, 0, -1);

And you have a mesh (object), from which you can get the bounding box (THREE.Box3):

var bbox = new THREE.Box3().setFromObject(object);

With this information the minimum and maximum x and y coordinates of the object can be calculated:

var min_x = camera.left  - bbox.min.x;
var max_x = camera.right - bbox.max.x;
var min_y = camera.top    - bbox.min.y;
var max_y = camera.bottom - bbox.max.y; 

The current camera position and target can be clamped to the limiting range:

let pos_x =  Math.min(max_x, Math.max(min_x, camera.position.x));
let pos_y =  Math.min(max_y, Math.max(min_y, camera.position.y));

Update the OrthographicCamera and the OrbitControls:

camera.position.set(pos_x, pos_y, camera.position.z);
camera.lookAt(pos_x, pos_y, orbitControls.target.z);

orbitControls.target.x = pos_x;
orbitControls.target.y = pos_y;
orbitControls.update();

See the example:

(function onLoad() {
  var container, loader, camera, scene, renderer, orbitControls, object, bbox;
  
  init();
  animate();

  function init() {
    container = document.getElementById('container');
    
    renderer = new THREE.WebGLRenderer({
      antialias: true,
      alpha: true
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    container.appendChild(renderer.domElement);

    aspect = window.innerWidth / window.innerHeight;
    camera = new THREE.OrthographicCamera(-5*aspect, 5*aspect, -5, 5, -100, 100);
    camera.position.set(0, 0, -1);

    loader = new THREE.TextureLoader();
    loader.setCrossOrigin("");

    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xffffff);
    scene.add(camera);
    window.onresize = resize;
    
    orbitControls = new THREE.OrbitControls(camera);

    orbitControls.enabled = true;
    orbitControls.enableRotate = false;
    orbitControls.screenSpacePanning = true; 
    orbitControls.mouseButtons = {
	    LEFT: THREE.MOUSE.RIGHT,
	    MIDDLE: THREE.MOUSE.MIDDLE,
	    RIGHT: THREE.MOUSE.LEFT
    }

    
    addGridHelper();
    createModel();

  }

  function createModel() {

    var material = new THREE.MeshBasicMaterial({color:'#ff4040'});
    var geometry = new THREE.BoxGeometry( 1, 1, 1 );

    object = new THREE.Mesh(geometry, material);
    bbox = new THREE.Box3().setFromObject(object);

    scene.add(object);
  }

  function addGridHelper() {
    
    var helper = new THREE.GridHelper(100, 100);
    helper.rotation.x = Math.PI / 2;
    helper.material.opacity = 0.25;
    helper.material.transparent = true;
    scene.add(helper);

    var axis = new THREE.AxesHelper(1000);
    scene.add(axis);
  }

  function resize() {
    
    var aspect = window.innerWidth / window.innerHeight;
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.left = -5*aspect;
    camera.right = 5*aspect;
    camera.updateProjectionMatrix();
  }

  function animate() {
    requestAnimationFrame(animate);

    var min_x = camera.left  - bbox.min.x;
    var max_x = camera.right - bbox.max.x;
    var min_y = camera.top    - bbox.min.y;
    var max_y = camera.bottom - bbox.max.y;

    let pos_x =  Math.min(max_x, Math.max(min_x, camera.position.x));
    let pos_y =  Math.min(max_y, Math.max(min_y, camera.position.y));
    
    camera.position.set(pos_x, pos_y, camera.position.z);
    camera.lookAt(pos_x, pos_y, orbitControls.target.z);

    orbitControls.target.x = pos_x;
    orbitControls.target.y = pos_y;
    orbitControls.update();
    
    render();
  }

  function render() {
    renderer.render(scene, camera);
  }
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="container"></div>
like image 133
Rabbid76 Avatar answered Oct 22 '25 07:10

Rabbid76