0

I have came across this sticky situation here so basically I am asked to write a function that should return a pointer to a figure if the point I clicked lies in the figure and null if the point doesn't lie in any figure.

CFigure *ApplicationManager::GetFigure(int x, int y) const
{
    //If a figure is found return a pointer to it.
    //if this point (x,y) does not belong to any figure return NULL
    int c = 0;
    for (size_t i = 0; i < FigCount; i++)
    {
        if (dynamic_cast<CRectangle*> (FigList[i]))
        {
            CFigure* basepointer = FigList[i];
            Point A = static_cast<CRectangle*>(basepointer)->GetCorner1();
            Point B = static_cast<CRectangle*>(basepointer)->GetCorner2();

            if ((x>=A.x && x<=B.x) || (x<=A.x && x>=B.x))
            {
                if ((y >= A.y && x <= B.y) || (y <= A.y && x >= B.y))
                {
                    c++;
                }
            }
        }
        else if (dynamic_cast<CCircle*> (FigList[i]))
        {
            CFigure* basepointer = FigList[i];
            Point A = static_cast<CCircle*>(basepointer)->getCntr();
            int B = static_cast<CCircle*>(basepointer)->GetRadius();

             double distance = sqrt(pow((x - A.x), 2) + pow((y - A.y), 2));
            if (distance<=(double)B)
            {
                c++;
            }
        }
        else if (dynamic_cast<CLine*> (FigList[i]))
        {
            CFigure* basepointer = FigList[i];
            Point A = static_cast<CLine*>(basepointer)->getPoint1();
            Point B = static_cast<CLine*>(basepointer)->getpoint2();
            double distance1 = sqrt(pow((x - A.x), 2) + pow((y - A.y), 2)); //Distance from point to P1
            double distance2 = sqrt(pow((x - B.x), 2) + pow((y - B.y), 2)); //Distance from Point to P2
            double distance3 = sqrt(pow((B.x - A.x), 2) + pow((B.y - A.y), 2)); //Distance from P1 to P2
            if (distance1+distance2==distance3)
            {
                c++;
            }
        }
        else
        {
            CFigure* basepointer = FigList[i];
            Point p1 = static_cast<CTriangle*>(basepointer)->getp1();
            Point p2 = static_cast<CTriangle*>(basepointer)->getp2();
            Point p3 = static_cast<CTriangle*>(basepointer)->getp3();
            float alpha = (((float)p2.y - (float)p3.y)*((float)x - (float)p3.x) + ((float)p3.x - (float)p2.x)*((float)y - (float)p3.y)) /
                (((float)p2.y - (float)p3.y)*((float)p1.x - (float)p3.x) + ((float)p3.x - (float)p2.x)*((float)p1.y - (float)p3.y));
            float beta = (((float)p3.y - (float)p1.y)*((float)x - (float)p3.x) + ((float)p1.x - (float)p3.x)*((float)y - (float)p3.y)) /
                (((float)p2.y - (float)p3.y)*((float)p1.x - (float)p3.x) + ((float)p3.x - (float)p2.x)*((float)p1.y - (float)p3.y));
            float gamma = 1.0f - alpha - beta;
            if (alpha>0 && beta>0 && gamma >0)
            {
                c++;
            }
        }
    }

    ///Add your code here to search for a figure given a point x,y  
    if (c==0)
    {
        return NULL;
    }

}

as you can see, I haven't decided on what to return yet, but my question is using dynamic cast the optimum solution here?

-CLine,CTriangle,CRectangle and CCircle are all derived classes from CFigure

4
  • 9
    You should implement the hit-detection via a polymorphic method that each class implements. Commented Apr 17, 2017 at 14:30
  • @OliverCharlesworth I am sorry I am not following, there is already a function that detects where the user has clicked Commented Apr 17, 2017 at 14:38
  • @AhmedKh, What Oliver is saying is that what you're doing is not the right OOP technique to use. You're basically have a big switch statement on the type of the figure. A better way would be have an abstract virtual method in the CFigure class (let's call it contains) that returns a bool telling you if the point is in the figure or not. Then just go through FigList and call contains with no cast needed.
    – pcarter
    Commented Apr 17, 2017 at 14:50
  • @pcarter so you're saying I should add 4 function in Cfigure that would check what figure is that exactly? if so Should I also use dynamic/static casting? EDIT: I mean one abstract virtual function. thanks for the help though Commented Apr 17, 2017 at 14:56

2 Answers 2

1

In class CFigure add

virtual bool isclicked(int x, int y) = 0;

This is a pure virtual function. All subclasses of CFigure must implement it. The subclass's implementation checks whether or not the click was inside its bounds and returns true or false accordingly.

The reduces ApplicationManager::GetFigure to something like

CFigure *ApplicationManager::GetFigure(int x, int y) const
{
    for (size_t i = 0; i < FigCount; i++)
    {
        if (FigList[i]->isclicked(x,y))
        {
            return FigList[i];
        }
    }
    return nullptr;
}

Through the magic of virtual functions and polymorphism, the program will figure out which subclass's isclicked function needs to be called with no further effort on your part.

0
0

You can use virtual functions to move the processing into each derived type rather than testing the types to decide how to process them.

See Virtual Functions

Instead of doing this:

struct B {};
struct D1: B {};
struct D2: B {};

// ...

void func(B* b)
{
    int c = 0;

    if(dynamic_cast<D1*>(b))
    {
        // do D1 stuff
        c = ...
    }
    else if(dynamic_cast<D2*>(b))
    {
        // do D2 stuff
        c = ...
    }
}

You should aim to have each sub-type know how to calculate itself:

struct B { virtual int calc() = 0; }; // virtual function calls derived type
struct D1: B { int calc() override { int c = 0; /* D1 calculation */ return c; } };
struct D2: B { int calc() override { int c = 0; /* D2 calculation */ return c; } };

// ...

void func(B* b)
{
    int c = b->calc(); // virtual means the correct type's function is used
}

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