-1

I would like to have a list of shapes, that appears in my window. Whenever I'm changing the size of the window, I would like to scale all of my drawings.

I already prepared classes, that store information about random shapes in a list (rectangles, ovals, etc.). I have no problem with painting them all, but I can't deal with the scaling problem. My solutions don't change anything, or make all of shapes disappear.

public class Shape extends JPanel{
    int x, y,width,height,red,green,blue;
    double scX, scY; //scale x and y

    public Shape(int x, int y, int width, int height, int red, int green, int blue) {
//...long constructor
        scX=1;
        scY=1;
    }
    void randomizeValues(){...}

        void setScale(double x, double y) {
        this.scX = x;
        this.scY = y;
    }
}

public class Rectangle extends Shape{

    public Rectangle(int x, int y, int width, int height, int red, int green, int blue) {
        super(x, y, width, height, red, green, blue);
    }

    @Override
    protected void paintComponent(Graphics graphics) {
        super.paintComponent(graphics);
        graphics.fillRect((int)(x*scX), (int)(y*scY), (int)(width*scX), (int)(height*scY));
    }
}


class Window extends JFrame {

    int defaultWidth = 768;
    int defaultHeight = 512;
    List<Shape> paintList = new ArrayList<>();

 public Window() {
        setTitle("Shape");
        add(new DrawShape);
        setSize(defaultWidth, defaultHeight);
        setVisible(true);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

class DrawShape extends JPanel {
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            for (int i = 0; i< paintList.size(); i++) {
                Shape s = paintList.get(i);
                s.setScale(this.getWidth()/defaultWidth, this.getHeight()/defaultHeight);
                s.paintComponent(g);
            }
   }
}

How to make a proper scale trick? Where should I multiply values, to make everything work good?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
B.Nary
  • 23
  • 3
  • `s.paintComponent(g);` - NO, don't do this, there is never any reason why you should ever call a components `paint` method directly. Instead, devise one or more classes which perform the required operations (ie `Box` which doesn't extend from a component, which has a "draw" method through which you can pass the `Graphics` context) – MadProgrammer Apr 28 '19 at 22:31
  • [This demonstrates the use of `AffineTransform#scale`](https://stackoverflow.com/questions/18396302/how-to-scale-image-using-getscaledinstance/18396317#18396317) which can be used to scale a `Graphics` context when dealing with pixel based renderings. A "better" solution would be to scale the coordinates of the individual shapes, which is demonstrated [here](https://stackoverflow.com/questions/29067700/how-can-i-change-the-size-of-a-figure-made-in-java-graphics2d-with-a-slider/29067753#29067753) – MadProgrammer Apr 28 '19 at 22:32
  • And if you'd like to see a solution scaling based on a window size, your could look at [this example](https://stackoverflow.com/questions/30041397/how-to-paint-a-group-of-quadcurves-using-java-graphics2d/30042600#30042600) – MadProgrammer Apr 28 '19 at 22:35
  • 2
    You are using names like Shape, WIndow, and Rectangle for your own classes where these are already defined in the Java API (where Shape is an interface). To avoid confusion you should change these. You should also check out [Custom Painting](https://docs.oracle.com/javase/tutorial/uiswing/painting/index.html) in the Java Tutorials. – WJS Apr 28 '19 at 22:43

1 Answers1

0

First of all, you should not add your JPanel to your window but set it as the ContentPane : setContentPane(new DrawShape());. Next, you should not repaint inside the loop but outside (at the end of your paintComponent(Graphics g) method for example). This way, the JPanel should be drawn again permanently. If you need to change the size of your shapes depending on the window dimension, do it like that in your JPanel paintComponent(Graphics g) method:

//fill it with the shapes base sizes (index 0 = width, index 1 = height)
Map<Shape,int[]> shapeSizes = new HashMap<Shape,int[]>();
public void paintComponent(Graphics g) {
double widthCoeff = this.getWidth()/(double)Window.this.defaultWidth;
double heightCoeff = this.getHeight()/(double)Window.this.defaultHeight;
for (int i = 0; i< paintList.size(); i++) {
Shape s = paintList.get(i);
int[] baseSize = shapeSizes.get(s);
int oldWidth = baseSize[0], oldHeight = baseSize[1];
int width = oldWidth*widthCoeff, height = oldHeight*heightCoeff;
//you can now give the shape its new width and height
}
}
GoldenBolt
  • 51
  • 8
  • Thank you for you advice. Could you please tell me, how should I paint every shape from my list, if it's a bad move to call listElement(i).paintComponent() in a loop? – B.Nary Apr 28 '19 at 23:21
  • I would recommend to turn your `Shape` objects into `Polygon`s (defined by Java API), that you will be able to paint by calling `g.drawPolygon(polygon);`. – GoldenBolt Apr 28 '19 at 23:26
  • 1
    *"First of all, you should not add your JPanel to your window but set it as the ContentPane : setContentPane(new DrawShape());"* Since Java 1.6, `JFrame#add` forwards to the `contentPane` automagically – MadProgrammer Apr 29 '19 at 00:49