0

I have a series of shape objects that can check if they intersect one another (an intersection is true if any part of either object overlaps the other). This works great with double dispatch because the intersection of two objects can be represented relative to either object and still be valid and true.

I recently added (read as: "attempted to add") a Contains method that tests whether or not a shape completely encloses another. The double dispatch fails because two shapes of differing sizes will work for Intersect but not Contains: shape_one is a big circle, and shape_two is a smaller circle completely enclosed by shape_one. The call shape_one.Contains(shape_two) will return true but shape_two.Contains(shape_one) will return false.

Is there a way to implement double dispatch for this type of scenario?

(Title edits are encouraged to increase comprehension of question...)

A sample of the Intersects methods with double dispatch:

bool Rectangle::Intersects(const Shape& shape) const {
    return shape.Intersects(*this);
}

bool Rectangle::Intersects(const Point& point) const {
    return point.Intersects(*this);
}

bool Rectangle::Intersects(const Line& line) const {
    return line.Intersects(*this);
}

bool Rectangle::Intersects(const Rectangle& rectangle) const {
    double myTop = this->GetY();
    double myLeft = this->GetX();
    double myRight = myLeft + this->GetWidth();
    double myBottom = myTop + this->GetHeight();
    double rTop = rectangle.GetY();
    double rLeft = rectangle.GetX();
    double rRight = rLeft + rectangle.GetWidth();
    double rBottom = rTop + rectangle.GetHeight();

    if(myTop > rBottom) return false;
    if(myBottom < rTop) return false;
    if(myLeft > rRight) return false;
    if(myRight < rLeft) return false;

    return true;
}

A sample of the Contains methods with double dispatch:

//THIS RESULTS IN A STACK OVERFLOW! DUE TO INFINITE RECURSION!
bool Rectangle::Contains(const Shape& shape) const {
    return this->Contains(shape);
}

//THIS IS NOT TRUE IN ALL CASES!
bool Rectangle::Contains(const Shape& shape) const {
    return shape.Contains(*this);
}


bool Rectangle::Contains(const Point& point) const {
    return this->Intersects(point);
}

bool Rectangle::Contains(const Line& line) const {
    return this->Intersects(line.GetPointOne()) && this->Intersects(line.GetPointTwo());
}

bool Rectangle::Contains(const Rectangle& rectangle) const {
    return this->Intersects(rectangle.GetTopLeft()) && this->Intersects(rectangle.GetTopRight()) && this->Intersects(rectangle.GetBottomLeft()) && this->Intersects(rectangle.GetBottomRight());
}
Casey
  • 10,297
  • 11
  • 59
  • 88

1 Answers1

0

Solved via pushing contains methods up to base class and a dynamic cast to the proper type with a type parameter in the base-to-base Contains call:

bool Shape::Contains(const Shape& shape) const {
    std::string type = shape.GetShapeType();
    if(type == "arc") {
        return this->Contains(dynamic_cast<const Arc&>(shape));
    } else if(type == "circle") {
        return this->Contains(dynamic_cast<const Circle&>(shape));
    } else if(type == "ellipse") {
        return this->Contains(dynamic_cast<const Ellipse&>(shape));
    } else if(type == "line") {
        return this->Contains(dynamic_cast<const Line&>(shape));
    } else if(type == "point") {
        return this->Contains(dynamic_cast<const Point&>(shape));
    } else if(type == "polygon") {
        return this->Contains(dynamic_cast<const Polygon&>(shape));
    } else if(type == "rectangle") {
        return this->Contains(dynamic_cast<const Rectangle&>(shape));
    } else if(type == "sector") {
        return this->Contains(dynamic_cast<const Sector&>(shape));
    } else if(type == "spline") {
        return this->Contains(dynamic_cast<const Spline&>(shape));
    } else if(type == "triangle") {
        return this->Contains(dynamic_cast<const Triangle&>(shape));
    } else {
        return false;
    }
}

bool Shape::Contains(const Point& point) const {
    return this->Intersects(point);
}

bool Shape::Contains(const Line& line) const {
    return this->Intersects(line.GetPointOne()) && this->Intersects(line.GetPointTwo());
}

bool Shape::Contains(const Rectangle& rectangle) const {
    return this->Intersects(rectangle.GetTopLeft()) && this->Intersects(rectangle.GetTopRight()) && this->Intersects(rectangle.GetBottomLeft()) && this->Intersects(rectangle.GetBottomRight());
}

//...etc
Casey
  • 10,297
  • 11
  • 59
  • 88