3

I have a loop below that is supposed to detect if a view in an array of UIView's overlap with each other at all. If they do, then adjust their center value.

Each view is 25x25.

func drawCircles() {
        for i in 0..<circles.count{
            circles[i].center = getRandomPoint()
            for j in 0..<circles.count{
                if(i != j) {
                    let comparingCentre = circles[j].center
                    let dist = distance(comparingCentre, circles[i].center)
                    if dist <= 25 {
                        
                        var newCenter = circles[i].center
                        var centersVector = CGVector(dx: newCenter.x - comparingCentre.x, dy: newCenter.y - comparingCentre.y)
                     
                        //Circle width is 25
                        centersVector.dx *= 26 / dist
                        centersVector.dy *= 26 / dist
                        newCenter.x = comparingCentre.x + centersVector.dx
                        newCenter.y = comparingCentre.y + centersVector.dy
                        circles[i].center = newCenter
                    }
                }
            }
        }
...
}

Below is the method for generating a random CGPoint that gets set to the view's center:

func getRandomPoint() -> CGPoint {
    let viewMidX = self.circlesView.bounds.midX
    let viewMidY = self.circlesView.bounds.midY
    
    let xPosition = self.circlesView.frame.midX - viewMidX + CGFloat(arc4random_uniform(UInt32(viewMidX*2)))
    let yPosition = self.circlesView.frame.midY - viewMidY + CGFloat(arc4random_uniform(UInt32(viewMidY*2)))
    let point = CGPoint(x: xPosition, y: yPosition)
    return point
}

And below is the method for determining the distance between two UIView's.

func distance(_ a: CGPoint, _ b: CGPoint) -> CGFloat {
    let xDist = a.x - b.x
    let yDist = a.y - b.y
    return CGFloat(hypot(xDist, yDist))
}

However, I'm still experiencing, occasionally, when two view's will overlap each other (see the red-circled section below):

enter image description here

EDIT This is code that adds the circles to the view:

func generateCircles() {
    numberOfCircles = Int.random(in: 1..<50)
    let circleWidth = CGFloat(25)
    let circleHeight = circleWidth
    
    var i = 0
    while i < numberOfCircles {
        let circleView = CircleView(frame: CGRect(x: 0.0, y: 0.0, width: circleWidth, height: circleHeight))
        let number = Int.random(in: 0..<2)
        if number == 1 {
            circleView.mainColor = .yellow
        } else {
            circleView.mainColor = .blue
        }
      
        circles.append(circleView)
        i += 1
    }
    drawCircles()
}
narner
  • 2,908
  • 3
  • 26
  • 63
  • I expect you're still getting overlaps because you're only moving **two** views apart. Without me trying to run your code, what happens if your random positioning ends up with `v1`, `v2`, and `v3` like this: https://i.stack.imgur.com/fb1k0.png ? Is your code going to handle moving `v3` away from `v1`, only to have it overlap `v2`? And then, possibly, move `v3` away from `v2` ... putting it back overlapping with `v1` again? – DonMag Sep 30 '20 at 20:58
  • @DonMag I think that makes sense / describes what's happening – narner Sep 30 '20 at 21:07
  • Why not change the logic of your getRandomPoint() so it only returns a valid random point and then you wouldn’t have to worry about moving circles. – valosip Oct 01 '20 at 03:57
  • @valosip Well, any point on the screen would theoretically be valid; it's just a matter of if determining the overlap (or maybe I'm not following you?) – narner Oct 01 '20 at 04:13
  • @narner How and where are you adding circles to your view? – valosip Oct 01 '20 at 18:55
  • @valosip just edited the question! – narner Oct 01 '20 at 19:06

2 Answers2

2

Here's a quick option based on my comment above about moving the logic for comparing distance into creation of the random point: This can be cleaned up, but I didn't want to fully rewrite it and used as much of your code as possible.

In the generateCirles function I only changed how you init your CircleView

func generateCircles() {
    numberOfCircles = Int.random(in: 1..<50)
    
    let circleWidth = CGFloat(25)
    let circleHeight = circleWidth
    
    for _ in 0..<numberOfCircles {
        let circleView = CircleView(frame: CGRect(origin: getRandomPoint(), size: (CGSize(width: circleWidth, height: circleHeight))))
        let number = Int.random(in: 0..<2)
        if number == 1 {
            circleView.mainColor = .yellow
        } else {
            circleView.mainColor = .blue
        }
        
        circles.append(circleView)
    }
    drawCircles()
}

In the getRandomPoint I added a check to see if the new point is a valid point based on your existing distance logic:

func getRandomPoint() -> CGPoint {
    let viewMidX = self.circlesView.bounds.midX
    let viewMidY = self.circlesView.bounds.midY
    
    let xPosition = self.circlesView.frame.midX - viewMidX + CGFloat(arc4random_uniform(UInt32(viewMidX*2)))
    let yPosition = self.circlesView.frame.midY - viewMidY + CGFloat(arc4random_uniform(UInt32(viewMidY*2)))
    let point = CGPoint(x: xPosition, y: yPosition)
    
    if !validatePoint(point) {
        return getRandomPoint()
    }
    return point
}

func validatePoint(_ point: CGPoint) -> Bool {
    for circle in circles {
        if distance(circle.frame.origin, point) <= 25 {
            return false
        }
    }
    return true
}

And I removed all of your code from drawCircles that you posted.

Here's what it looks like with 300 circles: (I used the view frame as the circlesView frame, so that's why the circles are over lapping with the screen bounds) This shouldn't be an issue for you, as I assume your circlesView frame is set to be smaller. enter image description here

valosip
  • 3,167
  • 1
  • 14
  • 26
1

I think you should be able to do something like this:

let firstCircle = circles[i]
let secondCircle = circles[j]
let intersect = firstCircle.frame.intersection(secondCircle.frame)

if intersect.isNull {
  // The circles do NOT overlap.
} else {
  // The circles overlap. Handle accordingly.
}

Note that this doesn't handle the various edge cases such as comparing the same circles with each other, but this should simplify understanding if they overlap.

AdamPro13
  • 7,232
  • 2
  • 30
  • 28
  • Yeah that's super close; I'm in the `else` section I just reset the center of `circles[i]` to be a new, random point; but then I'm in a situation where THAT point may then be intersection with `circles[j]`, so not sure how to handle that.. – narner Sep 30 '20 at 22:53