Way to keep the bounding box in the scene / canvas, always visible

Hi,

I cannot give a playground to show my issue so I will try to include as much detail as possible.

I have a html page with a canvas, inside my canvas is my babylon scene render with a custom imported mesh.

Im using the same logic as this playground to zoom my orthographic camera on the mesh: https://playground.babylonjs.com/#EBPQH9#96

My issue is that when zooming on the mesh, at a certain point some part of the mesh go out of my canvas. See picture below:

Is there a way so I can zoom properly on my mesh (in orthographic camera mode) and keep the bounding box of my mesh inside my canvas?

Can you use codepen.io or something similar to show it?

I’m not sure I understand what you want. Do you want the zoom to stop before the bounding box goes out of frame? Or maybe you want the camera position to move so that the bounding box stays in frame? Something else? :thinking:

Hello,

thanks for the answer, it will be really difficult even in codepen because the babylonjs code are imbedded in my Angular project. But yeah what I want is more the second solution you mention, I want to be able to zoom as mush as possible so moving the camera position so that the bounding box stays in the frame would be the best solution for me.

public toggleOrientationMode(maxMesh: number) {
		this.isGizmoToggle = !this.isGizmoToggle;
		this.viewerService.toggleGizmo.emit(this.isGizmoToggle);

		if (this.scalingInMeter) {
			maxMesh *= 1000;
		}

		if (this.isGizmoToggle) {
			this.disableNextBtn.emit(true);

			this.gizmo.attachedMesh = this.importedMesh;

			// - Deactivate observer
			if (this.camera.onViewMatrixChangedObservable.hasObservers()) {
				this.camera.onViewMatrixChangedObservable.remove(this.cameraObserver);
			}

			this.engineService.disposeAxis();
			this.engineService.showAxis(100, this.scene, true);

			// Attach the camera to a invisible node and rotate the node so the Axis will be in the right format
			const transformNode = new TransformNode('tn', this.scene);
			this.camera.parent = transformNode;
			transformNode.rotate(Axis.X, Math.PI / 2);

			this.axis = this.engineService.axis;

			// set a minimum distance for the camera on the x axis
			const cameraDistanceX = maxMesh < 600 ? 600 : 2 * maxMesh;

			this.resetCameraZoom(this.camera, cameraDistanceX);

			let totalZoom = 0;
			this.orientationScaleObserver = this.scene.onPointerObservable.add(
				({ event }) => {
					const wheelDelta =
						-(event as WheelEvent).deltaY || -(event as WheelEvent).detail;

					const minValue = Math.min(1, wheelDelta);
					const delta = Math.max(-1, minValue) * 60;

					if ((delta > 0 && totalZoom < 450) || delta < 0) {
						totalZoom += delta;
						this.zoom2DView(this.camera, delta);
					}
				},
				PointerEventTypes.POINTERWHEEL,
			);

			this.camera.target = this.axis.x.position;

			// 90 degree Axis Characters rotation
			const charX = this.scene.getMeshByName('TextPlaneX');
			const charY = this.scene.getMeshByName('TextPlaneY');
			const charZ = this.scene.getMeshByName('TextPlaneZ');
			charX.rotate(Axis.Y, Math.PI / 2);
			charY.rotate(Axis.Y, Math.PI / 2);
			charZ.rotate(Axis.Y, Math.PI / 2);

			// Lower the visibility of the mesh to better see the axis
			this.importedMesh.visibility = 0.7;

			this.camera.lowerRadiusLimit = this.camera.radius;
			this.camera.upperRadiusLimit = this.camera.radius;

			this.camera.detachControl(this.canvasRef);
			// this.camera.attachControl(true, false, 0);

			// console.log('attached inputs', this.camera.inputs.attached.pointers);
		
			// this.camera.inputs.removeByType();
			// this.camera.inputs.remove(this.camera.inputs.attached.pointers);
			this.camera.panningSensibility = 1000;

			this.camera.mode = Camera.ORTHOGRAPHIC_CAMERA;
		} else {
			this.removeOrientationMode();
		}
	}

	private zoom2DView = async (camera, delta) => {
		if (this.zoomTarget) {
			const totalX = Math.abs(camera.orthoLeft - camera.orthoRight);
			const totalY = Math.abs(camera.orthoTop - camera.orthoBottom);

			const aspectRatio = totalY / totalX;

			{
				const fromCoord = camera.orthoLeft - this.zoomTarget.x;
				const ratio = fromCoord / totalX;
				camera.orthoLeft -= ratio * delta;
			}

			{
				const fromCoord = camera.orthoRight - this.zoomTarget.x;
				const ratio = fromCoord / totalX;
				camera.orthoRight -= ratio * delta;
			}

			{
				const fromCoord = camera.orthoTop - this.zoomTarget.y;
				const ratio = fromCoord / totalY;
				camera.orthoTop -= ratio * delta * aspectRatio;
			}

			{
				const fromCoord = camera.orthoBottom - this.zoomTarget.y;
				const ratio = fromCoord / totalY;
				camera.orthoBottom -= ratio * delta * aspectRatio;
			}

			// decrease pan sensitivity the closer the zoom level.
			camera.panningSensibility = 6250 / Math.abs(totalX / 2);
		}
		this.setTopBottomRatio(this.camera);
	};

	private setTopBottomRatio = (camera) => {
		const ratio =
			this.canvasRef.nativeElement.height / this.canvasRef.nativeElement.width;
		camera.orthoTop = camera.orthoRight * ratio;
		camera.orthoBottom = camera.orthoLeft * ratio;
	};

private resetCameraZoom = (
		camera: ArcRotateCamera,
		cameraDistance: number, // Almost equal 600 all the time
	) => {
		camera.setPosition(new Vector3(cameraDistance, 0, 0)); // (600 ,0 ,0)
		camera.orthoLeft = -cameraDistance;
		camera.orthoRight = cameraDistance;

		this.setTopBottomRatio(camera);
	};

Here I give you some function of my code but I am perfectly aware that without a playground it is difficult to help me so if you see a quick solution it will be extraordinary but if not it’s perfectly fine. I am already working on another solution to move the mesh when he is outside the frame using this code:

this.camera.attachControl(true, false, 0);

console.log('attached inputs', this.camera.inputs.attached.pointers);
	
// this.camera.inputs.removeByType(); What type do I need to remove mouseWheel click
this.camera.inputs.remove(this.camera.inputs.attached.pointers); // This remove all the click, not what I wanted

But I want to deactivate the mouseWheel click to not be able to rotate the camera around mesh, but keep the mouseWheel to zoom on mesh.
Is there a way to achieve this?

Thank you.

Marc-André.

It will certainly take effort in order to create a small repo. :slight_smile: If you boil down the problem to just the relevant parts, I would hope it doesn’t take too long.

At some point, the user will not be able to zoom in more without the bounding box being cut off. I’m assuming then it needs to stop zooming at that point?

Yeah, I don’t know how to even start looking at this without a repro.

@PolygonalSun may be able to help with this one.

The easiest way to do this is just to remove the middle-click value (1 according to MDN) from the buttons array for the camera’s pointers input. You can do this with one line:

// 'camera' is just your camera object
// '0' is left-click and 2 is right-click
camera.inputs.attached.pointers.buttons = [0, 2];
1 Like

Hi, thank you!

the only issue is that it seems that in my project using babylonjs/core, the buttons property doesn’t exist on pointers.

I’m trying to think if there’s a better approach but the easiest one that comes to mind is just to cast it to ArcRotateCameraPointersInput:

let pointers: BABYLON.ArcRotateCameraPointersInput = camera.inputs.attached.pointers as BABYLON.ArcRotateCameraPointersInput;
pointers.buttons = [0, 2];
2 Likes

This work like a charm and is perfectly fine for what I need!

A big thanks!!

1 Like