Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(globeControls): zoom on mouse position while using wheel #2041

Merged
merged 2 commits into from
Oct 11, 2024

Conversation

AnthonyGlt
Copy link
Contributor

Description

In globe view, zoom to the cursor position while using mousewheel.

Motivation and Context

Linked issue: #2011

@ftoromanoff
Copy link
Contributor

From what i understand of this PR, the purpose is that when you zoom in or out, the terrain under the mouse should always stay the same.

I tried this PR on a globe view. I noticed that zooming in on the mouse position seems to work when you are at close range but not at all when you are far enought and the zoom out never stay at the mouse position.
I havn't really gone into the code yet, and from my first test, the aim is not achived.

@AnthonyGlt
Copy link
Contributor Author

Thanks for the feedback.

I use zoom coordinates interpolated between the mouse position and the camera position that's why we don't go directly to the mouse position. This allows us to have a smoother transition. The behaviour is similar to the globe view of Google Maps. This alpha parameter (interpolation factor) is fixed but depending on feedback, could be edited before release. Concerning the zoom out, the factor is different (dezoom_interp) to avoid brutal rotation while zooming out. Same as alpha, this could be edited before release. I could even make these values as parameters of globecontrols, then the user could decide of the zoom behaviour.

@alexLuky
Copy link

@ftoromanoff @AnthonyGlt @jailln
By the way, take a look at this control. It has all useful functions.
I've mentioned it here in another discussion.

I think it would be great to integrate it in iTowns. Also for navigation under the ground or in hidden globe mode.

@gchoqueux
Copy link
Contributor

With your PR, the target position doesn't stay under the mouse cursor.

cursor.mp4

In the same condition with google earth, the target position stay under the cursor and doesn't come out of the screen.

cursorgoogleearth.mp4

@AnthonyGlt
Copy link
Contributor Author

I was referring to Google Maps (cf attached record). If this isn't an appropriate solution, an alternative could be implemented to have a similar zoom as Google Earth instead. Currenctly, I used a bit of a mix of the zoom function from the planarControls (interpolation) and the double click zoom from the globeControls (travel -> lookAtCoordinate).

map.mp4

@gchoqueux
Copy link
Contributor

it's good feature that improves the user experience.
I'm asking if it would be possible to factor your code for use in the handleDolly(event) method (call by pressing the middle button)

@AnthonyGlt
Copy link
Contributor Author

AnthonyGlt commented May 5, 2023

it's good feature that improves the user experience. I'm asking if it would be possible to factor your code for use in the handleDolly(event) method (call by pressing the middle button)

It's now implemented. I store the coordinates when the wheel button is clicked to zoom at the precise mouse location. But it's not really a "dolly" anymore.
In a future improvement, we could also store the first coordinates when the mouse wheel is used, to zoom to the exact mouse location. I did it on another branch of my repo. I did a draft PR on it: #2091

dolly.mp4

Copy link
Contributor

@gchoqueux gchoqueux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your PR is good way to add this feature
In the future we will have to think about how to solve the issue of the pointer shift when zooming.
This issue is accentuated when the zoomFactor is increased

src/Controls/GlobeControls.js Outdated Show resolved Hide resolved
src/Controls/GlobeControls.js Outdated Show resolved Hide resolved
src/Controls/GlobeControls.js Outdated Show resolved Hide resolved
Comment on lines 784 to 779
if (camPos.x * point.x < 0) { // Correct rotation at 180th meridian by using 0 <= longitude <=360
if (camPos.x - point.x > 180) { point.x += 360; } else if (point.x - camPos.x > 180) { camPos.x += 360; }
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a lookAtCoordinate issue, it's not support a correct rotation at 180th meridian by using 0 <= longitude <=360?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it's for the lerp method (interpolation). At the 180th meridian, we have values going from -179 to 179 (-179, -180/180, 179). So when interpolating a value between, for example, -170 and 172, we will obtain a value of 1 instead of the expecting -179.
To avoid that, I add 360 (to keep the same value modulo 360) and interpolate on positive values only. Then I substract the 360 to get the value back between -180 and 180.

src/Controls/GlobeControls.js Outdated Show resolved Hide resolved
src/Controls/GlobeControls.js Outdated Show resolved Hide resolved
Comment on lines 598 to 600
// Store first position of mouse when dolly
if (!dollyOrigin) {
dollyOrigin = this.view.getPickingPositionFromDepth(event.viewCoords); // mouse position
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you use pickedPosition instead of dollyOrigin, it seems to have a duplicate use

Copy link
Contributor Author

@AnthonyGlt AnthonyGlt Jun 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you're right, I could use pickedPosition.

this.view.getPickingPositionFromDepth(event.viewCoords, pickedPosition); // mouse position

But I need something to know if I'm doing a dolly (cf next comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StateControl should handle dolly state?

this.updateTarget();
const delta = -event.delta;
this.dolly(delta);
var point = dollyOrigin ?? this.view.getPickingPositionFromDepth(event.viewCoords); // mouse position
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a case where the dollyOrigin is null

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you rename point with explicit name, pickedPoint?

Copy link
Contributor Author

@AnthonyGlt AnthonyGlt Jun 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If dolly, then I don't update pickedPoint value. So I can use a boolean instead of dollyOrigin. What do you think ?

Suggested change
var point = dollyOrigin ?? this.view.getPickingPositionFromDepth(event.viewCoords); // mouse position
var pickedPoint = dollyFlag ? pickedPosition : this.view.getPickingPositionFromDepth(event.viewCoords); // mouse position

@@ -759,26 +770,36 @@ class GlobeControls extends THREE.EventDispatcher {
}
}

handleZoom(event) {
handleZoom(event, dollyOrigin) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember if there's a change of state for the zoom?
to handle the state zoom

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no state change for the zoom. I need to disguish between a regular zoom and a dolly one. Maybe I could

Suggested change
handleZoom(event, dollyOrigin) {
handleZoom(event, dollyFlag) {

with dollyFlag used to know if I need to update pickedPosition value

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no state change for the zoom. I need to disguish between a regular zoom and a dolly one. Maybe I could

This would seem to confirm that StateControl should manage its two states?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in my last commit, i've used directly the event type to distinguish between the 2 zoom

src/Controls/GlobeControls.js Outdated Show resolved Hide resolved
@AnthonyGlt
Copy link
Contributor Author

@Desplandis rebased 👍

@AnthonyGlt
Copy link
Contributor Author

@Desplandis rebased 👍

Copy link
Contributor

@mgermerie mgermerie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi ! Thanks for the improvement on zoom ! There is just one functional issue and a few remarks on form listed in my review.
Also, when you'll rebase your commits, could you add a breaking change note in the commit message ?

}
point.lerp( // point interpol between mouse cursor and cam pos
camPos,
(event.delta > 0 ? this.zoomInScale : this.zoomOutScale), // interpol factor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could directly use zoomScale defined on line 768 instead of having to check event.delta again.

src/Controls/GlobeControls.js Show resolved Hide resolved
src/Controls/GlobeControls.js Outdated Show resolved Hide resolved
src/Controls/GlobeControls.js Outdated Show resolved Hide resolved
Copy link
Contributor

@mgermerie mgermerie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your commit that indeed fixes the issue when zooming over minDistance value.

Trying out the example again, I came upon a glitch that occurs when zooming with the mouse wheel while orbiting (with ctrl+drag). On master branch, one can do so as you can test on view_3d_map example

When doing the same on your branch, I noticed that the camera moves strangely. Moreover, if I keep orbiting after having rolled the mouse wheel, the camera zooms out. The zoom should not be affected by an orbit movement.

I found out this is due to this line in lookAtCoordinates method which you call in your new handleZoom. It modifies cameraTarget which is used as a reference in handlePanoramic, thus for orbit movement. Removing this line fixes the issue I mention but brings out some more issues, notably with "dolly" movement (wheel-click + drag)

@AnthonyGlt
Copy link
Contributor Author

@mgermerie we could just cancel the Panoramic once the zoom is triggered (looks like how it's handled in Google Earth), WDYT ?
By adding
this.currentState = this.NONE;
before
this.dispatchEvent({ type: this.ZOOM._event, delta: event.deltaY, viewCoords });

in

onMouseWheel(event) {
event.preventDefault();
if (this.enabled && this.ZOOM.enable) {
viewCoords.copy(this._view.eventToViewCoords(event));
this.dispatchEvent({ type: this.ZOOM._event, delta: event.deltaY, viewCoords });
}
}

@Desplandis
Copy link
Contributor

@AnthonyGlt I agree with you on that point, we should disable this behavior (is this really used BTW?) to mimic the one in Earth and to decrease the complexity of state handling.

@mgermerie Any thoughts?

@AnthonyGlt
Copy link
Contributor Author

@Desplandis squashed 🎾

@mgermerie
Copy link
Contributor

@AnthonyGlt Your proposition is ok for me in terms of how to code it.
Now, I personally would not be so found of deleting it. The behavior already exists in master branch, and is implemented in Google Maps as well. However, this is a personal preference and I don't know if this behavior is used in iTowns so if you decide to remove it, it is ok for me as long as you document a breaking change.

BREAKING CHANGE: disabled multi actions when zooming
@AnthonyGlt
Copy link
Contributor Author

@AnthonyGlt Your proposition is ok for me in terms of how to code it. Now, I personally would not be so found of deleting it. The behavior already exists in master branch, and is implemented in Google Maps as well. However, this is a personal preference and I don't know if this behavior is used in iTowns so if you decide to remove it, it is ok for me as long as you document a breaking change.

@mgermerie I've added the breaking change to the commit

Thank you for your review

@AnthonyGlt AnthonyGlt merged commit 89bbbd8 into iTowns:master Oct 11, 2024
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 🍏 Adds a new feature
Projects
Status: Long term
Development

Successfully merging this pull request may close these issues.

6 participants