Higher Resolution / Interpolated Drag Events

I searched this on the forum but didn’t see any wholesome recommendations.

I’ve been building a “sequencer” type component where users can draw steps / shapes into a sequence.

One currently bad thing about the UX is that fast mouseDrags skip steps in the sequence as they don’t come in at a high enough resolution.

Are there any tricks to increase the mouseDrag resolution on a component? Or a method to interpolate between mouse drag events and fake mouse drags?

The common approach is to “draw” a line from the last point to the current point.

1 Like

hmm yeah that would work but I don’t see a way to create a fake mouse event from that line I could call mouseDrag from?

Maybe I should just move the entire mouse drag function to a function which takes a point instead of a mouse event? the issue is needing to check for modifiers

ahh I see… nevermind, I can copy the event and interpolate across a line, that’s not too bad thank you!

Sleepy morning here…

On Windows at least, WM_MOUSEMOVE which triggers the mouseMove in JUCE isn’t a real event, in that it doesn’t get generated every time the mouse moves. Instead, every time the message loop is idle, it checks if the mouse has moved since the last time it was idle, and if so generates a WM_MOUSEMOVE. This way, if your app is slow, mouse messages won’t pile up. Standard polling rate is 125 Hz, so if you’re getting less mouse messages than that, you are doing to much in your UI thread. If you are getting 125 Hz and you still need more, you’re going to need to interpolate.

3 Likes

Ohh nice – well yeah there’s a lot of brute force / uncached painting happening at the moment still all the little curvy section regenerating each paint even if they’re unchanged.

I’ll take a look at some optimizations before forcing high resolution when it may not be needed.

Thanks for that heads up!

The only way to get highly accurate input is to register with the native HID and get the device’s data that way (like the mouse or touch events). This is what game engines use and it allows working with fast and raw input in parallel to the message queue. JUCE doesn’t support this, but you can certainly look into it: About Raw Input - Win32 apps | Microsoft Docs

I switched on OpenGL rendering and optimized the graphics – but I allow very tiny grid sizes on the envelope with a pencil tool, almost like just draw your own wavetable.

The resolution was still an issue – I haven’t refined the code yet but something like this is a “relatively” unobtrusive solution:

if (!mProcessingDragInterpolation) {
        
        mProcessingDragInterpolation = true;
        
        float horizontal_grid_size = (float)getWidth() / (float)getCurrentNumberHorizontalGridPoints();
        
        float distanced_traveled = event.getPosition().x - mLastDragPosition.x;
        
        if (fabs(distanced_traveled) > horizontal_grid_size) {
            
            int steps_needed = floor(fabs(distanced_traveled) / horizontal_grid_size) + 1;
                        
            float distance_per_step_x = (event.getPosition().x - mLastDragPosition.x) / steps_needed;
            float distance_per_step_y = (event.getPosition().y - mLastDragPosition.y) / steps_needed;
            
            for (int i = 1; i <= steps_needed; i++) {
                juce::Point<float> new_pos = mLastDragPosition;
                new_pos.x += distance_per_step_x * i;
                new_pos.y += distance_per_step_y * i;
                MouseEvent fake_event = event.withNewPosition(new_pos);
                
                mouseDrag(fake_event);
            }
        }
        
        mProcessingDragInterpolation = false;
    }
    
    if (!mProcessingDragInterpolation) {
        mLastDragPosition = event.getPosition().toFloat();
    }

It’s definitely a tad bit hacky right now and needs a rewrite – but atleast this way I don’t need to worry about cross platform compatibility or stepping outside of JUCE.