Как привязка свойства значения ползунка к свойству масштаба приводит к неправильному вычислению поворота в приложении маJAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Как привязка свойства значения ползунка к свойству масштаба приводит к неправильному вычислению поворота в приложении ма

Сообщение Anonymous »

Введение
В предыдущем посте я спросил, почему масштабирование с помощью ползунка не работает должным образом, когда я привязываю ползунок в двух направлениях к масштабу, чтобы гарантировать, что ползунок обновляется во время масштабирования колеса.
В комментарии James_D предположил, что ползунок может обновлять свойство масштаба до того, как будет вызван прослушиватель свойства значения ползунка. Он был прав, поэтому newScale - oldScale всегда равен 0, заставляя прослушиватель каждый раз возвращаться и завершать работу.
Исправления и проблема
Чтобы устранить проблему, я использовал слайдер.valueProperty().addListener((_, oldV, newV) вместо слайдер.valueProperty().addListener((_, _, newV) для доступа к предыдущему значению свойства до его изменения из-за привязки.
Хотя проблема исчезла при масштабировании с помощью ползунка, теперь она появляется при масштабировании с помощью колеса, перемещаясь в те же положения, которые упоминались ранее.
Я также заметил, что минимум ползунка значение не обновляется при изменении размера окна. Чтобы исправить это, CENTER_FIT_SCALE пересчитывается каждый раз, когда изменяется LayoutBoundsProperty области просмотра. Это позволяет установить минимальное значение ползунка на Math.min(viewport.getWidth() / image.getWidth(), viewport.getHeight() / image.getHeight()), представляющее коэффициент масштабирования, обеспечивающий изображение помещается в контейнер, сохраняя при этом соотношение сторон.
Чтобы убедиться, что масштабирование с помощью ползунка и масштабирование с помощью колеса используют правильные значения oldScale и newScale, я выполняю последовательные действия масштабирования, используя один метод, затем переключаюсь на другой, каждый раз печатая новый и старый масштаб. Моя цель — убедиться, что ползунок устанавливает новый масштаб, а колесо расчеты точны, и при возобновлении работы старая шкала становится новой. Это показало, что значения правильно установлены и рассчитаны, и они корректно возобновляются при переключении.
После обширных проверок и отладки проблема, похоже, связана с Pivot. В методе колеса параметры tx и ty устанавливаются следующим образом:

Код: Выделить всё

 tx.set(tx.get() + (oldScale - newScale) * pivotOnImage.getX());
ty.set(ty.get() + (oldScale - newScale) * pivotOnImage.getY());
А вот как они устанавливаются в слайдере:

Код: Выделить всё

 Point2D pivotOnImage = imageView.sceneToLocal(viewport.localToScene(viewport.getWidth() / 2, viewport.getHeight() / 2));
tx.set(tx.get() + (oldScale - newScale) * pivotOnImage.getX());
ty.set(ty.get() + (oldScale - newScale) * pivotOnImage.getY());
Теперь отладка значений поворота будет непростой, поскольку мне нужно поместить мышь в одну и ту же точку, чтобы сравнить вычисленные значения поворота обоими методами: когда свойство значения ползунка привязано, а когда нет. Однако различия довольно велики, поэтому ясно, что источником проблемы является вычисленный опорный элемент, в чем можно убедиться с помощью простых распечаток.
Другой факт, подтверждающий, что причиной является опорный элемент, является удаление PivotOnImage.getX(), PivotOnImage.getY() из формулы, устанавливающей tx и ty в слайдере, проблема переходит от метода колеса обратно к слайдеру!!!!
Обновленный код

Код: Выделить всё

import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Affine;
import javafx.stage.Stage;
import javafx.scene.control.Slider;
import javafx.util.StringConverter;

import java.util.Objects;

public class ZoomAndShiftImage extends Application {
private final double MAX_SCALE = 8.0;
private static double CENTER_FIT_SCALE;

@Override
public void start(Stage stage) {
Image image = new Image(Objects.requireNonNull(Objects.requireNonNull(getClass().getResourceAsStream("/playground/bird.jpg"))));
ImageView imageView = new ImageView(image);
imageView.setPreserveRatio(true);
imageView.setSmooth(true);

Affine affine = new Affine();
imageView.getTransforms().add(affine);

Pane viewport = new Pane(imageView);
Rectangle clip = new Rectangle();

clip.widthProperty().bind(viewport.widthProperty());
clip.heightProperty().bind(viewport.heightProperty());

viewport.setClip(clip);
viewport.setStyle("-fx-background-color: #1d2026;");

final Delta drag = new Delta();

DoubleProperty scale = new SimpleDoubleProperty(1.0);
DoubleProperty tx = new SimpleDoubleProperty(0);
DoubleProperty ty = new SimpleDoubleProperty(0);

Runnable applyTransform = () -> affine.setToTransform(scale.get(), 0, tx.get(), 0, scale.get(), ty.get());

scale.addListener((_, _, _) -> applyTransform.run());
tx.addListener((_, _, _) -> applyTransform.run());
ty.addListener((_, _, _) -> applyTransform.run());
applyTransform.run();

Slider slider = getSlider();
slider.setMax(MAX_SCALE);
slider.valueProperty().bindBidirectional(scale);

viewport.layoutBoundsProperty().addListener((_, _, _) -> {
CENTER_FIT_SCALE = Math.min(viewport.getWidth() / image.getWidth(), viewport.getHeight() / image.getHeight());
slider.setMin(CENTER_FIT_SCALE);

centerFit(imageView, viewport, scale, tx, ty);
});

viewport.setOnMousePressed(e -> {
if (e.getButton() == MouseButton.PRIMARY) {
// Save the current coordinates of the click
drag.x = e.getX();
drag.y = e.getY();
}
});

viewport.setOnMouseDragged(e -> {
double dx = e.getX() - drag.x;
double dy = e.getY() - drag.y;

drag.x = e.getX();
drag.y = e.getY();

tx.set(tx.get() + dx);
ty.set(ty.get() + dy);
});

viewport.setOnMouseClicked(e -> {
if (e.getClickCount() == 2 && e.getButton() == MouseButton.PRIMARY) {
scale.set(1.0);
tx.set(0.0);
ty.set(0.0);

centerFit(imageView, viewport, scale, tx, ty);
}
});

viewport.addEventFilter(ScrollEvent.SCROLL, e -> {
Point2D pivotOnImage = imageView.sceneToLocal(e.getSceneX(), e.getSceneY());

if (!imageView.getBoundsInLocal().contains(pivotOnImage))
return;

double direction = (e.getDeltaY() > 0) ? 1.1 : (1 / 1.1);
double oldScale = scale.get();
double newScale = clamp(oldScale, direction, CENTER_FIT_SCALE);

System.out.println("wheel: " + oldScale + " | " + newScale + " | "  + CENTER_FIT_SCALE);

if (newScale == 0) {
centerFit(imageView, viewport, scale, tx, ty);
return;
}

tx.set(tx.get() + (oldScale - newScale) * pivotOnImage.getX());
ty.set(ty.get() + (oldScale - newScale) * pivotOnImage.getY());

scale.set(newScale);
e.consume();
});

slider.valueProperty().addListener((_, oldV, newV) -> {
double oldScale = oldV.doubleValue();
double newScale = newV.doubleValue();

if (Math.abs(newScale - oldScale) < 1e-9)
return;

if (newV.doubleValue() 

Подробнее здесь: [url]https://stackoverflow.com/questions/79803086/how-binding-the-slider-value-property-to-a-scale-property-makes-the-pivot-calcul[/url]
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»