Mike O
Mike O

Reputation: 291

Can I apply 2D transforms to shapes on a JavaFX 8 Canvas?

I'm porting a class that that was previously done in Swing to JavaFX 8. It displays a UI element that looks like an analog electric voltage meter with a half circle surrounded by a collection of "tic marks" at regular intervals. In the Swing version the class was an extension of JPanel and the tic marks were drawn in paintComponent(Graphics g) as follows:

private Line2D ticLine = new Line2D.Float(0, LINE_ROOT_Y, TIC_LENGTH, LINE_ROOT_Y);

public void paintComponent(Graphics g)
   {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D) g;

      // Draw tic marks
      if (ticCount > 0)
      {
         g2.draw(ticLine); // First tic

         AffineTransform ticTrans = new AffineTransform();
         // Draw all additional tics rotated around half circle
         for (int i = 1; i < ticCount; i++)
         {
            ticTrans.rotate(Math.toRadians(ticGap),
                 METER_MIDDLE, METER_BASE_Y);
            g2.draw(ticTrans.createTransformedShape(ticLine));
         }
      }
   }

This worked fine.

Now with JavaFX I'm using a class extending VBox. It contains 2 stacked Canvas objects. One of which will draw the static elements like the half circle and tic marks, and the other for the regularly moving meter line. On that first Canvas I was hoping to use a similar loop as I did in the Swing version to easily redraw the first tic mark in a ticCount # of additional positions around the half circle. So I tried the following which compiled and ran but only drew that first tic mark:

// Called from the constructor:
MeterGC = MeterCanvas.getGraphicsContext2D();
Line ticLine = new Line(0, LINE_ROOT_Y, TIC_LENGTH, LINE_ROOT_Y);

// Draw tic marks
if (ticCount > 1)
{
   MeterGC.setStroke(Color.GRAY);
   MeterGC.setLineWidth(BASIC_LINE_WIDTH);
   MeterGC.strokeLine(ticLine.getStartX(), ticLine.getStartY(), 
                      ticLine.getEndX(), ticLine.getEndY());

   Rotate ticTrans = new Rotate(Math.toRadians(ticGap), METER_MIDDLE, METER_BASE_Y);
   for (int i = 1; i < ticCount; i++)
   {
      ticLine.getTransforms().add(ticTrans);
      MeterGC.strokeLine(ticLine.getStartX(), ticLine.getStartY(), 
                         ticLine.getEndX(), ticLine.getEndY());
    }
}

It may be that trying to Transform a Shape object like this only works when drawing to a scene and not on a Canvas. Or that I have to do something after the "ticLine.getTransforms().add(ticTrans)" line to get them to apply to the line. Am I at least close? Or is there a much better way to do what I'm trying here?

Upvotes: 0

Views: 2033

Answers (2)

Mike O
Mike O

Reputation: 291

This is my revised for-loop and it works perfectly. Noticing that converting the angle in Rotate to radians is no longer necessary in JavaFX was also key in getting it to work.

for (int i = 1; i < ticCount; i++)
{
   Rotate ticTrans = new Rotate(ticGap * i, METER_MIDDLE, METER_BASE_Y);
   MeterGC.setTransform(ticTrans.getMxx(), ticTrans.getMyx(), ticTrans.getMxy(),
                        ticTrans.getMyy(), ticTrans.getTx(), ticTrans.getTy());
   MeterGC.strokeLine(ticLine.getStartX(), ticLine.getStartY(), 
                      ticLine.getEndX(), ticLine.getEndY());
}

Upvotes: 0

jewelsea
jewelsea

Reputation: 159291

What you are doing wrong

In your sample code you are applying the transform to a Line object (which you never display).

How to fix it

You need to set the transform on the canvas GraphicsContext before you stroke the line on the canvas.

Sample code

For an example, see:


/**
 * Sets the transform for the GraphicsContext to rotate around a pivot point.
 *
 * @param gc the graphics context the transform to applied to.
 * @param angle the angle of rotation.
 * @param px the x pivot co-ordinate for the rotation (in canvas co-ordinates).
 * @param py the y pivot co-ordinate for the rotation (in canvas co-ordinates).
 */
private void rotate(GraphicsContext gc, double angle, double px, double py) {
    Rotate r = new Rotate(angle, px, py);
    gc.setTransform(r.getMxx(), r.getMyx(), r.getMxy(), r.getMyy(), r.getTx(), r.getTy());
}

Upvotes: 1

Related Questions