Using Visitor Pattern to detect intersection between two shapes

房东的猫 提交于 2019-12-08 13:09:02

问题


I realize this is a very specific question so it would be helpful if the answer people give includes explicit codes on how to do this. Thanks.

I have an abstract base class Shape:

class Shape
{
    .....
    virtual bool GetIntersection(Shape* _shape) = 0;
}

class Circle : public Shape  {...}
class Triangle : public Shape {...}

Both these derived classes overrides GetIntersection;

I have:

//main.cpp
....
Shape* shape;
Shape* circle = new Circle;

if(input == 0) shape = new Circle;
else shape = new Triangle;

circle->GetIntersection(shape);

which gives an error.

I read something about visitor patterns and think that this might be the way to solve my problem as I basically need to determine which derived class the parameter to GetIntersection is using. Can someone explain how I would implement a visitor pattern for this case? Or if there is another simpler solution to this problem.

Any help would be appreciated.


回答1:


I'm right now facing the same problem with collisions between different shapes.

I think we cannot use the Visitor pattern "as is", because it requires that the classes of a hierarchy A (visitors) visit the classes of another hierarchy B (visited elements), where the classes of B only know about the abstraction of A (IVisitor, for example) while the classes of B know about the subclasses of A (VisitedElement1, VisitedElement2... subclasses of VisitedElement).

In our case, we are trying to visit Shapes with Shapes while keeping our Shape class decoupled from the subclasses, so the Visitor pattern doesn't apply.

The best solution I can think of is that the Shape class, directly or through another interface, declares "GetSpecificIntersection" methods for all the subtypes:

class Shape
{
    .....
public:
    virtual bool GetIntersection(Shape* _shape) = 0;    
protected:
    virtual bool GetSpecificIntersection(Circle* _circle) = 0;
    virtual bool GetSpecificIntersection(Triangle* _triangle) = 0;
}

class Circle
{
    .....
public:
    virtual bool GetIntersection(Shape* _shape);
    {
        return _shape->GetSpecificIntersection(this);
    }
protected:
    virtual bool GetSpecificIntersection(Circle* _circle) { ... }
    virtual bool GetSpecificIntersection(Triangle* _triangle) { ... }
}

class Triangle { /* analog to Circle */ }

Therefore, you must implement those specific intersection methods for each kind of shape to any other kind of shape and the Shape class is coupled to all the possible shapes. If I'm not wrong, this violates OCP but preserves LSP.

Another option I've just come up with is to "favor composition over inheritance" and make something like this:

class Shape
{
    .....
public:
    virtual bool GetIntersection(Shape* _shape)
    {
        return _shape->figure->GetIntersection(this->figure);
    }

private:
    Figure* figure;
}

class Figure
{
    .....
public:
    virtual bool GetIntersection(Figure* _figure) = 0;
protected:
    virtual bool GetSpecificIntersection(Circle* _circle) = 0;
    virtual bool GetSpecificIntersection(Triangle* _triangle) = 0;
}

class Circle
{
    .....
public:
    virtual bool GetIntersection(Figure* _figure)
    {
        return _figure->GetSpecificIntersection(this);
    }
protected:
    virtual bool GetSpecificIntersection(Circle* _circle) { ... }
    virtual bool GetSpecificIntersection(Triangle* _triangle) { ... }
}

class Triangle { /* analog to Circle */ }

It's the same as before, but by doing this you decouple your Shape class (which is the interface used by the classes of your game) from the different "Figures" (I didn't come up with a better name), so adding new Figures shouldn't even lead to recompile your client classes. This violates OCP only in the "Figures" hierarchy, and preserves LSP for both "Shape" and "Figure".

If someone can suggest a better way to do this while avoiding downcasting, I'll be very thankful :D




回答2:


This question seems to overlap with this one: Design pattern for checking collision between shapes

in which the visitor pattern is used, and seems quite clear. The trick is to make each shape (circle, square, whatever) be the visitor for each other shape.




回答3:


your solution is right.

You could at first get a bounding box and check collision between bounding boxes. Keep in mind that in any case, you can't compare apples to oranges. So start comparing both bounding boxes.

Then if there is a collision, you might want to look for individual points. Each shape can return points or vector of its contour and you can easily find out if there is a collision or not. There might be some ways to speed up things like knowing if a point is inside a square is easy or knowing if a point is inside a circle is just checking the length from a point to center is bigger than the radius.

You can then do something like Shape { bool isPointInside(Point p) = 0; }

Which will be much faster than checking for each individual points.

You can also override getIntersection for circle to check differently for squares etc. Instead of just Shape.




回答4:


You may use f.x. dynamic_cast<Circle *>(shape) to see if a shape is a Circle. It returns null if shape is not a Circle, a valid pointer to a Circle instance otherwise.

Us this if one of your GetInserection methods knows how to intersect with a specific other Shape sub-type.

Apart from that, follow the guideline from Loïc Faure-Lacroix's answer.



来源:https://stackoverflow.com/questions/11381774/using-visitor-pattern-to-detect-intersection-between-two-shapes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!