[Canvas] GraphicsContext set transform not working as expected

XMLWordPrintable

      package canvastest;

      import java.awt.geom.Rectangle2D;

      import javafx.application.Application;
      import javafx.event.ActionEvent;
      import javafx.scene.Scene;
      import javafx.scene.canvas.Canvas;
      import javafx.scene.canvas.GraphicsContext;
      import javafx.scene.control.Button;
      import javafx.scene.control.ScrollPane;
      import javafx.scene.control.ScrollPane.ScrollBarPolicy;
      import javafx.scene.control.ToolBar;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.paint.Color;
      import javafx.scene.shape.StrokeLineCap;
      import javafx.scene.shape.StrokeLineJoin;
      import javafx.scene.transform.Affine;
      import javafx.stage.Stage;

      public class JFXMapRenderingTest extends Application {

      /** X coordinates of points to testing */
      private final static double[] XPoints = { 839688.142, 842040.845, 842067.716, 839828.468, 840900.322, 840748.053 };
      /** Y coordinates of points to testing */
      private final static double[] YPoints = { 834278.158, 834322.943, 835979.987, 835978.944, 835224.614, 835129.073 };

      /** Max map extent: [ xmin, xmax, ymin, ymax ] */
      private final static double[] MaxMapExtent = { 839650.414, 842180.086, 834204.376, 835988.085 };
      /** Current map extent: [ xmin, xmax, ymin, ymax ] */
      private final static double[] CurrentMapExtent = { 839650.414, 842180.086, 833831.3944999999, 836361.0665 };
      /** Viewport extent: [ xmin, xmax, ymin, ymax ] */
      private static Rectangle2D.Double viewport;

      /** Map Scale X */
      private static double mapscaleX = 1.0;
      /** Map Scale Y */
      private static double mapscaleY = 1.0;
      /** Map Scale */
      private static double mapscale = 1.0;
      /** Map Translation X*/
      private static double maptranslateX = 0.0;
      /** Map Translation Y*/
      private static double maptranslateY = 0.0;

      @Override
      public void start(Stage primaryStage) {
      primaryStage.setTitle("JFX Canvas Map Rendering Test");
      BorderPane root = new BorderPane();
      Scene scene = new Scene(root, 500, 533);

      ScrollPane scrollPane = new ScrollPane();
      scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
      scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
      root.setCenter(scrollPane);

      Canvas canvas = new Canvas();
      canvas.widthProperty().bind(scrollPane.widthProperty());
      canvas.heightProperty().bind(scrollPane.heightProperty());
      scrollPane.setContent(canvas);
      canvas.setScaleY(-1); // change the origin(0, 0) to lower left

      Button btnDraw = new Button("Draw");
      btnDraw.setOnAction((ActionEvent event) -> {
      clear(canvas);
      draw(canvas);
      });

      Button btnClear = new Button("Clear");
      btnClear.setOnAction((ActionEvent event) -> {
      clear(canvas);
      });

      Button btnFullExtent = new Button("FullExtent");
      btnFullExtent.setOnAction((ActionEvent event) -> {
      double scalex = canvas.getScaleX();
      double scaley = canvas.getScaleY();
      scalex = scalex > 0 ? 1.0 : -1.0;
      scaley = scaley > 0 ? 1.0 : -1.0;
      canvas.setScaleX(scalex);
      canvas.setScaleY(scaley);
      // draw(canvas);
      });

      Button btnZoomin = new Button("ZoomIn");
      btnZoomin.setOnAction((ActionEvent event) -> {
      canvas.setScaleX(2 * canvas.getScaleX());
      canvas.setScaleY(2 * canvas.getScaleY());
      // draw(canvas);
      });

      Button btnZoomout = new Button("ZoomOut");
      btnZoomout.setOnAction((ActionEvent event) -> {
      canvas.setScaleX(0.5 * canvas.getScaleX());
      canvas.setScaleY(0.5 * canvas.getScaleY());
      // draw(canvas);
      });

      ToolBar toolbar = new ToolBar(btnDraw, btnClear, btnFullExtent, btnZoomin, btnZoomout);
      root.setTop(toolbar);

      primaryStage.setScene(scene);
      primaryStage.show();

      viewport = new Rectangle2D.Double(0, 0, canvas.getWidth(), canvas.getHeight());
      }

      private void clear(Canvas canvas) {
      double xmin = MaxMapExtent[0];
      double xmax = MaxMapExtent[1];
      double ymin = MaxMapExtent[2];
      double ymax = MaxMapExtent[3];
      double width = xmax - xmin;
      double height = ymax - ymin;
      GraphicsContext gc = canvas.getGraphicsContext2D();
      gc.clearRect(xmin, ymin, width, height);
      }

      private void draw(Canvas canvas) {
      GraphicsContext gc = canvas.getGraphicsContext2D();

      // Adjust the scope of the current map to match the width to height ratio of the viewport
      double aspectRatio = viewport.width / viewport.height;
      double xmin = CurrentMapExtent[0];
      double xmax = CurrentMapExtent[1];
      double ymin = CurrentMapExtent[2];
      double ymax = CurrentMapExtent[3];
      double w = (xmax - xmin);
      double h = (ymax - ymin);
      if (w < h) {
      xmin = CurrentMapExtent[0] -= Math.abs((w - aspectRatio * h) / 2);
      xmax = CurrentMapExtent[1] += Math.abs((w - aspectRatio * h) / 2);
      } else {
      ymin = CurrentMapExtent[2] -= Math.abs((h - w / aspectRatio) / 2);
      ymax = CurrentMapExtent[3] += Math.abs((h - w / aspectRatio) / 2);
      }
      w = (xmax - xmin);
      h = (ymax - ymin);

      mapscaleX = canvas.getWidth() / w;
      mapscaleY = canvas.getHeight() / h;
      mapscale = mapscaleX > mapscaleY ? mapscaleX : mapscaleY;
      maptranslateX = canvas.getTranslateX() - xmin; // 0 - xmin
      maptranslateY = canvas.getTranslateY() - ymin; // 0 - ymin

      /** No.1: why can't do like this??? what's the different with the No.2??? */
      // gc.setTransform(mapscale, 0.0, 0.0, mapscale, maptranslateX, maptranslateY);

      /** No.2: this will be ok */
      // gc.setTransform(mapscale, 0.0, 0.0, mapscale, 0, 0);
      // gc.translate(maptranslateX, maptranslateY);


      /** No.3: why can't do like this??? what's the different with the No.4??? */
      // Affine affine = new Affine(mapscale, 0.0, maptranslateX, 0.0, mapscaleY, maptranslateY);
      // gc.setTransform(affine);

      /** No.4: this will be ok */
      Affine affine = new Affine();
      affine.appendScale(mapscale, mapscale);
      affine.appendTranslation(maptranslateX, maptranslateY);
      gc.setTransform(affine);

      System.out.println("start rendering...");
      long st = System.currentTimeMillis();

      gc.setFill(Color.GREEN);
      gc.setStroke(Color.ORANGE);
      gc.setLineCap(StrokeLineCap.ROUND);
      gc.setLineJoin(StrokeLineJoin.ROUND);
      gc.setLineWidth(2.0);

      gc.strokePolyline(XPoints, YPoints, XPoints.length);

      System.out.println("Time consuming: " + (System.currentTimeMillis() - st) / 1000.0 + " s\n");
      }

      public static void main(String[] args) {
      launch(args);
      }

      }

      Question 1: please see draw() method that like "No.1, No.2 ..." etc.
      Question 2: when I call the draw() method first, the line width looks very well... but from after the second, the line width looks bad and different with the first one, why??

            Assignee:
            Jim Graham
            Reporter:
            J. Duke
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported: