I have a fractal tree generator and I am trying to make a slider control the number of iterations, but I can not get it to work. Also, the layout gets messed up whenever the repaint()
method is called. Any thoughts on how to fix this?
public class FractalTree extends JPanel implements ChangeListener {
static JSlider slider = new JSlider(0,12);
static int slideVal=7;
public FractalTree()
{
super();
slider.addChangeListener(this);
}
public void paint(Graphics g)
{
g.setColor(Color.green);
drawTree(g, 400, 750, 200, Math.toRadians(-90), Math.toRadians(45), slideVal); //Don't let # of iterations exceed 12, it is useless
}
private void drawTree(Graphics g, int x1, int y1, double l, double t, double dt, double iterations) {
if (iterations > 0) {
int x2 = x1 + (int) (l * Math.cos(t));
int y2 = y1 + (int) (l * Math.sin(t));
g.drawLine(x1, y1, x2, y2);
drawTree(g, x2, y2, l / 1.5, t + dt, Math.PI / 4, iterations - .5);
drawTree(g, x2, y2, l / 1.5, t - dt, Math.PI / 4, iterations - .5);
}
}
@Override
public void stateChanged(ChangeEvent e) {
slideVal=slider.getValue();
repaint();
}
public static void main(String[] args) {
JFrame t = new JFrame("Some swaggy fractal shit");
FractalTree tree = new FractalTree();
slider.setValue(slideVal);
slider.setMinorTickSpacing(1);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
tree.add(slider);
t.add(tree);
t.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
t.setResizable(false);
t.setLocationByPlatform(true);
t.setSize(800, 800);
t.setBackground(Color.black);
t.setVisible(true);
}
}
Two main problems:
paint
instead of paintComponent
.super.paintComponent(g)
(or in your case, super.paint(g)
) as the first thing in your overridden method.This is what you need to have:
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.green);
drawTree(g, 400, 750, 200, Math.toRadians(-90), Math.toRadians(45), slideVal);
}
Other things to consider:
BorderLayout.PAGE_START
instead of to the panel. If you add it to the panel, you risk drawing where the slider is.super()
in the constructor, it's automatic.setResizable(false)
on the frame should generally be avoided. No need to restrict the user's space.pack()
on the frame instead of setSize(...)
. The latter is too dependent on the local graphics configuration.
getPreferredSize
method to return the correct size for the drawing.Response to comments
Why
paintComponent
should be used?
See these:
public void paint(Graphics g)
This method actually delegates the work of painting to three protected methods:
paintComponent
,paintBorder
, andpaintChildren
. They're called in the order listed to ensure that children appear on top of component itself. [...] A subclass that just wants to specialize the UI (look and feel) delegate's paint method should just overridepaintComponent
.
You saw that if you override paint
and added the slider to the panel, you got issues with the slider painting because you ignored paintChildren
.
What calling the superclass constructor does?
Best to answer is the JLS:
JLS 8.8.7. Constructor Body
If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.
So calling super()
does nothing.