mpetroff / pannellum

Pannellum is a lightweight, free, and open source panorama viewer for the web.
https://pannellum.org/
MIT License
4.22k stars 717 forks source link

Operation of minPitch and maxPitch? #206

Closed MmmDee closed 8 years ago

MmmDee commented 8 years ago

edit: I just (re)read issues #169, #92, #13. I'm still unclear as to the preferred method to limit pitch changes to zero.

On first impression, minPitch and maxPitch would indicate to me the amount an image is allowed to be pitched down and up (respectively). Meaning if I center an image's horizon to "pitch": 0 and have "minPitch": 0 and "maxPitch": 0, it should not be possible to move the image down/up.

Here's the code fragment:

pannellum.viewer('panorama', {
    "type": "equirectangular",
    "hfov": 60,
    "haov": 60,
    "vaov": 100,
    "pitch": 0,
    "minHfov": 60,
    "maxHfov": 60,
    "minYaw": -30,
    "maxYaw": 30,
    "minPitch": 0,
    "maxPitch": 0,
    "autoLoad": true,
    "hotSpotDebug": true,
    "panorama": "https://pannellum.org/images/tocopilla-preview.jpg"
});

With the preceding parameters, the apparent pitch is +/- 21.05.

Am I misunderstanding? I see in the other issues, things work as "expected" if I calculate (see calculation in next comment):

minPitch= -vfov/2 
maxPitch= vfov/2

This wasn't intuitive to me and requires calculation, whereas setting minPitch=maxPitch=0 is very (semantically) straightforward (to me).

MmmDee commented 8 years ago

Just for others, here's a quick little routine to calculate minPtich and maxPitch to keep image from being able to move down/up (this is the way in which pannellum.js calculates vfov and minPitch/maxPitch limits). Note, this is particular to a specific hfov. Also note, getPitchBounds() returns the JSON-configured or setPitchBounds() returned minPitch and maxPitch, not the extent of "pitch" in the image.

function getMinMaxPitch() {
    var canvas = viewer.getRenderer().getCanvas();
    var vfov = 2 * Math.atan(Math.tan(viewer.getHfov() / 180 * Math.PI * 0.5) /
                (canvas.width / canvas.height)) / Math.PI * 180;

// Enabling the following line will lock pitch changes
//  viewer.setPitchBounds([(-vfov/2),(vfov/2)]);  

    alert("minPitch=" + (-vfov/2).toFixed(3) + "\n" + "maxPitch=" + (vfov/2).toFixed(3));
};

Perhaps we could get a way to lock the pitch...

mpetroff commented 8 years ago

The minPitch and maxPitch parameters originally set the limits for the pitch of the center of the viewer as this was the easiest to implement. However, the most common use was to limit the viewer to the extent of a partial panorama, which proved problematic as the values that were used needed to change based on the HFOV and the aspect ratio of the viewer. Therefore, I changed the behavior in f6e972a152af2686e6d3307c543b08fa26f52cf3 to set the limits for the edge of the viewer. What's the use case for locking the pitch? I'm not sure I see one.

MmmDee commented 8 years ago

The case came about after I was thinking of issue #205 and thought it might be nice to load a preview image and click on it. So I was experimenting with loading a portion of a panoramic image (or any non-panoramic image for that matter--perhaps a very small non-panoramic image), and setting up a mousedown event to do a loadScene. The reason was to keep a user from rotating and pitching the pseudo preview image since it was not really a panoramic image.

Preventing rotation is easy by setting hfov (ie 60), then minYaw = -hfov/2 and maxYaw = hfov/2. Preventing pitching is/was not quite so easy and led to the short pitch-locking routine above.

The following toy demo illustrates:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Preview</title>
    <link rel="stylesheet" href="http://mysite/pannellum/pannellum.css">
    <script type="text/javascript" src="http://mysite/pannellum/pannellum.js"></script>
    <style>
    #panorama {
        width: 877px;
        height: 584px;
    }
    </style>
</head>
<body>
<div id="panorama"></div>
<script>
viewer = pannellum.viewer('panorama', {
    "default": {
        "type": "equirectangular",
        "firstScene": "preview",
        "autoLoad": true
    },
    "scenes": {
        "preview": {
            "hfov": 60,
            "haov": 60,
            "minHfov": 60,
            "maxHfov": 60,
            "vaov": 60,
            "pitch": 0,
            "showZoomCtrl": false,
            "showFullscreenCtrl": false,
            "panorama": "https://pannellum.org/images/tocopilla-preview.jpg"
        },
        "house": {
            "showZoomCtrl": true,
            "panorama": "https://pannellum.org/images/bma-0.jpg",
        }
    }
});

function lockPreview() {
    lockPitch();
    lockYaw();
}

function lockPitch() {
    if (viewer) {
        var renderer = viewer.getRenderer();
        if (renderer) {
            var canvas = renderer.getCanvas();
            if (canvas) {
                var vfov = 2 * Math.atan(Math.tan(viewer.getHfov() / 180 * Math.PI * 0.5) /
                            (canvas.width / canvas.height)) / Math.PI * 180;
                var minPitch = -vfov / 2;
                var maxPitch =  vfov / 2;
                viewer.setPitchBounds([minPitch,maxPitch]);
                viewer.setPitch(0);
            } else alert ("lockPitch: No canvas");
        } else alert ("lockPitch: No renderer");
    } else alert ("lockPitch: No viewer");
};

function lockYaw() {
    if (viewer) {
               var hfov = viewer.getHfov();
               var yaw = viewer.getYaw();
               var minYaw = yaw + -hfov / 2;
               var maxYaw =  yaw + (hfov / 2);
               viewer.setYawBounds([minYaw, maxYaw]);
    } else alert ("lockYaw: No viewer");
};

function mousedownListener() {
        viewer.off("load",lockPreview);
        viewer.off("mousedown",mousedownListener);
        viewer.loadScene("house", 0, 5, 110);
};

viewer.on('load', lockPreview);
viewer.on('mousedown', mousedownListener);
</script>

</body>
</html>

It turned out to not be such a big deal, because when the "preview" is loaded and the mousedown event armed, then as soon as the preview is "touched", the loadScene is triggered.

MmmDee commented 8 years ago

I'm closing this as I can appreciate the current semantic definition of minPitch & maxPitch and suitable "workarounds" exist.