1

I want to set up a mathematical (where y grows up not down) coordinate space from (-1, -1) to (+1, +1) and have it fit in the window regardless of the window size.

I am using an anonymous JComponent subclass in Java SE 7 and casting the incoming Graphics in paintComponent to Graphics2D and then drawing on the Graphics2D

But the Graphics2D is set to a computer coordinate space that changes with the size of the window. How to get it to rescale according to window size and have Y go upwards? The following program should show a dark square in upper right quadrant.

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class G {
  public static void main (String [] args) {
    JFrame frame = new JFrame(G.class.getCanonicalName());
    frame.setUndecorated(true);
    JComponent component = new JComponent() {
      private static final long serialVersionUID = 1L;
      @Override
      protected void paintComponent (Graphics g) {
        super.paintComponent(g);
        paint2D((Graphics2D)g);
      }
      protected void paint2D (Graphics2D g2) {
        g2.draw(new Rectangle2D.Double(0.1, 0.1, 0.9, 0.9));
      }
    };
    frame.add(component);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400, 400);
    frame.setVisible(true);
  }
}
necromancer
  • 23,916
  • 22
  • 68
  • 115

1 Answers1

6

Setup the coordinate system how you want, using transform() and translate(). So:

  1. you want the origin to be at (0, height); bottom left.
  2. then you want to flip the Y axis.

Example code:

AffineTransform tform = AffineTransform.getTranslateInstance( 0, height);
tform.scale( 1, -1);
g2.setTransform( tform);

[My edited version]:

public static void main (String [] args) {
    JFrame frame = new JFrame( G2dTransform_Question.class.getCanonicalName());
    JComponent component = new JComponent() {
        private static final long serialVersionUID = 1L;
        @Override
        protected void paintComponent (Graphics g) {
            super.paintComponent(g);
            paint2D((Graphics2D)g);
        }
        protected void paint2D (Graphics2D g2) {
            AffineTransform tform = AffineTransform.getTranslateInstance( 0, getHeight());
            tform.scale( getWidth(), -getHeight());    // NOTE -- to make 1.0 'full width'.
            g2.setTransform( tform);

            g2.setColor( Color.BLUE);  // NOTE -- so we can *see* something.
            g2.fill( new Rectangle2D.Double(0.1, 0.1, 0.8, 0.8));  // NOTE -- 'fill' works better than 'draw'.
        }
    };

    frame.setLayout( new BorderLayout());    // NOTE -- make the component size to frame.
    frame.add( component, BorderLayout.CENTER);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400, 400);
    frame.setVisible(true);
}

[Hovercraft's version]: Thanks Hover!

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class G {
  public static final int PREF_W = 400;
  public static final int PREF_H = PREF_W;

public static void main (String [] args) {
    JFrame frame = new JFrame(G.class.getCanonicalName());
    frame.setUndecorated(true);
    JComponent component = new JComponent() {
      private static final long serialVersionUID = 1L;
      @Override
      protected void paintComponent (Graphics g) {
        super.paintComponent(g);
        AffineTransform tform = AffineTransform.getTranslateInstance( 0, getHeight());
        tform.scale( 1, -1);
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setTransform( tform);
        paint2D(g2);
        g2.dispose();
      }
      protected void paint2D (Graphics2D g2) {
        g2.draw(new Rectangle2D.Double(10, 10, 20, 30));
      }

      @Override
      public Dimension getPreferredSize() {
         return new Dimension(PREF_W, PREF_H);
      }
    };
    frame.add(component);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
  }
}
Thomas W
  • 13,940
  • 4
  • 58
  • 76
  • thanks, but i don't know how to accomplish this. should i use `getWidth()` and `getHeight()` to make the `[-1,+1]` interval fit each dimension? and how do i flip? i am not versed with the matrices. is there a method to create a flip transformation? or do i rotate twice? it is essentially specific methods and which arguments to supply that i am looking for. – necromancer May 14 '13 at 02:22
  • thanks but it didn't work. earlier i used to get a tiny dot on the upper left as expected. now there's nothing... upvoted but didn't solve the problem of showing a rectangle in upper right quadrant. – necromancer May 14 '13 at 02:32
  • ` AffineTransform transform = AffineTransform.getTranslateInstance(0, getHeight()); transform.scale(1, -1); g2.setTransform(transform); g2.draw(new Rectangle2D.Double(+0.1, +0.1, +0.9, +0.9));` – necromancer May 14 '13 at 02:33
  • 1
    Example added. Thomas you can edit or correct as you see fit. But it shows that your suggestion works. I recommend only transforming copies of the Graphics object to prevent side effects down stream. I also like overriding `getPreferredSize(...)` as a cleaner way to set my component's and thus my GUI's size. 1+ to the answer. – Hovercraft Full Of Eels May 14 '13 at 02:40
  • wow @HovercraftFullOfEels i am touched. tears streaming down the cheek. not because your answer is correct or not, but because you gave code not manual! i will try out the answer now. – necromancer May 14 '13 at 02:43
  • @HovercraftFullOfEels thank you for all the improvements on how to get g2 and how to use getPreferredSize. the core part is better but i am puzzled why if i plot a rectangle of dimensions (10, 10, 390, 390), only the lower and left sides are visible. the visible part maxes out at (10, 10, 389, 390) – necromancer May 14 '13 at 02:53
  • @ThomasW ahh.. manna from heaven! thank you - works perfectly! :) – necromancer May 14 '13 at 03:30
  • @ThomasW on second thoughts, when i changed the `0.8` to `0.9` the result looks off. don't bother fixing it unless it is not a bother. i think will solve the equations to derive the transforms the hard way. – necromancer May 14 '13 at 04:11
  • @randomstring -- those second two parameters for the Rect are _width/ height_, not X/Y! – Thomas W May 14 '13 at 04:36
  • 1
    @ThomasW turns out `draw` was tripping me over. `fill` works differently than `draw` see http://stackoverflow.com/questions/16535904/graphics2d-does-postmodern – necromancer May 14 '13 at 05:43
  • 1
    @HovercraftFullOfEels followup to this one http://stackoverflow.com/questions/16535904/graphics2d-does-postmodern – necromancer May 14 '13 at 05:44