5
\$\begingroup\$

This is from Bjarne Stroustrup's C++ Programming: Principles and Practice, Chapter 13 Exercise 2:

Draw a box with rounded corners. Define a class Box, consisting of four lines and four arcs.

I've already defined class Arc and using existing facilities (class Lines) for drawing the lines.

Here is the code that draws a rounded box of random size:

roundedBox.h

//#include "GUI.h"
//#include "Simple_window.h"
//#include <iostream>

namespace Graph_lib{
// Class Arc
bool validInput(int w, int h);

class Arc: public Ellipse{
public:
  Arc(Point p, int w, int h, double s, double e)
  : Ellipse(Point(p.x, p.y), w, h), start(s), end(e) 
  { if (!validInput(w,h)) error("Invalid input Arc"); }

  void draw_lines() const;
  // starting and ending angle of the arc: start from 3'oclock counterclockwise
  double start;
  double end;
};

// Member function
void Arc::draw_lines() const{
    if(color().visibility()) fl_arc(point(0).x, point(0).y, major() , minor() , start, end);
}

//------------------------------------------------------------------------------------------------
// Class RoundedBox
class Box: public Lines{
public:
    Box(Point p, int w, int h);
    Box(Point ul, Point dr);

private:
    Point upperLeft;
    Point downRight;

    int width;
    int height; 
    // 1/4th of width and height, respectively 
    int roundWidth;
    int roundHeight;
};
//--------------------------------------------------------------------------------------------------
// Function Implementation
// Helper function
bool validInput(int w, int h){
    if (w < 0 || h < 0) return false;
    else return true;
}

bool validInput(Point ul, Point dr){
    if(ul.x - dr.x < 0 || ul.y - dr.y < 0) return false;
    return true;
}

// Class Constructors
Box::Box(Point p, int w, int h)
    : upperLeft(p), downRight(p.x + w, p.y + h), roundWidth(w / 4), roundHeight(h / 4) 
{ 
    if (!validInput(w,h)) error("Invalid input Box");
    Lines::add(Point(p.x + roundWidth, p.y), Point(p.x + w - roundWidth, p.y));
    Lines::add(Point(p.x + w, p.y + roundHeight), Point(p.x + w, p.y + h - roundHeight));
    Lines::add(Point(p.x + w - roundWidth, p.y + h), Point(p.x + roundWidth, p.y + h));
    Lines::add(Point(p.x, p.y + h - roundHeight), Point(p.x, p.y + roundHeight));
}

Box::Box(Point ul,Point dr) 
    : upperLeft(ul), downRight(dr), roundWidth((dr.x - ul.x) / 10.), roundHeight((dr.y - ul.y) / 10.) 
{ 
    if(!validInput(ul, dr)) error ("Invalid input Box");
    Lines::add(Point(ul.x + roundWidth, ul.y), Point(dr.x - roundWidth, ul.y));
    Lines::add(Point(dr.x, ul.y + roundHeight), Point(dr.x, dr.y - roundHeight));
    Lines::add(Point(dr.x - roundWidth, dr.y), Point(ul.x + roundWidth, dr.y));
    Lines::add(Point(ul.x, dr.y - roundHeight), Point(ul.x, ul.y + roundHeight));
}

} // end of Graph_lib namespace

roundedBox.cpp

#include "GUI.h"
#include "Simple_window.h"
#include <iostream>
#include "roundedBox.h"
//-----------------------------------------------------------------------------------------------------------------------

int main(){
 // window parameters
 int winWidth = 800;
 int winHeight = 600;
 Point center((x_max() - winWidth) / 2., (y_max() - winHeight) / 2.);
 Simple_window* sw = new Simple_window(center, winWidth, winHeight, "Chapter 13 Exercise 2");

 // rounded box parameters
 int width = 500;
 int height = 400;
 int xCoord = (sw->x_max() - width) / 2;
 int yCoord = (sw->y_max() - height) / 2;

try{
    // draw the 4 conrer arcs clockwise, starting from the top left corner
    // each center of an arc is shiftet away from the center of the rounded box by multiples of its width and height
    Graph_lib::Arc ulArc(Point(xCoord + width/2, yCoord + height/2), width/2, height/2, 90, 180); 
    ulArc.draw_lines();
    sw->attach(ulArc);
    Graph_lib::Arc urArc(Point(xCoord + width, yCoord + height/2), width/2, height/2, 0, 90); 
    urArc.draw_lines();
    sw->attach(urArc);
    Graph_lib::Arc drArc(Point(xCoord + width, yCoord + height), width/2, height/2, 270, 0); 
    drArc.draw_lines();
    sw->attach(drArc);
    Graph_lib::Arc dlArc(Point(xCoord + width/2, yCoord + height), width/2, height/2, 180, 270); 
    dlArc.draw_lines();
    sw->attach(dlArc);

    // create a box with rounded corners by creating four non-intersecting lines that will match the arcs
    // both arcs and roundedbox are parametrized in function of the width and height 
    Graph_lib::Box rb(Point(xCoord, yCoord), width, height);
    sw->attach(rb);

    sw->wait_for_button();
    delete sw;
}catch(exception& e){
    cerr << e.what() << endl;
    getchar();
}catch(...){
    cerr <<"Default exception!"<< endl;
    getchar();
}
return 0;
}

Output:

enter image description here

Suggestion:

Could I alter the definition of class Box such that includes both arcs and lines?

Note:

Until now:

  • I've (unsuccessfully) tried including an override of the function void draw_lines(): as a result I get nothing on the screen. In addition, if class Arc is included as base class and its constructor initialized via the class Box constructor list, I get ambiguities in the void draw_lines() function.
  • I've (unsuccessfully) tried passing a Simple_window w* object to the class Box and then use it to attach() Arc objects within the function void draw_lines().
  • Another idea is to define class Arcs that holds multiple Arc objects and to add them either in the body of the class Box constructor or in void draw_line().

Additional dependencies are here. The FLTK could be found here.

\$\endgroup\$
3
  • \$\begingroup\$ here is a hint: composition over inheritance \$\endgroup\$ – ratchet freak Sep 15 '15 at 13:16
  • \$\begingroup\$ @ratchet freak yes, I've tried class Arc: public Ellipse, public Arc{...};, and it is not working. I'm probably not taking into consideration some inheritance properties from the already existing facilities. \$\endgroup\$ – Ziezi Sep 15 '15 at 13:20
  • \$\begingroup\$ An arc is not an ellipse. A box (rectangle) is not a line. An arc is all points equidistant from a central point and contained in between a starting angle and an ending angle. A box (rectangle) contains a collection of four lines, which themselves can be calculated using two and only two points. \$\endgroup\$ – Casey Sep 15 '15 at 15:42
0
\$\begingroup\$

I didn't find a way to use/combine the existent facilities, thus using directly the FLTK library here is what I came with:

namespace Graph_lib{
class RoundedBox: public Shape{
public:
    RoundedBox(Point ul, Point dr);
    RoundedBox(Point ul, int w, int h);

    void draw_lines() const;
private:
    Point upperLeft;
    int width;
    int height;
    // roundedness of the box 
    // should be changed together with Arc center, major and minor axes
    int roundWidth;
    int roundHeight;
};
// Class member implementations
RoundedBox::RoundedBox(Point ul, Point dr)
    : upperLeft(ul), width(abs(ul.x - dr.x)), height(abs(ul.y - dr.y)), 
      roundWidth(abs(ul.x - dr.x) / 4), roundHeight(abs(ul.y - dr.y) / 4)  
{ add(ul); }

RoundedBox::RoundedBox(Point ul, int w, int h)
    : upperLeft(ul), width(w), height(h), roundWidth(w / 4), roundHeight(h / 4)  
{ add(ul); }

void RoundedBox::draw_lines() const{
    if(color().visibility()){ 
    // Arcs
    // upper left arc
    fl_arc(point(0).x , point(0).y, width/2, height/2, 90, 180);
    // upper right arc
    fl_arc(point(0).x + width/2, point(0).y, width/2, height/2, 0, 90);
    // down right arc
    fl_arc(point(0).x + width/2, point(0).y + height/2, width/2, height/2,
           270, 0); 
    // down left arc
    fl_arc(point(0).x, point(0).y + height/2, width/2, height/2, 180, 270); 
    // Lines
    // top horizontal
    fl_xyline(point(0).x + roundWidth, point(0).y,
              point(0).x + width - roundWidth);
    // right vertical
    fl_yxline(point(0).x + width, point(0).y + roundHeight,
              point(0).y + height - roundHeight);
    // bottom horizontal
    fl_xyline(point(0).x + roundWidth, point(0).y + height,
              point(0).x + width - roundWidth);
    // left vertical
    fl_yxline(point(0).x, point(0).y + roundHeight, 
              point(0).y + height - roundHeight);
    }
}

} // end of namespace Graph_lib 

The execution looks like this:

#include "GUI.h"
#include "Simple_window.h"
#include <iostream>
#include "Chapter13Exercise2Version2.h"
//-------------------------------------------------------------------------- 

int main(){
 // window parameters
 int winWidth = 800;
 int winHeight = 600;
 Point center((x_max() - winWidth) / 2., (y_max() - winHeight) / 2.);
 Simple_window* sw = new Simple_window(center, winWidth, winHeight,
                                       "Chapter 13 Exercise 2");

 // rounded box parameters
 int width = 400;
 int height = 200;
 int xCoord = (sw->x_max() - width) / 2;
 int yCoord = (sw->y_max() - height) / 2;

try{
    Graph_lib::RoundedBox rb(Point(xCoord, yCoord), width, height);
    sw->attach(rb);

    sw->wait_for_button();
    delete sw;
}catch(exception& e){
    cerr << e.what() << endl;
    getchar();
}catch(...){
    cerr <<"Default exception!"<< endl;
    getchar();
}
return 0;
}

And the result is:

enter image description here

\$\endgroup\$
1
  • \$\begingroup\$ You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer. \$\endgroup\$ – Toby Speight Sep 7 '18 at 7:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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