Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add body, is it colliding? #478

Closed
ghost opened this issue Aug 27, 2017 · 5 comments
Closed

Add body, is it colliding? #478

ghost opened this issue Aug 27, 2017 · 5 comments

Comments

@ghost
Copy link

ghost commented Aug 27, 2017

Looked around but don't see an easy way to do this. When adding a body to the world, is there a way to easily ask if that body is "on top" (colliding with) any existing body? Tried to process "collisionStart/End" but it looks like those happen only as bodies move around.

Two additions:

  • To explain: I need to detect if an application drag/drop event, which normally would add a body to my world, would result in a colliding body. If so, I want to change its collisionFilter characteristics.
  • I've studied the code in the MouseConstraint and think it will work for me with some modification.

So comments would be welcome, otherwise you can close this issue. Thanks.

@liabru
Copy link
Owner

liabru commented Sep 17, 2017

Try this:

var collision = Matter.SAT.collides(bodyA, bodyB);

if (collision.collided) {
    // do something
}

Where bodyA is your new body and bodyB is some other body to check, so naturally you should loop this over every body that could be collided. For instance those from Composite.allBodies(world).

Actually, this might be a nice addition to Matter.Query!

@ghost
Copy link
Author

ghost commented Sep 17, 2017

Shamelessly stealing code, I created a routine that returns intersecting (colliding) bodies given a body and a filter. I use this to detect drag/drop collisions, but also to display intersections among bodies.

        self.intersections = function (target, filter) {
            var result = [],
                oldFilter = target.collisionFilter;

            target.collisionFilter = filter || FILTER_TILE;

            var _getRegion = function (body) {
                var grid = engine.broadphase,
                    bounds = body.bounds,
                    startCol = Math.floor(bounds.min.x / grid.bucketWidth),
                    endCol = Math.floor(bounds.max.x / grid.bucketWidth),
                    startRow = Math.floor(bounds.min.y / grid.bucketHeight),
                    endRow = Math.floor(bounds.max.y / grid.bucketHeight);
                return {
                    id: startCol + ',' + endCol + ',' + startRow + ',' + endRow,
                    startCol: startCol,
                    endCol: endCol,
                    startRow: startRow,
                    endRow: endRow
                }
            };

            var _getBucketId = function (column, row) {
                    return column + ',' + row;
                },
                i, row, col;

            var region = _getRegion(target);
            // var region = target.region;

            for (col = region.startCol; col <= region.endCol; col++) {
                for (row = region.startRow; row <= region.endRow; row++) {

                    var bucketId = _getBucketId(col, row),
                        bucket = engine.broadphase.buckets[bucketId];

                    // broad phase
                    if (bucket) {
                        for (i = 0; i < bucket.length; i++) {
                            var body = bucket[i];
                            if (body.id !== target.id) {
                                if (Matter.Detector.canCollide(target.collisionFilter, body.collisionFilter)) {
                                    // mid phase
                                    if (Matter.Bounds.overlaps(target.bounds, body.bounds)) {
                                        parts_scan:
                                            for (var j = target.parts.length > 1 ? 1 : 0; j < target.parts.length; j++) {
                                                var partA = target.parts[j];

                                                for (var k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
                                                    var partB = body.parts[k];

                                                    if ((partA === target && partB === body) || Bounds.overlaps(partA.bounds, partB.bounds)) {
                                                        // narrow phase
                                                        var collision = Matter.SAT.collides(partA, partB);
                                                        if (collision.collided) {
                                                            if (!result.includes(body)) {
                                                                result.push(body);
                                                            }
                                                            break parts_scan;
                                                        }
                                                    }
                                                }
                                            }
                                    }
                                }
                            }
                        }
                    }

                }
            }
            target.collisionFilter = oldFilter;
            return result;
        };

@ghost
Copy link
Author

ghost commented Oct 5, 2017

I spent today creating a prototype of a pull request which would implement my collidingBodies idea. I want to make it efficient, so I want to leverage broadphase to narrow down the bodies to test.

I created a Grid.findBodies call to return all the bodies in buckets within a given bounds. And then I wrote a routine in my own application that would loop through these bodies and return
identified collisions for all the parts of the target with all the parts of the bodies.

In a "real" pull request I would generalize this more.

Although I'm happy to create a pull request, in studying your existing independent modules it is not clear where a routine such as mine would make sense. Any module I choose seems to introduce a new unnatural dependency for that module.

So my question is: would such a routine make sense in matterjs? Or should I just keep it in my codebase?

    /**
     * Given a bounds, returns contained bodies
     */
    Grid.findBodies = function (grid, bounds) {
        var i,
            row,
            col,
            region = _getRegion(grid, {bounds:bounds}),
            result = [];

        for (col = region.startCol; col <= region.endCol; col++) {
            for (row = region.startRow; row <= region.endRow; row++) {
                var bucketId = _getBucketId(col, row),
                    bucket = grid.buckets[bucketId];
                if (bucket) {
                    result.push.apply(result, bucket);
                }
            }
        }

        return result;
    };
And the routine in my code:
    self.collisions = function (target, filter) {
        var result = {},
            oldFilter = target.collisionFilter,
            i,
            grid = engine.broadphase,
            bodies = grid.controller && grid.controller.findBodies ? grid.controller.findBodies(grid, target.bounds) : engine.world.bodies;

        var makeResultKey = function (partA, partB) {
            return partA.id + ":" + partB.id;
        };

        target.collisionFilter = filter || FILTER_TILE;

        for (i = 0; i < bodies.length; i++) {
            var body = bodies[i];
            if (body.id !== target.id && (!target.ignoreId || target.ignoreId !== body.id)) {
                if (Matter.Detector.canCollide(target.collisionFilter, body.collisionFilter)) {
                    // mid phase
                    if (Matter.Bounds.overlaps(target.bounds, body.bounds)) {
                        for (var j = target.parts.length > 1 ? 1 : 0; j < target.parts.length; j++) {
                            var partA = target.parts[j];

                            for (var k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
                                var partB = body.parts[k];

                                if ((partA === target && partB === body) || Matter.Bounds.overlaps(partA.bounds, partB.bounds)) {
                                    // narrow phase
                                    var collision = Matter.SAT.collides(partA, partB);
                                    if (collision.collided) {
                                        var resultKey = makeResultKey(partA, partB);
                                        if (!result[resultKey]) {
                                            result[resultKey] = {target: partA, part: partB};
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        target.collisionFilter = oldFilter;
        return result;
    };

@liabru
Copy link
Owner

liabru commented Oct 6, 2017

Can you put this in a jsfiddle giving an example of it working? Then I can get my head around it better!

@liabru liabru closed this as completed in 6593a72 Nov 26, 2017
@xfstef
Copy link

xfstef commented Apr 3, 2018

Hey so I managed to check for collisions by using the Bounds.overlaps function. See example below:

....
var Bounds = Matter.Bounds;
....
function checkIfSpaceOccupied(cx, cy, approximateRange) {
var newBounds = {
min: {
x: cx - approximateRange,
y: cy - approximateRange
},
max: {
x: cx + approximateRange,
y: cy + approximateRange
}
}
for(var j = 0; j < worldObjects.length; j++) {
if (Bounds.overlaps(worldObjects[j].bounds, newBounds)) {
return true;
}
}
return false;
}
....

This example is not perfect as it is intended to be used with circular objects, but you guys get the idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants