0

I am trying to design a geometric intersection API. Below is the code to represent geometric elements.

#include <iostream>
#include <memory>

// Since I did not write the Shape class I cannot edit or change the geometric elements class. 
class Shape {
  //
public:
  virtual ~Shape() {}
};

class Segment : public Shape {
public:
  ~Segment() {}
};

class Triangle : public Shape {
public:
  ~Triangle() {}
};

class Quad : public Shape {
public:
  ~Quad() {}
};

// Below is the function that handles the intersection of the geometric elements.


bool intersect(const std::shared_ptr<Segment> &s1,
               const std::shared_ptr<Segment> &s2) {
  std::cout << "Seg Seg " << std::endl;
  // Algorithm goes here
  return true;
}

bool intersect(const std::shared_ptr<Segment> &s1,
               const std::shared_ptr<Triangle> &t1) {
  std::cout << "Seg Tri " << std::endl;
  // Algorithm goes here
  return true;
}

bool intersect(const std::shared_ptr<Triangle> &t1, 
               const std::shared_ptr<Segment> &s1
               ) {
  return intersect(s1, t1);
}

bool intersect(const std::shared_ptr<Shape> &s1,
               const std::shared_ptr<Shape> &s2) {
  // Segment Segment
  { 
    const std::shared_ptr<Segment> segment1 =
      std::dynamic_pointer_cast<Segment>(s1);
    const std::shared_ptr<Segment> segment2 =
      std::dynamic_pointer_cast<Segment>(s2);

    if (segment1 && segment2) {
     return intersect(segment1, segment2);
    }
  }
  // Segment Triangle
  {
    const std::shared_ptr<Segment> segment =
      std::dynamic_pointer_cast<Segment>(s1);
    const std::shared_ptr<Triangle> triangle =
      std::dynamic_pointer_cast<Triangle>(s2);

    if (segment && triangle) {
      return intersect(segment, triangle);
    }
  }

 // Triangle Segment
  {
    const std::shared_ptr<Triangle> triangle =
      std::dynamic_pointer_cast<Triangle>(s1);
    const std::shared_ptr<Segment> segment =
      std::dynamic_pointer_cast<Segment>(s2);

    if (segment && triangle) {
      return intersect(triangle, segment);
    }
  }
    // Handle other types appropriately .. 
  return false;
}

int main() {
  std::shared_ptr<Shape> s1(std::make_shared<Segment>());
  std::shared_ptr<Shape> s2(std::make_shared<Segment>());
  std::shared_ptr<Shape> t1(std::make_shared<Triangle>());

  bool ret_val_seg_seg = intersect(s1, s2);
  bool ret_val_seg_tri = intersect(s1, t1);
  bool ret_val_tri_seg = intersect(t1, s1);
}

My main concern is with code that is inside function bool intersect(const std::shared_ptr<Shape> &s1, const std::shared_ptr<Shape> &s2) and the use of dynamic_pointer_cast. Is there any better way to handle the geometric elements. I am open any suggestion.

My worry in that function is :

To do Triangle Segment intersection it has to go through six dynamic cast and three if statement. The last combination of intersection get hit the most.

  • 3
    Why do you use shared_ptr as function arguments? Within the intersect functions you do not have any ownership changes, so you can directly use a const reference to the shape types instead of a const reference to a shared_ptr. – pschill Feb 5 at 8:09
  • 4
    Have you heard of the visitor pattern? It's a special case of multiple dispatch, but extends easily enough to double dispatch, which is what you need here. If not, entering those terns into your preferred search engine should help. – Useless Feb 5 at 9:04
  • About smart pointers: See what Herb Sutter says (GotW 91). – Aconcagua Feb 6 at 18:31
1

My first thought is, "why do you need separate classes for different primitives?" All of the primitives you list are made from 1 or more segments. So why not just have a single Shape class which contains a std::vector of Segments? Then your intersection test becomes simply testing each segment of one Shape against all segments of the other Shape without needing to have any special cases.

  • For example, Triangle Segment intersection is not just three segment segment intersection, because there can be condition where segment is inside triangle. Also I do need separate classes for other variosu reasons. – solti Feb 5 at 11:41
  • 1
    You can still test for one being inside the other without having separate classes. But even if you do need separate classes you can still implement the intersection test once in the base class and not have n different versions of an intersection test. – user1118321 Feb 5 at 17:02

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.