0

The program spawns a flock of triangles that 'fly' through an environment. In this environment, there is a rectangle and sometimes the flock will fly through the rectangle. The flock itself is comprised of 20 triangles - what I want to know is how many of these triangles pass through the rectangle each time the flock flys through the environment (once the flock disappears outside of the environment it respawns).

At the moment, my attempt at implementing this is the "hasFoundFood" method where I attempt to detect whether, for each of the boids, they have come into contact with the bounds of the rectangle but the value for noSuccessful is always 20.

Is there a way to detect whether any or all of the triangles have come into contact with the boundaries of rectangle and then count how many times this has occurred?

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;
import static java.lang.Math.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import javax.swing.Timer;
import java.util.Random;
import javax.swing.JButton;

public class Boids extends JPanel {


    Flock flock;
    Flock flock2;
    final int w, h;
  int noSuccessful;
  public Area food = new Area(new Rectangle(600,250,200,200));
  int[] xTriangle = {33,36,34};
  int[] yTriangle = {30,30,20};

 public Area triangle = new Area(new Polygon(xTriangle, yTriangle, 3));

    public Boids() {
        w = 1200;
        h = 600;

        setPreferredSize(new Dimension(w, h));
        setBackground(Color.black);

        spawnFlock();
       flock.hasFoundFood(noSuccessful);
       new Timer(17, (ActionEvent e) -> {
            if (flock.hasLeftTheBuilding(w)) 

                spawnFlock(); 
            repaint();
       }).start(); 

    }

    public void spawnFlock() {

        Random rand = new Random(); 
        int n = rand.nextInt(599) + 1;
        flock = Flock.spawn(100, h - n, 20);
       flock2 = Flock.spawn(100, h - n, 1);

        }

    @Override
    public void paintComponent(Graphics gg) {
        super.paintComponent(gg);
        Graphics2D g = (Graphics2D) gg;
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        flock.run(g, w, h);
    g.draw(food);

     }


    public static void main(String[] args) {

        SwingUtilities.invokeLater(() -> {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setTitle("Simulator v0.6");
            f.setResizable(false);
            f.add(new Boids(), BorderLayout.CENTER);
            f.pack(); 
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });

    }
    }

class Boid {


    static final Random r = new Random();
    static final Vec migrate = new Vec(0.02, 0);
    static final int size = 3;
/*    static final Path2D shape = new Path2D.Double();

    static {
        shape.moveTo(0, -size * 2);
        shape.lineTo(-size, size * 2);
        shape.lineTo(size, size * 2);
        shape.closePath();
    }
 */
    final double maxForce, maxSpeed;

    Vec location, velocity, acceleration;
    private boolean included = true;

    Boid(double x, double y) {
        acceleration = new Vec();
        velocity = new Vec(r.nextInt(3) + 1, r.nextInt(3) - 1);
        location = new Vec(x, y);
        maxSpeed = 3.0;
        maxForce = 0.05;
    }
    void update() {
          velocity.add(acceleration);
            velocity.limit(maxSpeed);
            location.add(velocity);
            acceleration.mult(0);

    }

    void applyForce(Vec force) {
        acceleration.add(force);
    }

    Vec seek(Vec target) {
        Vec steer = Vec.sub(target, location);
        steer.normalize();
        steer.mult(maxSpeed);
        steer.sub(velocity);
        steer.limit(maxForce);
        return steer;
    }

    void flock(Graphics2D g, List<Boid> boids) {
        view(g, boids);

        Vec rule1 = separation(boids);
        Vec rule2 = alignment(boids);
        Vec rule3 = cohesion(boids);

        rule1.mult(2.5);
        rule2.mult(1.5);
        rule3.mult(1.3);

        applyForce(rule1);
        applyForce(rule2);
        applyForce(rule3);
        applyForce(migrate);


    }
    public boolean doAreasCollide(Area area1, Area area2) {
        boolean collide = false;

        Area collide1 = new Area(area1);
        collide1.subtract(area2);
        if (!collide1.equals(area1)) {
            collide = true;
        }

        Area collide2 = new Area(area2);
        collide2.subtract(area1);
        if (!collide2.equals(area2)) {
            collide = true;
        }

        return collide;
    }



    void view(Graphics2D g, List<Boid> boids) {
        double sightDistance = 100;
        double peripheryAngle = PI * 0.85;

        for (Boid b : boids) {
            b.included = false;

            if (b == this)
                continue;

            double d = Vec.dist(location, b.location);
            if (d <= 0 || d > sightDistance)
                continue;

            Vec lineOfSight = Vec.sub(b.location, location);

            double angle = Vec.angleBetween(lineOfSight, velocity);
            if (angle < peripheryAngle)
                b.included = true;
        }
    }

    Vec separation(List<Boid> boids) {
           double desiredSeparation = 25;

            Vec steer = new Vec(0, 0);
            int count = 0;
            for (Boid b : boids) {
                if (!b.included)
                    continue;

                double d = Vec.dist(location, b.location);
                if ((d > 0) && (d < desiredSeparation)) {
                    Vec diff = Vec.sub(location, b.location);
                    diff.normalize();
                    diff.div(d);        // weight by distance
                    steer.add(diff);
                    count++;
                }
            }
            if (count > 0) {
                steer.div(count);
            }

            if (steer.mag() > 0) {
                steer.normalize();
                steer.mult(maxSpeed);
                steer.sub(velocity);
                steer.limit(maxForce);
                return steer;
            }
            return new Vec(0, 0);
        }


    Vec alignment(List<Boid> boids) {
          double preferredDist = 50;

            Vec steer = new Vec(0, 0);
            int count = 0;

            for (Boid b : boids) {
                if (!b.included)
                    continue;

                double d = Vec.dist(location, b.location);
                if ((d > 0) && (d < preferredDist)) {
                    steer.add(b.velocity);
                    count++;
                }
            }

            if (count > 0) {
                steer.div(count);
                steer.normalize();
                steer.mult(maxSpeed);
                steer.sub(velocity);
                steer.limit(maxForce);
            }
            return steer;
        }

    Vec cohesion(List<Boid> boids) {
          double preferredDist = 50;

            Vec target = new Vec(0, 0);
            int count = 0;

            for (Boid b : boids) {
                if (!b.included)
                    continue;

                double d = Vec.dist(location, b.location);
                if ((d > 0) && (d < preferredDist)) {
                    target.add(b.location);
                    count++;
                }
            }
            if (count > 0) {
                target.div(count);
                return seek(target);
            }
            return target;
        }

    void draw(Graphics2D g) {
        AffineTransform save = g.getTransform();
        int[] xTriangle = {33,36,34};
        int[] yTriangle = {30,30,20};
  Area   triangle = new Area(new Polygon(xTriangle, yTriangle, 3));
        g.translate(location.x, location.y);
        g.rotate(velocity.heading() + PI / 2);
        g.setColor(Color.green);
     //   g.fill(shape);
        g.setColor(Color.green);
        g.draw(triangle);
        g.fill(triangle);
        g.setTransform(save);
    }




   public void run(Graphics2D g, List<Boid> boids, int w, int h) {  //similair method to run leader 
        flock(g, boids);
        update();
        draw(g);
    }


}

class Flock {
    List<Boid> boids;

    Flock() {
        boids = new ArrayList<>();
    }

    void run(Graphics2D g,  int w, int h) {
        for (Boid b : boids) {
            b.run(g, boids, w, h);

        }
    }

    boolean hasLeftTheBuilding(int w) {
        int count = 0;
        for (Boid b : boids) {
            if (b.location.x + Boid.size > w) //will also be used to calculate votes based on whether boids is near food
                count++;

            }
        return boids.size() == count;
    }

    void addBoid(Boid b) {
        boids.add(b);
    }

    static Flock spawn(double w, double h, int numBoids) {
        Flock flock = new Flock();
        for (int i = 0; i < numBoids; i++)
            flock.addBoid(new Boid(w, h));
        return flock;
    }

    int hasFoundFood(int noSuccessful) {
         Area food = new Area(new Rectangle(600,250,200,200));
          int[] xTriangle = {33,36,34};
          int[] yTriangle = {30,30,20};
         Area triangle = new Area(new Polygon(xTriangle, yTriangle, 3));
    noSuccessful = 0;
       for (Boid b : boids) {
    if(b.doAreasCollide(food, triangle)); {
        noSuccessful++;
    }

    }
  System.out.println(noSuccessful);
      return noSuccessful; 

}



}
class Vec {
    double x, y;

    Vec() {
    }

    Vec(double x, double y) {
        this.x = x;
        this.y = y;
    }

    void add(Vec v) {
        x += v.x;
        y += v.y;
    }

    void sub(Vec v) {
        x -= v.x;
        y -= v.y;
    }

    void div(double val) {
        x /= val;
        y /= val;
    }

    void mult(double val) {
        x *= val;
        y *= val;
    }

    double mag() {
        return sqrt(pow(x, 2) + pow(y, 2));
    }

    double dot(Vec v) {
        return x * v.x + y * v.y;
    }

    void normalize() {
        double mag = mag();
        if (mag != 0) {
            x /= mag;
            y /= mag;
        }
    }

    void limit(double lim) {
        double mag = mag();
        if (mag != 0 && mag > lim) {
            x *= lim / mag;
            y *= lim / mag;
        }
    }

    double heading() {
        return atan2(y, x);
    }

    static Vec sub(Vec v, Vec v2) {
        return new Vec(v.x - v2.x, v.y - v2.y);
    }

    static double dist(Vec v, Vec v2) {
        return sqrt(pow(v.x - v2.x, 2) + pow(v.y - v2.y, 2));
    }

    static double angleBetween(Vec v, Vec v2) {
        return acos(v.dot(v2) / (v.mag() * v2.mag()));
    }
}

Update: I have altered the code in line with Andrews code about collisions detection using Area's and it still wont work?! when I call hasFoundFood it still produces the value 20 which is incorrect as, at this point, it should be 0. I am pulling my hair out at this point and cannot for the life of me figure out what is wrong here?!

  • See [Collision detection with complex shapes](http://stackoverflow.com/a/14575043/418556) for a start. – Andrew Thompson Apr 18 '18 at 12:20
  • Hey, thanks for the tip; I have read through your post there a few times and it feels like the answer to my problem is definitely in there somewhere but I can't figure out how I can take your method of objectcollision and apply it to my code, my abilities are fairly poor in this area -> my current implementation is if ( Boid.shape.intersects(rect.getBounds()) == true); noSuccessful++; where 'rect' is a rectangle I created using Rectangle2D but this is falling flat on its face and always return a max for count. Even if you can't help thanks for the link! – totalthomas Apr 18 '18 at 12:55
  • If you can't achieve a solution from looking at that example, I suggest making an [edit] to the question to post a [mcve], like in that answer. Note the answer comes up with a complete, working solution in a single class that has less lines of code (131) than either of the two classes seen above (540 & 267 LOC respectively) & does not involve 3rd party APIs. This question won't be getting close attention from the people who can help, unless it is made into an MCVE – Andrew Thompson Apr 18 '18 at 13:57
  • I have just edited the question in to bring it more in line with the concept of MCVE. I really hope this helps as, despite trying to adapt the code from the link you provided I am still not having any luck – totalthomas Apr 18 '18 at 14:43
  • Well, it compiles and runs as a single copy/paste, but is still rather long. That aside, it is close enough to being an MCVE for me to run it .. and get confused. What method attempts to count the number of birds? Is it `hasFoundFood(..)`? I put a `println` in that method and nothing is printing. What is calling it? BTW(1) - simple debugging like that is something you could be doing. BTW(2) - lose all the `static` declarations unless you can explain why the member needs to be or should be `static`. Using `static` *causes* more problems than it *solves*, when used without understanding. – Andrew Thompson Apr 18 '18 at 15:37
  • I have just updated the code with a small update - the method flock.hasfoundfood is called at the very start, whereby it prints the value 20 (which is incorrect as no collisions can have occured at this point) meaning that there is still an issue with detecting collisions - I also implemented a loop to call the method recursively, which I will call when the problem with detecting collisions has been fixed. I am in the process of getting rid of the static declarations as I type this as you make a very good point; thank you for the assistance so far! – totalthomas Apr 19 '18 at 08:53
  • I have included a println in hasFoundFood which prints the value 20 once at the start. When debugging, I am calling checkFood to see if the value changes as it should go from 0 to however many triangles collide with the rectangle but it just spams 20 in the console. – totalthomas Apr 19 '18 at 08:56
  • Hey Andrew, I have been looking over your code that you posted yesterday and it appears that I might be able to find the solution to my problem in there by adapting your algorithm for my own purposes. However, I was wondering if you know how I could stop the triangles from jittering as they do? I have updated the code with the triangles now using Area, which is definitely the way forward! – totalthomas Apr 19 '18 at 12:25
  • *"I was wondering if you know how I could stop the triangles from jittering as they do?"* Huh? When running the first version of the code that I commented on, I didn't notice any 'jittering'. In fact, I thought the way each bird & the flock moved, was fluid and kind of cool! – Andrew Thompson Apr 19 '18 at 14:56
  • Oh.. OK. I see you meant jittery in the ***latest*** code. What was different in the code besides changing to areas? – Andrew Thompson Apr 19 '18 at 16:15
  • I remove the following section: 'code' static final Path2D shape = new Path2D.Double(); static { shape.moveTo(0, -size * 2); shape.lineTo(-size, size * 2); shape.lineTo(size, size * 2); shape.closePath(); } This is where the actual shape was drawn but to be able to detect collisions I am now going to use Area; other than adding the following to create the 'areas' in "draw" int[] xTriangle = {33,36,34}; int[] yTriangle = {30,30,20}; Area triangle = new Area(new Polygon(xTriangle, yTriangle, 3)); nothing else had changed – totalthomas Apr 20 '18 at 04:29
  • Hey Andrew, I have updated the code in line with your code about object collision detection for areas and it still wont work. I actually have no idea what is wrong with it at this point, I'm sure it's something simply but I have been at it for hours and can't figure it out; when I call hasFoundFood it still produces 20 when it should be 0 (called at start of program)....I literally don't know what else to do – totalthomas Apr 20 '18 at 05:37
  • Hey Andrew I have asked a new question with some updated code, I would really appreciate it if you could take a look at it – totalthomas Apr 22 '18 at 15:30
  • Could you please take a look at this? – totalthomas Apr 23 '18 at 10:24

0 Answers0