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()
}
6
  • 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: i.sstatic.net/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? Commented Sep 30, 2020 at 20:58
  • @DonMag I think that makes sense / describes what's happening Commented Sep 30, 2020 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. Commented Oct 1, 2020 at 3: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?) Commented Oct 1, 2020 at 4:13
  • @narner How and where are you adding circles to your view? Commented Oct 1, 2020 at 18:55

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

Comments

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.

1 Comment

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..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.