Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion src/rocky/vsg/MapManipulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,13 +458,49 @@ MapManipulator::reinitialize()
_thrown = false;
_delta.set(0.0, 0.0);
_throwDelta.set(0.0, 0.0);
_zoomInertia = ZoomVelocityState();
_continuousDelta.set(0.0, 0.0);
_continuous = 0;
_lastAction = ACTION_NULL;
_previousMove.clear();
clearEvents();
}

void
MapManipulator::cancelZoomInertia()
{
_zoomInertia.velocity = 0.0;
_zoomInertia.isActive = false;
}

bool
MapManipulator::serviceZoomInertia(vsg::time_point now)
{
if (!_zoomInertia.isActive)
return false;

double dt = to_seconds(now - _previousTime);
if (dt <= 0.0)
return _zoomInertia.isActive;

// Apply velocity as zoom delta
double frameDelta = _zoomInertia.velocity * dt;
zoom(0.0, frameDelta);

// Frame-rate independent exponential decay
double decayFactor = std::pow(settings.scrollZoomFriction, dt * 60.0);
_zoomInertia.velocity *= decayFactor;

// Stop when below threshold
if (std::abs(_zoomInertia.velocity) < settings.scrollZoomThreshold)
{
_zoomInertia.velocity = 0.0;
_zoomInertia.isActive = false;
}

return _zoomInertia.isActive;
}

bool
MapManipulator::createLocalCoordFrame(const vsg::dvec3& worldPos, vsg::dmat4& out_frame) const
{
Expand Down Expand Up @@ -999,6 +1035,9 @@ MapManipulator::apply(vsg::ButtonPressEvent& buttonPress)

//std::cout << "ButtonPressEvent" << std::endl;

// Cancel any active zoom inertia
cancelZoomInertia();

// simply record the button press event.
clearEvents();

Expand Down Expand Up @@ -1155,6 +1194,15 @@ MapManipulator::apply(vsg::FrameEvent& frame)

serviceTask(frame.time);

// Service zoom inertia
if (_zoomInertia.isActive)
{
if (serviceZoomInertia(frame.time))
{
_dirty = true;
}
}

if (isSettingViewpoint())
{
setViewpointFrame(frame.time);
Expand Down Expand Up @@ -1884,7 +1932,28 @@ MapManipulator::handleAction(
case ACTION_ZOOM:
case ACTION_ZOOM_IN:
case ACTION_ZOOM_OUT:
_task.set(TASK_ZOOM, d, duration, time);
if (settings.scrollZoomEnabled)
{
// Velocity-based zoom
double direction = (action._dir == DIR_UP) ? -1.0 : 1.0;
_zoomInertia.velocity += direction * settings.scrollZoomAcceleration * settings.scrollSensitivity;

// Clamp to max velocity
if (std::abs(_zoomInertia.velocity) > settings.maxScrollZoomVelocity)
{
_zoomInertia.velocity = ((_zoomInertia.velocity > 0) ? 1.0 : -1.0) * settings.maxScrollZoomVelocity;
}

_zoomInertia.isActive = true;
_dirty = true;
if (_context)
_context->requestFrame();
}
else
{
// Legacy task-based zoom
_task.set(TASK_ZOOM, d, duration, time);
}
break;

default:
Expand Down
28 changes: 28 additions & 0 deletions src/rocky/vsg/MapManipulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,21 @@ namespace ROCKY_NAMESPACE
//! Whtehr to zoom towards the mouse cursor when zooming
bool zoomToMouse = true;

//! Enable velocity-based scroll zoom (vs discrete task-based)
bool scrollZoomEnabled = true;

//! Acceleration per scroll event (zoom units per scroll notch)
double scrollZoomAcceleration = 0.3;

//! Friction coefficient - velocity retained per 1/60s frame (0.88 = 12% decay)
double scrollZoomFriction = 0.88;

//! Minimum velocity to keep animating
double scrollZoomThreshold = 0.001;

//! Maximum zoom velocity cap (prevents jarring zoom from rapid scrolling)
double maxScrollZoomVelocity = 3.0;


//! Assigns behavior to the action of dragging the mouse while depressing one or
//! more mouse buttons and modifier keys.
Expand Down Expand Up @@ -430,6 +445,12 @@ namespace ROCKY_NAMESPACE
//vsg::time_point _previousTick;
};

//! State for velocity-based scroll zoom
struct ZoomVelocityState {
double velocity = 0.0;
bool isActive = false;
};

// "ticks" the resident Task, which allows for multi-frame animation of navigation
// movements.
bool serviceTask(vsg::time_point);
Expand Down Expand Up @@ -472,6 +493,12 @@ namespace ROCKY_NAMESPACE
virtual bool handlePointAction(const Action& type, float mx, float my, vsg::time_point time);
virtual void handleMovementAction(const ActionType& type, vsg::dvec2 delta);

//! Services the zoom velocity animation each frame
bool serviceZoomInertia(vsg::time_point now);

//! Cancels any active zoom inertia
void cancelZoomInertia();

void clearEvents();
vsg::ref_ptr<MapNode> getMapNode() const;

Expand Down Expand Up @@ -526,6 +553,7 @@ namespace ROCKY_NAMESPACE
bool _thrown;
vsg::dvec2 _throwDelta;
vsg::dvec2 _delta;
ZoomVelocityState _zoomInertia;
vsg::dmat4 _viewMatrix;
State _state;
Task _task;
Expand Down