0

I have a custom component that manages a set of shape nodes (path).

I also need to be able to horizonatal zoom and pan, so I thought I'd put all the shapes in a group and scale/translate the X axis.

However, I do not want the stroke width to scale, only the size of the shapes.

How do I make only the geometry scale? I could post some code but it is pretty much the same zooming code as this post but using different shapes.

JavaFX correct scaling

Any way to turn off stroke scaling? Change scaling behavior?

Community
  • 1
  • 1
John Baker
  • 89
  • 12
  • I have had the same use case as well. http://stackoverflow.com/questions/22048114/drawing-transform-independent-layout-bounds-in-javafx – Oliver Jan Krylow Dec 04 '15 at 15:35
  • Thanks for the tip but I don't think your approach will cover my situation. Next I am going to try manually rebuilding all the shapes at a different scale/offset each time I zoom or pan. Then replace the entire previous group with the new one. One by one would probably affect performance too much, but swapping them in one go might be fine. I will check now. – John Baker Dec 04 '15 at 16:26

1 Answers1

0

I had a hard time following the answer in the link provided, so I give my solution which I think is a bit easier to understand. The problem is, if you scale a node the width of the stroke also scale's and you get these big fat lines for the shape. The solution that I use is that I apply a scaling transform to the node, and I also descale the width of the stroke. I'll show the code for two different scenarios: 1.) I used an SVGPath node and show how to scale the svg without scaling the stroke line, and 2.) I'll do the same thing using the GraphicsContent on a canvas. Here's the code snippet

private void start() {
    Task task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            double originalWidth, originalHeight, scaleX, scaleY;


            // here's scaling using SVGPath Node type
            SVGPath svgPath = new SVGPath();
            svgPath.setFill(Color.TRANSPARENT);
            //svgPath.setStrokeWidth(0);
            svgPath.setStroke(Color.BLACK);
            svgPath.setContent("M 0 0 L 6 0  L 6 5.5 A.5,.5 0 0 1 5.5 6 L0,6 L0,0");
            // compute scale ratio
            originalWidth = svgPath.prefWidth(-1);
            originalHeight = svgPath.prefHeight(originalWidth);
            scaleX = canvas1Width / originalWidth;
            scaleY = canvas1Height / originalHeight;
            // scale the svgPath; adjust the stroke width
            svgPath.setScaleX(scaleX);
            svgPath.setScaleY(scaleY);
            svgPath.setStrokeWidth(1.0 / scaleX);
            canvasGroup1.getChildren().addAll(svgPath);

            // here's scaling using the Canvas / GraphicsContext

            GraphicsContext gc = canvas2.getGraphicsContext2D();
            double strokeWidth = gc.getLineWidth();
            originalHeight =6;
            originalWidth = 6;
            scaleX = canvas2Width / originalWidth;
            scaleY = canvas2Height / originalHeight;
            Affine affine = new Affine();
            affine.appendScale(scaleX, scaleY);
            gc.setTransform(affine);
            // the scaling must be set before the appendSVGPath statement
            // the SVGPath will not scale if apply the transform after the appendSVGPath
            gc.appendSVGPath("M 0 0 L 6 0  L 6 5.5 A.5,.5 0 0 1 5.5 6 L0,6 L0,0");
            gc.setLineWidth(strokeWidth/scaleX);
            gc.stroke();

Both scenarios work, but I will mention that there are two cons to using the GraphicsContext approach. First, the GraphicsContext approach doesn't produce as smooth of an arc as using the SVGPath. Lines are fine. Secondly, you can't easily get the bounds of the appendSVGPath to compute your scaling factor. As you may notice, I hard wired them in for that scenario.

Tom Rutchik
  • 1,183
  • 1
  • 12
  • 15
  • An additional observation. Although the two approaches are using the same svg description and the same scaling factor, the SVGPath produced a slightly smaller object. The reason is that the SVGPath Node adds 2 pixels to the originalWidth and originalHeight. It does that to reserve space for a StrokeType Outside. If you want the same scaling subtract 2 from both those original values and they'll be the same size – Tom Rutchik Jan 07 '22 at 00:07