1

I want to draw a simple stick figure. This requires a circle and some rectangles. I could seperatly draw the head, torso, etc. with many g2d.fill() calls. However, if possible I'd like to be able to represent it as one shape and draw it with one fill call. Also, if I wanted to move the stick figure 10 pixels right, I'd currently have to change the x coordinate on the ellipse + 10 and the x coordinate on the rectangle + 10 and so on. I'd like to be able to change a single x coordinate that then moves all off the separate components over.

How can I accomplish this?

Code:

public class StickFigure{
    Ellipse2D head = new Ellipse2D.Double(0, 0, 100, 100);
    Rectangle torso = new Rectangle(100, 100, 50, 110); 
    Rectangle rightArm;
    ...
}

What I have to do:

g2d.fill(sf.head);
g2d.fill(sf.torso);
...

What I want to do:

g2d.fill(sf.figure) //figure being a shape which includes both head and torso

What I have to do:

     sf.head.x = 3;
     sf.head.y = 4;
     sf.torso.x = 3;
     sf.torso.y = 4;
     g2d.fill(sf.head);
     g2d.fill(sf.torso);
     ...

What I want to do:

sf.figure.x = 3; //which shifts both the head and the torso
sf.figure.y = 4; // ^^
g2d.fill(sf.figure);
Ashwin Gupta
  • 2,159
  • 9
  • 30
  • 60
  • I am unsure if you can do this easily swing, but to assist you in finding an answer, what you are looking for is known as a "scene graph". a quick google search seems to imply there are [several scene graphs you can choose from](http://stackoverflow.com/questions/4752131/java-2d-scene-graph-library-for-gui). If you are not particularly attached to using swing, you could consider upgrading to javafx, which includes a scene graph out of the box. – clearlyspam23 Feb 02 '16 at 02:25
  • @clearlyspam23 well I got no problem using JavaFX. I am a new programmer so I don't mind learning new stuff. I've done a bit of reserach myself, the best thing I found was a class called Area, this seems to work but I can't get it to do what I want exactly. – Ashwin Gupta Feb 02 '16 at 02:32

2 Answers2

2

You can create a BufferedImage to represent your stick figure. Then you draw the rectangles and ellipsis onto the BufferedImage.

BufferedImage bi = new BufferedImage(...);
Graphics2D g2d = bi.getGraphics();
g2d.fillOval(...);
g2d.fillRect(...);

The BufferedImage would be an instance variable that you create when you construct the class. Then you can just paint the BufferedImage in your paintComponent() method.

graphics.drawImage(bi, ...);

Then whenever you want to move the image you can just change the x/y values in the drawImage(...) method.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • Thats what I was looking for! Good thought, hadn't even considred this. In fact, I dont even have to draw my sprite in java2d anymore, I'll just use GIMP and export it as an jpg. Then I can ImageIO in java. Thanks! – Ashwin Gupta Feb 02 '16 at 02:59
2

The different shapes that make up a stick figure can be turned into a single Area.

screenshot

class StickFigure {

    private Area stickFigure;
    private Color color;

    StickFigure(Color color, int x) {
        this.color = color;
        stickFigure = new Area(new Ellipse2D.Double(x, 0, 100, 100));
        stickFigure.add(new Area(new Rectangle(x+25, 100, 50, 110)));
    }

    public Area getStickFigure() {
        return stickFigure;
    }

    public void draw(Graphics2D g) {
        g.setColor(color);
        g.fill(stickFigure);
    }
}

MCVE

import java.awt.*;
import java.awt.geom.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class StickFigurePainter {

    private JComponent ui = null;

    StickFigurePainter() {
        initUI();
    }

    public void initUI() {
        if (ui != null) {
            return;
        }

        ui = new JPanel(new BorderLayout(4, 4));
        ui.setBorder(new EmptyBorder(4, 4, 4, 4));

        ArrayList<StickFigure> list = new ArrayList<StickFigure>();
        list.add(new StickFigure(Color.RED, 0));
        list.add(new StickFigure(Color.GREEN, 110));
        list.add(new StickFigure(Color.BLUE, 220));
        
        ui.add(new StickCanvas(list));
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception useDefault) {
                }
                StickFigurePainter o = new StickFigurePainter();

                JFrame f = new JFrame(o.getClass().getSimpleName());
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                f.setLocationByPlatform(true);

                f.setContentPane(o.getUI());
                f.pack();
                f.setMinimumSize(f.getSize());

                f.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

class StickCanvas extends JPanel {

    private ArrayList<StickFigure> stickFigures;

    StickCanvas(ArrayList<StickFigure> stickFigures) {
        this.stickFigures = stickFigures;
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        for (StickFigure stickFigure : stickFigures) {
            stickFigure.draw(g2);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        Area area = new Area();
        for (StickFigure stickFigure : stickFigures) {
            area.add(stickFigure.getStickFigure());
        }
        return area.getBounds().getSize();
    }
}

class StickFigure {

    private Area stickFigure;
    private Color color;

    StickFigure(Color color, int x) {
        this.color = color;
        stickFigure = new Area(new Ellipse2D.Double(x, 0, 100, 100));
        stickFigure.add(new Area(new Rectangle(x+25, 100, 50, 110)));
    }

    public Area getStickFigure() {
        return stickFigure;
    }

    public void draw(Graphics2D g) {
        g.setColor(color);
        g.fill(stickFigure);
    }
}
Community
  • 1
  • 1
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Thanks! This actually works really, really perfect. This technically answers my question better than the other answer I already accepted. Is it rude to accept this one instead and unaccept the other? – Ashwin Gupta Feb 03 '16 at 03:44
  • *"Is it rude to accept this one instead and unaccept the other?"* (shrug) while some people consider it 'rude' (there's just no pleasing some people) it is encouraged by the SE network. After all, this is a Q&A site (where the best answer should get the 'tick'), not a social network where we set out to please everybody. As an aside, I've been on both sides of this effect, and I expect, so has @camickr .. – Andrew Thompson Feb 03 '16 at 03:59
  • Well said Andrew. I agree. @camickr not that your answer wasn't great (and may be even more useful in many situations), but for my case specifically this one works better and answers the question better also. – Ashwin Gupta Feb 03 '16 at 04:45
  • I don't have a problem with you changing your mind as to what is the best answer as long as you take the time to understand the concept suggested by any given answer (and don't just accept an answer because of pretty pictures or complete code posting). Only you can decide what Is best for your current requirement. I often give answers that don't directly answer the question because there is always more than one way to solve a problem. I like to suggest alternative approaches (when I think that are appropriate). As you say you never know when they will come in handy. – camickr Feb 03 '16 at 16:02