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

ScaleUpdateDetails focalPointDelta #89

Closed
DonRubiczek opened this issue Feb 29, 2024 · 27 comments
Closed

ScaleUpdateDetails focalPointDelta #89

DonRubiczek opened this issue Feb 29, 2024 · 27 comments

Comments

@DonRubiczek
Copy link

Hey, I am working with PdfViewer version 0.4.47. Over my pdfViewer I have custom positioned widget placed in stack that i want to scale on zoom (its size and position). I am using onInteractionUpdate callback, getting scale from ScaleUpdateDetails, multiplying its size with it works correctly but I also want to scale position and I assumed that focalPointDelta could be useful in that case - documentation says ( The amount the gesture's focal point has moved in the coordinate space of the event receiver since the previous update.) but its value is always 0 and I did not find any part in documentation which explains how to use it.

What should i do to use it?
Or is there any other way to scale position on zoom?
I would really appreciate any help.

@espresso3389
Copy link
Owner

The most important design dicision on InteractiveViewer (and of course, PdfViewer) is
that the controller assodicated to the widget is derived from ValueNotifier<Matrix4>.

It means that you can be notified when any zoom or scroll position change by subscribing(addListener/removeListener) the controller.

The largest headache with the approach is that it's too difficult to obtain zoom/offset from the matrix. But PdfViewerController provides you with several helper methods that eases to get current zoom, center position, and so on.

@espresso3389
Copy link
Owner

but its value is always 0 and I did not find any part in documentation which explains how to use it.

I've checked the InteractiveViewer's code (that is just forked version from the Flutter original), only the following lines handles focalPointDelta:

if (event.kind == PointerDeviceKind.trackpad &&
!widget.trackpadScrollCausesScale) {
// Trackpad scroll, so treat it as a pan.
widget.onInteractionStart?.call(
ScaleStartDetails(
focalPoint: event.position,
localFocalPoint: event.localPosition,
),
);
final Offset localDelta = PointerEvent.transformDeltaViaPositions(
untransformedEndPosition: event.position + event.scrollDelta,
untransformedDelta: event.scrollDelta,
transform: event.transform,
);

It seems that the value might be non-zero if the event is from PointerDeviceKind.trackpad and NOT SCALING. So your assumption is anyway wrong.

@DonRubiczek
Copy link
Author

Thanks for the reply. Yes, I also tried using matrix properties eg. X,Y, delta X, delta Y, but as you said it was not working as expected. I will try convert my position scaling algorithm to use centerPosition and reply with the result. Also thanks for the explanation with focalPointDelta.

@DonRubiczek
Copy link
Author

DonRubiczek commented Mar 5, 2024

I am working on widgets scaling/ positioning on zooming using PdfViewerController properties (currentZoom, centerPosition). I added listener to controller, but on zooming it is called 2 times with the same currentZoom, but different centerPosition offset. Is it rule that for every zoom value, listener will be called 2 times for the same currentZoom value?

To be honest it would be nice feature to add to listener parameters indicating whether it was zoomed or scrolled.

@espresso3389
Copy link
Owner

but on zooming it is called 2 times with the same currentZoom, but different centerPosition offset. Is it rule that for every zoom value, listener will be called 2 times for the same currentZoom value?

To be honest it would be nice feature to add to listener parameters indicating whether it was zoomed or scrolled.

Bascally, they're handled on a single matrix value using asynchronous animations. And I cannot tell when or how many times it is changed. And future versions may change the internal logic anyway.

@DonRubiczek
Copy link
Author

DonRubiczek commented Mar 8, 2024

As u advised above I tried using PdfViewerController properties to scale widget above pdfViewer, but using centerPosition does not give results as expected and I do not know if I am doing anything wrong or is it not working correctly. I used onInteractionStart and onInteractionUpdate callbacks because they are called just one time with the same scale value.
OnInteractionStart I am setting startZoom and startCenterPosition properties that are later used as baseCenterPosition and baseScale to calculate position and size of widget after zooming.

onInteractionStart: (details) {
  pageController.onStartCenterPosition.value = pageController.pdfViewerController.centerPosition;
  pageController.onStartZoomCombined.value = pageController.pdfViewerController.currentZoom;
},

On interaction callbacks are called not only on scale but on every user interaction, so at begginning of onInteractionUpdate I am checking if scale value changed regarding one set in onInteractionStart and if yes then I am starting to resize widget size and position.

onInteractionUpdate: (details) {

        final startZomm =
            (pageController.onStartZoomCombined.value * 1000).round();
        final updatedZoom =
            (pageController.pdfViewerController.currentZoom * 1000).round();

        if (startZomm == updatedZoom) {
          return;
        }

        pageController.combinedScale.value =
            pageController.pdfViewerController.currentZoom;
        pageController.stampWidth.value =
            pageController.baseStampWidth * pageController.combinedScale.value;
        pageController.stampHeight.value =
            pageController.baseStampHeight * pageController.combinedScale.value;

        final deltaX = pageController.onStartCenterPosition.value.dx -
            pageController.pdfViewerController.centerPosition.dx;
        final deltaY = pageController.onStartCenterPosition.value.dy -
            pageController.pdfViewerController.centerPosition.dy;

        pageController.position.value = Offset(
          pageController.basePosition.dx +
              (deltaX * pageController.combinedScale.value),
          pageController.basePosition.dy +
              (deltaY * pageController.combinedScale.value),
        );
      },

After scale check I am setting new scaleValue and resizing widget regarding new scale and baseSize. After that I am trying to scale position counting deltaX, deltaY then multiplying it by scale and adding to start position.
I made a lot of tests and the delta calculated in this way has a different value than the shift of the PDF view on the screen after scaling. Am I doing anything wrong?
Scalling widget size on zoom works correctly, just position is wrong.

@DonRubiczek
Copy link
Author

For example:

OnInteractionStartPosX = 297.6
OnInteractionStartPosY = 317.3
OnInteractionStartScale = 2.9032258064516125

OnInteractionUpdatePosX = 317.3
OnInteractionUpdatePosY = 314.7
OnInteractionUpdateScale = 2.972864949361671

then

OnInteractionStartPosX = 302
OnInteractionStartPosY = 314.7
OnInteractionStartScale = 2.972864949361671

OnInteractionUpdatePosX = 375.3
OnInteractionUpdatePosY = 269.9
OnInteractionUpdateScale = 5

Calculated deltaX = 77.7 * 5 = 388,5
Calculated deltaY = 47.4 * 5 = 237

real pdfViewer deltaX = 543.1
real pdfViewer deltaY = 140

For every zooming onInteractionCallbacs are called two times. First one with small zoom change and second one with target zoom.

@espresso3389
Copy link
Owner

Sorry, I didn't understand what you want to achieve. Could you please explain what is your first motivation?

@DonRubiczek
Copy link
Author

Sure, I want to make pdfViewer that allows user to scroll and zoom it. Above pdfViewer on stack I have rectangle that user can move around the screen to designate a place for signature. I want this rectangle to scale its size and position on zoom to be exactly the same relative to pdfViewer.
Screenshot 2024-03-08 at 11 42 06

@espresso3389
Copy link
Owner

I'm thinking...

One possible option is PdfViewerParams.pageOverlaysBuilder, which directly places widgets on the page.

But it may not be suitable for your situation.

@DonRubiczek
Copy link
Author

DonRubiczek commented Mar 8, 2024

I am placing my rectangle in stack above pdfViewer and moving it is working correctly, the only thing that I have problem with is scalling its position on Zoom using controllers' CenterPosition to be exactly the same relatively to pdfViewer. Do you have any idea why?

Above I tried step by step explain how I am trying to do it.

@espresso3389
Copy link
Owner

OK, so we need to create some simple code to illustrate/check/test the current behavior.

@DonRubiczek
Copy link
Author

Do you want me to create that and share it?

@espresso3389
Copy link
Owner

Please wait. I'm still thinking.

PdfViewerParams.viewerOverlayBuilder is also implemented using Stack and the approach may be really straight forward. It realizes scroll thumb.
It uses controller's addListener to receive updates on refreshed on each update.

It calculate the view position based on controller.value.x / controller.value.y:

final thumbSize = widget.thumbSize ?? const Size(25, 40);
final view = widget.controller.visibleRect;
final all = widget.controller.documentSize;
if (all.height <= view.height) return const SizedBox();
final y = -widget.controller.value.y / (all.height - view.height);
final vh = view.height * widget.controller.currentZoom - thumbSize.height;
final top = y * vh;

@espresso3389
Copy link
Owner

I'm not sure what is the best approach for us. Furthermore, I don't know whether we already have the tool or not.

@DonRubiczek
Copy link
Author

I will think about your approach with scroll thumb and try to convert my algorithm this way, and get back to you.

@DonRubiczek
Copy link
Author

Using controller's visibleRect, the intended effect was achieved and scaling of the relative position in relation to the PDF when zooming works correctly. Thank you for the idea. Also I have one more question. I am trying to use PdfViewerParams - minScale and maxScale. Setting maxScale works correctly, but when I am trying to set minScale it does not effect viewer. PdfViewerController has its own value at minimum scale, equal to alternativeFitScale. Is it bug or am I using it wrongly?

@espresso3389
Copy link
Owner

Ah, minScale is currently not used...
Basically, I think there's no pragmatic reason to make the page size smaller than alternativeFitScale. So it's actually a design and minScale should be removed.

But do you have any reason to use minScale? If there's some reason to justify the existance of minScale, I'll make it work in some future version (though there're many issues around it if used with boundaryMargin or such).

@DonRubiczek
Copy link
Author

To be honest I have. When I load a document, it initially tries to occupy the entire available screen for example zooming it to 290% and I want this 2.9 to be my minimum scale. I mean i want to enable scaling but just for zooming in, not zooming out bigger than full screen.

@espresso3389
Copy link
Owner

Do you want to disable alternativeFitScale ?

@DonRubiczek
Copy link
Author

DonRubiczek commented Mar 11, 2024

Yes, I want to have the ability to disable alternativeFitScale in some cases. Also I want to be able to set minScale to some value and not allow to zoom out smaller than this value. In my case this value is zoom ratio of page to fit full width - maybe this value also can be useful for someone like alternativeFitScale.

@DonRubiczek
Copy link
Author

DonRubiczek commented Mar 14, 2024

Screenshot 2024-03-14 at 12 01 32

Is there any way to move pdfImage to top instead of centering it like in this example?
@espresso3389

@espresso3389
Copy link
Owner

For minScale, 1.0.50/0.4.50/0.0.50 introduces PdfViewerParams.useAlternativeFitScaleAsMinScale, You can set the value to false to enable minScale.

@DonRubiczek
Copy link
Author

What about this?
#89 (comment)

On above photo I am displaying a map pdf file of 3600 width and 1700 height. Is there any way to move map image it to top instead of center it?

@DonRubiczek
Copy link
Author

@espresso3389 What about that? #89 (comment)

@espresso3389
Copy link
Owner

Currently, it's too difficult to place the PDF on the top of the view. It's kind of design tradeoff or such and if I introduce some new feature, it breaks the current consistency. I should think some better handling...

@espresso3389
Copy link
Owner

For the centering issue, I opened a new issue #111.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants