PPP2 Chapter 14 Exercise 5

Pages: 12
If I derive it from Rectangle, I'll have to use Rectangle's constructor which doesn't have the stripe_width parameter (I don't need the others, I think). Unless I can also define another constructor, I think it's going to be hard with that. I'll switch to m_striped_lines.draw(). Thanks for the tip.

Edit: This is my code now:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Osman Zakir
// 6 / 4 / 2017
// Bjarne Stroutrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 14 Exercise 5
// chapter14ex5.vcxproj -> main.cpp
// Exercise Specifications:
/**
* Define a Striped_rectangle where instead of fill, the rectangle is “filled”
* by drawing one-pixel-wide horizontal lines across the inside of the rectangle
* (say, draw every second line like that). You may have to play with the
* width of lines and the line spacing to get a pattern you like.
*/

#include <iostream>
#include <stdexcept>
#include "../../Graph.h"
#include "../../Simple_window.h"

namespace Graph_lib
{
	class Striped_rectangle : public Rectangle
	{
	public:
		Striped_rectangle(const Point &xy, const int width, const int height, const int stripe_width,
			const Color &stripe_color);
		void draw_lines() const override;
		void set_stripe_color(const Color &color) { m_stripe_color = color; }
		void set_stripe_width(int width) { m_stripe_width = width; }
		const Color stripe_color() const { return m_stripe_color; }
		int stripe_width() const { return m_stripe_width; }
	private:
		Color m_stripe_color;
		Lines m_striped_lines;
		int m_stripe_width;
		int m_width;
		int m_height;
	};
}

int main()
{
	using namespace Graph_lib;
	using std::runtime_error;

	constexpr int win_x{ 100 }, win_y{ 100 };
	const Point win_tl{ win_x, win_y };
	constexpr int win_width{ 600 }, win_height{ 400 };
	Simple_window win{ win_tl, win_width, win_height, "Striped Rectangle Class" };

	try
	{
		constexpr int rect_x{ (win_width / 2) }, rect_y{ (win_height / 2) };
		const Point rect_tl{ rect_x, rect_y };
		constexpr int rect_width{ 400 }, rect_height{ 200 };
		Striped_rectangle striped_rect{ Point{rect_tl.x - rect_width / 2, 
			rect_tl.y - rect_height / 2 }, 
			rect_width, rect_height, 2, Color::blue };
		striped_rect.set_color(striped_rect.stripe_color());

		win.attach(striped_rect);
		win.wait_for_button();
	}
	catch (const runtime_error &rte)
	{
		constexpr int text_x{ (win_width / 2) - 100 }, text_y{ (win_height / 2) - 15 };
		const Point text_tl{ text_x, text_y };
		Text err_msg_start{ text_tl, "Runtime Error: " };
		Text err_msg{ Point{int((text_tl.x + err_msg_start.label().length() + 80)), 
			text_tl.y}, rte.what() };

		err_msg_start.set_color(Color::black);
		err_msg.set_color(Color::black);

		win.attach(err_msg_start);
		win.attach(err_msg);
		win.wait_for_button();
	}
}

Graph_lib::Striped_rectangle::Striped_rectangle(const Graph_lib::Point &xy, const int width, 
	const int height, const int stripe_width, const Graph_lib::Color &stripe_color)
	: m_width{ width }, m_height{ height }, m_stripe_width{ width }, 
	Rectangle{ xy, width, height }, m_stripe_color{ stripe_color }
{
	if (m_width <= 0 || m_height <= 0)
	{
		error("Bad rectangle: non-positive side");
	}
	if (m_stripe_width <= 0)
	{
		error("Invalid stripe width");
	}
	add(xy);

	m_striped_lines.set_color(m_stripe_color);
	m_striped_lines.set_style(Line_style::Line_style(
		Line_style::Line_style_type::solid, m_stripe_width)
	);
	for (int i = 1; i < m_height / m_stripe_width; ++i)
	{
		m_striped_lines.add(
			Point{ point(0).x + 1, point(0).y + i * m_stripe_width },
			Point{ point(0).x + m_width - 2, point(0).y + i * m_stripe_width }
		);
	}
}

void Graph_lib::Striped_rectangle::draw_lines() const
{
	Rectangle::draw_lines();
	m_striped_lines.draw();
}


No stripes are drawn in.

Could you please tell me if my constructor is as it should be? Striped_rectangle::draw_lines(), as well.

Edit: I got it to work. I could show the updated code, but it'd make the post larger and I also don't want to double post if I can help it.
Last edited on
Sorry, at time I have no time testing your version. But what I could say is, that you should invoke the Rectangle's constructor within the Striped_rectangle constructor. Actually I don't know how to invoke explicitly a constructor or a method of a superclass. But this concerning is interesting. Try it out. When I have the time, I will going to look how this should be correctly handled.

Or ask about this topics in a new thread. It would be nice if you post here a link to possible solutions if you got them :)
Last edited on
I was able to get it to work, though for some reason setting stripe_width to 1 makes the rectangle completely filled in with the stripe color (i.e. if stripe_color is set to blue, the whole rectangle will become blue). Setting it to higher numbers creates stripes as far apart as how big the number is.

And I can't get it to work when I call Lines::draw() instead of Lines::draw_lines in Striped_rectangle::draw_lines().

Here's the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Osman Zakir
// 6 / 4 / 2017
// Bjarne Stroutrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 14 Exercise 5
// chapter14ex5.vcxproj -> main.cpp
// Exercise Specifications:
/**
* Define a Striped_rectangle where instead of fill, the rectangle is “filled”
* by drawing one-pixel-wide horizontal lines across the inside of the rectangle
* (say, draw every second line like that). You may have to play with the
* width of lines and the line spacing to get a pattern you like.
*/

#include <iostream>
#include <stdexcept>
#include "../../Graph.h"
#include "../../Simple_window.h"

namespace Graph_lib
{
	class Striped_rectangle : public Rectangle
	{
	public:
		Striped_rectangle(const Point &xy, const int width, const int height, const int stripe_width,
			const Color &stripe_color);
		void draw_lines() const override;
		void set_stripe_color(const Color &color) { m_stripe_color = color; }
		void set_stripe_width(const int stripe_width) { m_stripe_width = stripe_width; }
		void set_width(const int width) { m_width = width; }
		void set_height(const int height) { m_height = height; }
		const Color stripe_color() const { return m_stripe_color; }
		int stripe_width() const { return m_stripe_width; }
		int width() const { return m_width; }
		int height() const { return m_height; }
	private:
		Color m_stripe_color;
		Lines m_striped_lines;
		int m_stripe_width;
		int m_width;
		int m_height;
	};
}

int main()
{
	using namespace Graph_lib;
	using std::runtime_error;

	constexpr int win_x{ 100 }, win_y{ 100 };
	const Point win_tl{ win_x, win_y };
	constexpr int win_width{ 600 }, win_height{ 400 };
	Simple_window win{ win_tl, win_width, win_height, "Striped Rectangle Class" };

	try
	{
		constexpr int rect_x{ (win_width / 2) }, rect_y{ (win_height / 2) };
		const Point rect_tl{ rect_x, rect_y };
		constexpr int rect_width{ 400 }, rect_height{ 200 };
		Striped_rectangle striped_rect{ Point{rect_tl.x - rect_width / 2, 
			rect_tl.y - rect_height / 2 }, 
			rect_width, rect_height, 2, Color::blue };
		striped_rect.set_color(striped_rect.stripe_color());

		win.attach(striped_rect);
		win.wait_for_button();
	}
	catch (const runtime_error &rte)
	{
		constexpr int text_x{ (win_width / 2) - 100 }, text_y{ (win_height / 2) - 15 };
		const Point text_tl{ text_x, text_y };
		Text err_msg_start{ text_tl, "Runtime Error: " };
		Text err_msg{ Point{int((text_tl.x + err_msg_start.label().length() + 80)), 
			text_tl.y}, rte.what() };

		err_msg_start.set_color(Color::black);
		err_msg.set_color(Color::black);

		win.attach(err_msg_start);
		win.attach(err_msg);
		win.wait_for_button();
	}
}

Graph_lib::Striped_rectangle::Striped_rectangle(const Graph_lib::Point &xy, const int width, 
	const int height, const int stripe_width, const Graph_lib::Color &stripe_color)
	: m_width{ width }, m_height{ height }, m_stripe_width{ stripe_width }, 
	Graph_lib::Rectangle::Rectangle{ xy, width, height }, m_stripe_color{ stripe_color }
{
	if (m_width <= 0 || m_height <= 0)
	{
		error("Bad rectangle: non-positive side");
	}
	if (m_stripe_width <= 0)
	{
		error("Invalid stripe width");
	}
	add(xy);

	m_striped_lines.set_color(m_stripe_color);
	m_striped_lines.set_style(Line_style(Line_style::Line_style_type::solid, m_stripe_width));
	for (int i = 1; i < m_height / m_stripe_width; ++i)
	{
		m_striped_lines.add(
			Point{ xy.x + 1, xy.y + i * m_stripe_width },
			Point{ xy.x + m_width - 2, xy.y + i * m_stripe_width }
		);
	}
}

void Graph_lib::Striped_rectangle::draw_lines() const
{
	Rectangle::draw_lines();
	m_striped_lines.draw_lines();
}
Last edited on
I took out the m_striped_lines.set_style(Line_style(Line_style::Line_style_type::solid, m_stripe_width)); line in the code and changed the Striped_rectangle::draw_lines() to have it call Lines::draw() instead of Lines::draw_lines(). Now it almost works, but I still need to know how to control the line width and line spacing. It'd be appreciated if I could get some help on that.
Here's what I did when I worked through that exercise a few months ago.

Graph.h
1
2
3
4
5
6
struct Striped_rectangle : Rectangle
{
    using Rectangle::Rectangle;

    void draw_lines() const override;
};


Graph.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void Striped_rectangle::draw_lines() const
{
    if( fill_color().visibility() ) {
        fl_color( fill_color().as_int() );

        const int stripe_width{ 5 },
            // divide by 2 as stripes alternate
            // i.e. stripe, no stripe, stripe, etc.
            num_stripes{ width() / stripe_width / 2 };
        for( int i{}; i < num_stripes; i++ ) {
            // to have an alternating stripe pattern
            if( i % 2 ) continue;
            fl_rectf( point( 0 ).x, i * stripe_width + point( 0 ).y,
                width(), stripe_width );
        }

        fl_color( color().as_int() );
    }

    if( color().visibility() ) {
        fl_color( color().as_int() );
        fl_rect( point( 0 ).x, point( 0 ).y, width(), height() );
    }
}


Source.cpp
1
2
3
Striped_rectangle strect{ { 100, 100 }, 200, 100 };
strect.set_fill_color( Color::red );
win.attach( strect );


Output (includes other exercises as well): https://puu.sh/wnWw7/212e9d11d3.png
I don't know; is it really a good idea to change Graph.h and Graph.cpp for this?

Anyway, I'll try it that way. Thanks for posting it.

By the way, I'm stuck on the example code for graphing the approximation of the exp() function, expe(), on Chapter 15. Dr. Stroustrup used a lambda function as the first argument to Function's constructor, but my compiler doesn't like me doing that. Visual Studio 2017. I tried the MSVC-v141 toolset, the Clang/C2 toolset and the LLVM-vs2014 toolset all with the same result. I want to compile it with Clang on the command-line, but I don't know the correct command-line switches to link the FLTK libraries and specify the Include Paths to it, as well as the switches for telling it where to find .h and .cpp files for Dr. Stroustrup's GUI interface library for the book. I couldn't do it at all on the normal Windows Command Prompt, so I tried it on Bash on Windows (WSL); I still couldn't do it successfully, but at least it now it seems like I just need to tell it correctly where the GUI interface library is (would be good if someone told me how to specify the Include Paths to the FLTK root directory as well, such that it can find the headers, as well as how to provide the path to the Library directory for FLTK so it can find the library object files).

What other IDE can I use on Windows 10 (with the Creator's Update installed, latest build and everything) that works well with LLVM, one that I can successfully compile and link with that code?

Note: Here's the error message from my compiler:

1>main.cpp(66,12): error : no matching constructor for initialization of 'Graph_lib::Function'
1> Function e{ [n](double x) { return expe(x, n); },
1> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1>./../../Graph.h(167,3): note: candidate constructor not viable: no known conversion from '(lambda at main.cpp:66:15)' to 'Fct *' (aka 'double (*)(double)') for 1st argument
1> Function(Fct f, double r1, double r2, Point orig, int count = 100, double xscale = 25, double yscale = 25);
1> ^
1>./../../Graph.h(165,9): note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 7 were provided
1> struct Function : Shape {
1> ^
1>90 warnings and 1 error generated.


Anyway, please help.

Edit to add: The line of code that generated the error was the first of these two lines:
1
2
Function e{ [n](double x) { return expe(x, n); },
			r_min, r_max, orig, 200, x_scale, y_scale };


Edit: Actually, I'm not sure how Dr. Stroustrup and others got that code to compile because apparently, lambda functions that use captures can't be passed as arguments to function parameters expecting a function pointer. And there's also no clean solution, it seems. Could someone help me solve this?

Edit2: I found one workaround that at least seems to be working (not sure how to check in my debugger because I'm not sure what values n is meant to have): I defined a static variable s_n and a function
1
2
3
4
5
double expe2(double x)
{
	return expe(x, n_s);
	n_s = 0;
}


In main I do this in the loop where the lambda function was originally:
1
2
3
4
5
6
7
8
9
10
11
12
for (int n = 0; n < 50; ++n)
{
	ostringstream ss;
	ss << "exp approximation; n==" << n;
	win.set_label(ss.str());
	// get next approximation:
	Function e{ expe2, r_min, r_max, orig, 200, x_scale, y_scale };
	win.attach(e);
	win.wait_for_button();
	win.detach(e);
	n_s = n;
}


Note the last line in the loop body.
Last edited on
Topic archived. No new replies allowed.
Pages: 12