10

enter image description here

From the image above, given an initial position of b0(x, y), an end position of b1(x, y) and positions a(x, y) and c(x, y). How can I predetermine if square B0 will move from b0(x, y) to b1(x, y) without getting into contact with rectangle A and C? I believe that the angle will be needed.

Community
  • 1
  • 1
ItsMeAgain
  • 595
  • 4
  • 18
  • no angle is needed, you know your start box, and your end box, just make lines from each point on B0 to its respective point on B1, and see if these lines intersect with A or C – Knight0fDragon Jan 04 '16 at 01:08
  • 3
    @Knight0fDragon please I'm not sure how to `just make lines from each point on B0 to its respective point on B1, and see if these lines intersect with A or C ` – ItsMeAgain Jan 04 '16 at 01:23
  • This is basic math, google how to make a line with 2 points, and how to check if a line intersects with a box – Knight0fDragon Jan 04 '16 at 01:24
  • btw, the only way the box can get into the space is if it goes straight up, so technically, all you need to do is see if B0.x <> B1.x, then it will make contact before positioning – Knight0fDragon Jan 04 '16 at 01:33
  • 2
    @Knight0fDragon have you taken the gap between A and B1 and between B1 and C into consideration? – oopology Jan 04 '16 at 01:54
  • oh yeah, if the box is smaller then you need to do the formula, if the box fits perfectly you do not, @oopology thanks – Knight0fDragon Jan 04 '16 at 01:57
  • 1
    How are the (relative) alignments and dimensions? Has A, B and C always the same height? Is A, B1 and C vertically centered (on a horizontal line)? Is B1 centered in the gap between A and C? – Qbyte Jan 04 '16 at 14:03
  • @Qbyte yes, the height is always the same for each of them. Yes, each of them is centre aligned. Yes, B1 is centred in the gap. – ItsMeAgain Jan 04 '16 at 16:56
  • @Knight0fDragon `all you need to do is see if B0.x <> B1.x`. Where B0.x < ? > B1.x. I don't think it's that simple, as @Qbyte asked some important questions that have to be considered. – NSSwift Jan 05 '16 at 07:06
  • @NSSwift my first comment handles qbytes question, your comment only asks if the box fits, my proposal will tell if a collision will happen if when b goes from 0 to 1 – Knight0fDragon Jan 05 '16 at 07:09
  • @Knight0fDragon okay, so basically, two lines at the corners from B0 to B1 can handle this. – NSSwift Jan 05 '16 at 07:19
  • @NSSwift, if you were optimizing it, you would have to do whatever 2 corners would make the box the widest, so if the box is going in a top right direction, you would use the top left and bottom right corners of b. – Knight0fDragon Jan 05 '16 at 07:24
  • @Knight0fDragon this is certainly not basic maths. I am reading about a cross product approach to finding the intersection – ItsMeAgain Jan 05 '16 at 07:57
  • It is basic, and i believe cgrect has it built in, but here is a post on SO that may help you. http://stackoverflow.com/questions/16203760/how-to-check-if-line-segment-intersects-a-rectangle – Knight0fDragon Jan 05 '16 at 07:59
  • I assume the box need to fit with a single motion in a straight line, right? You do not consider e.g. moving horizontally until it aligns with the hole and then forwards? – Nicolas Miari Jan 06 '16 at 02:58

1 Answers1

11

Some observations...

If box B's initial position is to the right of the ending position (in the gap), then the box can successfully move to the ending position without colliding with the other boxes only if theta is a counter-clockwise angle (see Figure below). For this test, use the box B's top-right corner and the bottom-left corner of C.

enter image description here

Similarly, If box B's initial position is to the left of the ending position, then it can successfully move to the ending position without colliding with the other boxes only if theta is a counter-clockwise angle (see Figure below). For this test, use the box B's top-left corner and the bottom-right corner of A.

enter image description here

Some code...

First, extend CGPoint to determine the corners of a box.

extension CGPoint {
    func bottomLeftCorner(size:CGSize) -> CGPoint {
        return CGPoint (x:x - size.width/2.0, y:y - size.height/2.0)
    }

    func bottomRightCorner(size:CGSize) -> CGPoint {
        return CGPoint(x:x + size.width/2.0, y:y - size.height/2.0)
    }

    func topLeftCorner(size:CGSize) -> CGPoint {
        return CGPoint (x:x - size.width/2.0, y:y + size.height/2.0)
    }

    func topRightCorner(size:CGSize) -> CGPoint {
        return CGPoint(x:x + size.width/2.0, y:y + size.height/2.0)
    }
}

The following code allows the user to drop/drag box B. While the user moves the box, the code performs an on-the-fly test to see if the box can move into the gap without colliding with the other boxes.

class GameScene: SKScene {

    let size1 = CGSize(width: 100, height: 50)
    let size2 = CGSize(width: 50, height: 50)
    let size3 = CGSize(width: 100, height: 50)

    var boxA:SKSpriteNode!
    var boxB:SKSpriteNode!
    var boxC:SKSpriteNode!

    var center:CGPoint!

    override func didMove(to view: SKView) {

        // This is box B's ending position
        center = CGPoint (x:0,y:0)

        // Define and add the boxes to the scene
        boxA = SKSpriteNode(color: SKColor.yellow, size: size1)
        boxB = SKSpriteNode(color: SKColor.red, size: size2)
        boxC = SKSpriteNode(color: SKColor.blue, size: size3)

        boxA.position = CGPoint(x: -size1.width, y: 0)
        boxB.position = CGPoint(x: 0, y: 0)
        boxC.position = CGPoint(x: size3.width, y: 0)

        boxB.zPosition = 1

        addChild(boxA)
        addChild(boxB)
        addChild(boxC)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in touches {
            let location = touch.location(in: self)

            // Allow user to drag box to a new location
            boxB.position = location

            // Find the appropriate corners
            var cornerA:CGPoint!
            var cornerB:CGPoint!
            var cornerC:CGPoint!
            if (boxB.position.x < center.x) {
                cornerA = boxA.position.bottomRightCorner(size: boxA.size)
                cornerB = boxB.position.topLeftCorner(size: boxB.size)
                cornerC = center.topLeftCorner(size: boxB.size)
            }
            else {
                cornerA = center.topRightCorner(size: boxB.size)
                cornerB = boxB.position.topRightCorner(size: boxB.size)
                cornerC = boxC.position.bottomLeftCorner(size: boxC.size)
            }
            // Test if box B can move in the gap without colliding
            if isCounterClockwise(A: cornerA, B: cornerB, C: cornerC) {
                boxB.color = SKColor.green
            }
            else {
                boxB.color = SKColor.red
            }
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        // Move box B to the ending position
        let action = SKAction.move(to: center, duration: 2)
        boxB.run(action)
    }

    // Test direction of angle between line segments AB and AC
    func isCounterClockwise (A:CGPoint, B:CGPoint, C:CGPoint) -> Bool {
        return (C.y-A.y)*(B.x-A.x) > (B.y-A.y)*(C.x-A.x)
    }
}

and a video clip...

enter image description here

Box B turns green if it can move into the gap without colliding and turns red if not.

0x141E
  • 12,613
  • 2
  • 41
  • 54