JavaFX 如何在不改变线宽的情况下缩放路径的坐标?
JavaFX How do I scale the coordinates of a Path without altering the line width?
我目前正在制作具有缩放和平移功能的阶梯折线图。由于我需要处理的数据量非常大,每次调用 layoutPlotChildren() 时,我都无法重新创建步进线的整个路径。所以我的想法是创建一次路径元素,然后在缩放和平移事件时对其进行转换。
到目前为止一切都按计划进行,但我在尝试扩展路径时遇到了一个大问题。由于 Path class 继承自 Shape,因此在 Graph 上缩放不仅会变换路径的坐标,还会改变其厚度。
我创建了一个最小的工作示例来展示问题:
public class Sandbox extends Application
{
@Override public void start(Stage primaryStage)
{
Path path = new Path();
path.getElements().add(new MoveTo(0, 0));
path.getElements().add(new LineTo(100, 0));
path.getElements().add(new LineTo(100, 100));
path.getElements().add(new LineTo(200, 100));
path.setScaleY(3);
Pane pane = new StackPane(path);
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
如您所见,坐标按预期缩放,但所有水平线的粗度都是以前的三倍。我完全理解,为什么会这样,但我想不出任何防止线条变粗的可能性。您对我如何在确保可接受的性能的同时解决这个问题有什么建议吗?
请注意,这不是 Shape
class 的问题,而是缩放实际作用的问题:Node
中的 A 用因子 x
缩放每个距离的渲染时间都是未缩放 Node
的 x
倍。这包括任何线条的粗细。
然而,您可以通过将坐标乘以比例因子来修改路径元素的坐标:
// class for holing a pair of (x, y) properties with method for modifying the values based on scale
public static class PointScaler {
private final DoubleProperty xProperty;
private final DoubleProperty yProperty;
private final double x0;
private final double y0;
public PointScaler(DoubleProperty xProperty, DoubleProperty yProperty) {
this.xProperty = xProperty;
this.yProperty = yProperty;
// save untransformed values
this.x0 = xProperty.get();
this.y0 = yProperty.get();
}
public void setScale(double factor) {
xProperty.set(factor * x0);
yProperty.set(factor * y0);
}
}
private double scaleFactor = 1;
@Override
public void start(Stage primaryStage) {
Path path = new Path();
MoveTo m0 = new MoveTo(0, 0);
LineTo l0 = new LineTo(100, 0);
LineTo l1 = new LineTo(100, 100);
LineTo l2 = new LineTo(200, 100);
path.getElements().addAll(m0, l0, l1, l2);
// create list with scaler for each (x,y) pair in the path elements
List<PointScaler> scalers = new ArrayList<>();
scalers.add(new PointScaler(m0.xProperty(), m0.yProperty()));
scalers.add(new PointScaler(l0.xProperty(), l0.yProperty()));
scalers.add(new PointScaler(l1.xProperty(), l1.yProperty()));
scalers.add(new PointScaler(l2.xProperty(), l2.yProperty()));
Pane pane = new StackPane(path);
Scene scene = new Scene(pane, 800, 600);
// change scale on click of mouse buttons
scene.setOnMouseClicked(evt -> {
if (evt.getButton() == MouseButton.PRIMARY) {
scaleFactor *= 1.2;
} else if (evt.getButton() == MouseButton.SECONDARY) {
scaleFactor /= 1.2;
} else {
return;
}
// apply scale to path points
for (PointScaler scaler : scalers) {
scaler.setScale(scaleFactor);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
请注意,setScale
方法保持 x 和 y 比例不变,但修改该方法以适用于不同的变换应该不会太难。请注意,某些 PathElement
像 CubicCurveTo
和 QuadCurveTo
需要多个 PointScaler
实例。
基于,我创建了自己的解决方案,这是步骤线(我最初的问题)的简化版本,但不如上述响应灵活。为了完整起见,我将 post 我的回答在这里,但我仍然认为 fabians 的答案必须是公认的。
public class Sandbox extends Application
{
private DoubleProperty scaleX = new SimpleDoubleProperty(1d);
private DoubleProperty scaleY = new SimpleDoubleProperty(1d);
@Override public void start(Stage primaryStage)
{
Path path = new Path();
path.getElements().add(new MoveTo(0, 0));
path.getElements().add(createLineTo(100, 0));
path.getElements().add(createLineTo(100, 100));
path.getElements().add(createLineTo(200, 100));
scaleY.setValue(3);
Pane pane = new StackPane(path);
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private LineTo createLineTo(double x, double y)
{
LineTo lineTo = new LineTo();
lineTo.xProperty().bind(scaleX.multiply(x));
lineTo.yProperty().bind(scaleY.multiply(y));
return lineTo;
}
}
这主要防止了 foreach 循环和创建单独的 class 的需要,但是如前所述,这不像 fabians answer 那样通用。
另一种方法是将逆比例应用于每个形状的 strokeWidthProperty。这可以通过绑定轻松完成。
因此,如果您的路径按 s = 3 缩放,例如您必须将 strokeWidthProperty 绑定到 w*1/3,其中 w 是标称笔划宽度。
我目前正在制作具有缩放和平移功能的阶梯折线图。由于我需要处理的数据量非常大,每次调用 layoutPlotChildren() 时,我都无法重新创建步进线的整个路径。所以我的想法是创建一次路径元素,然后在缩放和平移事件时对其进行转换。
到目前为止一切都按计划进行,但我在尝试扩展路径时遇到了一个大问题。由于 Path class 继承自 Shape,因此在 Graph 上缩放不仅会变换路径的坐标,还会改变其厚度。
我创建了一个最小的工作示例来展示问题:
public class Sandbox extends Application
{
@Override public void start(Stage primaryStage)
{
Path path = new Path();
path.getElements().add(new MoveTo(0, 0));
path.getElements().add(new LineTo(100, 0));
path.getElements().add(new LineTo(100, 100));
path.getElements().add(new LineTo(200, 100));
path.setScaleY(3);
Pane pane = new StackPane(path);
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
}
如您所见,坐标按预期缩放,但所有水平线的粗度都是以前的三倍。我完全理解,为什么会这样,但我想不出任何防止线条变粗的可能性。您对我如何在确保可接受的性能的同时解决这个问题有什么建议吗?
请注意,这不是 Shape
class 的问题,而是缩放实际作用的问题:Node
中的 A 用因子 x
缩放每个距离的渲染时间都是未缩放 Node
的 x
倍。这包括任何线条的粗细。
然而,您可以通过将坐标乘以比例因子来修改路径元素的坐标:
// class for holing a pair of (x, y) properties with method for modifying the values based on scale
public static class PointScaler {
private final DoubleProperty xProperty;
private final DoubleProperty yProperty;
private final double x0;
private final double y0;
public PointScaler(DoubleProperty xProperty, DoubleProperty yProperty) {
this.xProperty = xProperty;
this.yProperty = yProperty;
// save untransformed values
this.x0 = xProperty.get();
this.y0 = yProperty.get();
}
public void setScale(double factor) {
xProperty.set(factor * x0);
yProperty.set(factor * y0);
}
}
private double scaleFactor = 1;
@Override
public void start(Stage primaryStage) {
Path path = new Path();
MoveTo m0 = new MoveTo(0, 0);
LineTo l0 = new LineTo(100, 0);
LineTo l1 = new LineTo(100, 100);
LineTo l2 = new LineTo(200, 100);
path.getElements().addAll(m0, l0, l1, l2);
// create list with scaler for each (x,y) pair in the path elements
List<PointScaler> scalers = new ArrayList<>();
scalers.add(new PointScaler(m0.xProperty(), m0.yProperty()));
scalers.add(new PointScaler(l0.xProperty(), l0.yProperty()));
scalers.add(new PointScaler(l1.xProperty(), l1.yProperty()));
scalers.add(new PointScaler(l2.xProperty(), l2.yProperty()));
Pane pane = new StackPane(path);
Scene scene = new Scene(pane, 800, 600);
// change scale on click of mouse buttons
scene.setOnMouseClicked(evt -> {
if (evt.getButton() == MouseButton.PRIMARY) {
scaleFactor *= 1.2;
} else if (evt.getButton() == MouseButton.SECONDARY) {
scaleFactor /= 1.2;
} else {
return;
}
// apply scale to path points
for (PointScaler scaler : scalers) {
scaler.setScale(scaleFactor);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
请注意,setScale
方法保持 x 和 y 比例不变,但修改该方法以适用于不同的变换应该不会太难。请注意,某些 PathElement
像 CubicCurveTo
和 QuadCurveTo
需要多个 PointScaler
实例。
基于
public class Sandbox extends Application
{
private DoubleProperty scaleX = new SimpleDoubleProperty(1d);
private DoubleProperty scaleY = new SimpleDoubleProperty(1d);
@Override public void start(Stage primaryStage)
{
Path path = new Path();
path.getElements().add(new MoveTo(0, 0));
path.getElements().add(createLineTo(100, 0));
path.getElements().add(createLineTo(100, 100));
path.getElements().add(createLineTo(200, 100));
scaleY.setValue(3);
Pane pane = new StackPane(path);
Scene scene = new Scene(pane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private LineTo createLineTo(double x, double y)
{
LineTo lineTo = new LineTo();
lineTo.xProperty().bind(scaleX.multiply(x));
lineTo.yProperty().bind(scaleY.multiply(y));
return lineTo;
}
}
这主要防止了 foreach 循环和创建单独的 class 的需要,但是如前所述,这不像 fabians answer 那样通用。
另一种方法是将逆比例应用于每个形状的 strokeWidthProperty。这可以通过绑定轻松完成。 因此,如果您的路径按 s = 3 缩放,例如您必须将 strokeWidthProperty 绑定到 w*1/3,其中 w 是标称笔划宽度。