0

As mentioned in the title, I'm trying to draw rotating and / or concentric figures onto a panel using MVC. Those figures are Circles, Ellipses and Astroids.

I was given the parametric equation I'm supposed to use in the task description:

    x = r_x * cos^power(phi * PI/180) and y = r_y * sin^power(phi * PI/180).   

Power is 1 (so a normal cos/sin) for circles/ellipses and 3 for Astroids.

I am also supposed to solve this using MVC, which is somewhat new to me, so sometimes I'm unsure whether I can do something or should not do it, lest I violate the MVC character. Phi is used to calculate a certain point on the circle(or Ellipses) and thus can be used to calculate only 3,4,5 etc points, drawing a triangle, square, pentagon etc.

I managed to calculate and draw simple figures using the above formulas. The problem I'm now facing is rotating and/or making them concentric.

Here's the code I'm using right now (the important part):

DescView(mostly basic stuff like setting up Buttons, Labels etc)

public DescView(){      
    model = new DescModel();
    createGUI(); // creates and sets up the frame
    createCanvas(); // creates the Panel which is drawn upon(own Class, see below)
    createChoiceStuff(); // creates Buttons, Labels, Textfields for Choices
    createPanels(); // creates and places the Panel for Choices
    addContentToPanels(); // adds the Buttons etc to the Panel
    frame.setVisible(true);

    controller = new DescController(model, this);
}

Canvas (inner Class to DescView):

class Canvas extends JPanel{

    DescModel model;
    int temp = 0;

    public Canvas(DescModel _model){
        this.model = _model;
    }

    public void paintComponent(Graphics _graphics){
        super.paintComponent(_graphics);


        //First while loop: Used to prevent drawing when Points is Empty
        //which it always is when the program starts.
        while(!model.getPoints().isEmpty()){
            //second loop: Keeps drawing a Line using a Collection(Points) until
            //the last element would go out of bounds.
            //drawLine takes the first 4 Elements from Points, then the 2,3,4,5
            //and so on.
            while(3 + temp < model.getPoints().size()){
                _graphics.drawLine(model.getPoints().get(0 + temp), model.getPoints().get(1 + temp), model.getPoints().get(2 + temp), model.getPoints().get(3 + temp));
                temp += 2;
            }
            //drawing the last Line from the two last Points to the first two
            _graphics.drawLine(model.getPoints().get(model.getPoints().size() - 2), model.getPoints().getLast(), model.getPoints().getFirst(), model.getPoints().get(1));

            //resetting temp and Points so they can be used again
            temp = 0;
            model.getPoints().clear();
        }               
    }   

DescController

public class DescController implements ActionListener {

    DescModel model;
    DescView view;

    DescController(DescModel _model, DescView _view){       
         this.model = _model;
         this.view = _view;

        view.addViewListener(this); 
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
        //asks the model to calculate a circle(or a part of it, depending on degree = phi)
        //as you can see, Cirlce and Ellipse doe the same(which is good), unless RadiusX
        //and RadiusY are different. After the calculation the Canvas is oredered to
        //repainted
        if(ae.getSource() == view.getBtCirlce()){
            model.calcNumbers(Integer.parseInt(view.getTfRadiusX().getText()), Integer.parseInt(view.getTfRadiusY().getText()), 1, Integer.parseInt(view.getTfDegree().getText()));     
            view.getCanvas().repaint();
        }
        else if(ae.getSource() == view.getBtEllipse()){
            model.calcNumbers(Integer.parseInt(view.getTfRadiusX().getText()), Integer.parseInt(view.getTfRadiusY().getText()), 1, Integer.parseInt(view.getTfDegree().getText()));
            view.getCanvas().repaint();
        }
        else if(ae.getSource() == view.getBtAstroid()){
            model.calcNumbers(Integer.parseInt(view.getTfRadiusX().getText()), Integer.parseInt(view.getTfRadiusY().getText()), 3, Integer.parseInt(view.getTfDegree().getText()));         
            view.getCanvas().repaint();
        }
        else if(ae.getSource() == view.getBtInfo()){
            view.showInfo();
        }
        else if(ae.getSource() == view.getBtQuit()){
            System.exit(0);
        }
    }
}

DescModel

public class DescModel {
    int x = 1;
    int y = 1;

    LinkedList<Integer> points = new LinkedList<Integer>();

    public DescModel() {
    }
    //calculates X and Y coordinates one by one. Phi is used to calculate only a
    //certain number of points. For example for phi = 90 a Square is drawn
    //and a full circle for phi = 1 (if X = Y).
    //addOffset is used to place the figure in the middle of Canvas, which
    //has a width and height of 600.
    public void calcNumbers(int _radiusX, int _radiusY, int _potenz, int _phi){
        for(int i = 1; i <= 360 / _phi; i++){
            calcX(_radiusX, _potenz, _phi * i);
            calcY(_radiusY, _potenz, _phi * i);
        }
        addOffset();
    }   
    //Calculates using the above formula and adds the point to the Collection
    private void calcX(int _radiusX, int _potenz, int _phi){
        x = 300 + (int)(_radiusX * Math.pow(Math.cos(_phi * (Math.PI / 180)), _potenz));
        addToPoints(x);
    }

    private  void calcY(int _radiusY, int _potenz, int _phi){
        y = 300 + (int)(_radiusY * Math.pow(Math.sin(_phi * (Math.PI / 180)), _potenz));
        addToPoints(y);
    }

    private void addOffset(){
        for(int i = 0; i < points.size(); i++){
        }
    }

    private void addToPoints(int _number){
        points.add(_number);
    }
}

Now the next thing I want to / have to do is allowing the option of rotating with a fixed radius and drawing the same thing concentric. Obviously I could run the same model.calcNumbers() with smaller parameters. However, I'm not sure I can do that, since the view should not make a call to the model directly right? And even if that would be allowed, if I were to call repaint, the old circle would disappear. Using the same array also wouldn't work, since then I would draw circles which all had a single line connecting them. Regarding rotation, I'd probably add a certain value to each point and then draw it again. How would I go about that drawing part though? Same problem as with concentric: The old picture would be gone.

Thanks in advance for any help.

Edit: Since there appears to be some confusion: I don't need an animation of this. It'd be enough to have a set of shapes. For Example: Draw a circle with radius 100, another with radius 90, next with radius 80 etc.

On a side note: This is the first question for me, so any tips regarding formatting, formulating the question better etc. are of course welcome.

Khashayar
  • 3
  • 2
  • 1
    For me it's not entirely clear what you want to paint in the end. Should the objects be *animated*, or should there simply be multiple objects, arranged in a circle? (BTW: When you post a http://stackoverflow.com/help/mcve , you'll usually receive better help sooner. It's always helpful to just copy&paste the code into an IDE and *see* the current status...) – Marco13 Jul 13 '14 at 00:57
  • @Marco13: Put an edit at the end of the whole thing, hopefully that makes it clear. Regarding the mcve: Since part of my problem is with the MVC-Model(rather, what I can and can't do with it), how should I create minimal code, when I need several classes? If I were to post usable code, I'd have to *add* things(like the frame, buttons etc). There probably is a way to solve this, though I can't see it. – Khashayar Jul 13 '14 at 06:55
  • Maybe the links from trashgod already answer the question. But something that I noticed while scrolling over the code again: You should **NOT** call `model.getPoints().clear();` in the `paintComponent` method. From a "high-level" point of view, this means that the *view* modifies the *model*, which is not appropriate. But more imortantly: `paintComponent` may be called many times, and currently, it will only paint the contents when it is called the *first* time. In general, the `paintComponent` method should **NOT** have any side-effects (that is, it should not change any state). – Marco13 Jul 13 '14 at 11:17
  • Thanks for the hint on clear(). I'll problably simply change the clear location to the model call for calcNUmbers() or something. Don't know if the Links will help, I'm currently trying to figre out how to handle the g2d stuff. Though even if the don't solve my problem, they defenitly helped in understanding the concept, options I have etc. so a big thanks for that. – Khashayar Jul 14 '14 at 12:28
  • So in the end, I didn't use g2d. Instead, I only used one Collection to store every point. To decide when a new, smaller Circle starts, I added a "-1" in between. Also, I change the parameters for calcNumbers() to also take two Booleans, "rotate" and "concentric". Depending on them, I then called different calculations. Those mostly made use of for loops to decrease the radius or change the position. Nonetheless, the tips really helped in learning about the stuff in general (didn't even know something like AffineTransform existed). So thank you two for that. – Khashayar Jul 17 '14 at 09:25

1 Answers1

1

This AnimationTest illustrates the basic approach using a simpler parametric equation. For rotations, you can use AffineTransform, as shown here and here. Finally, this answer elaborates on how Swing uses the MVC pattern.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045