Help drawing a Superellipse C++ GUI

Pages: 123

What should m be assigned to?

Usually, the higher the values of m or n, the more rectangular it becomes. Try a smaller value, say 3 for m.
Well, now it looks like a D (I changed m's value to 3.0).

Screenshot: https://1drv.ms/i/s!As6LkLqTe7Ps73IJFU0BFaBm-jl2
That's because I forgot to account for the sign of sin/cos. Using lastchance's code fixes this.
lastchance wrote:

x = a * (abs(cos theta))2/m * sign(cos theta)
y = b * (abs(sin theta))2/n * sign(sin theta)
1
2
3
4
5
6
7
8
Point superellipse( double deg, double a, double b, double m, double n )
{
	deg *= M_PI / 180.0;
	return Point(
		a * pow( abs( sin( deg ) ), 2 / m ) * (sin( deg ) > 0 ? 1 : -1),
		b * pow( abs( cos( deg ) ), 2 / n ) * (cos( deg ) > 0 ? 1 : -1)
	);
}


edit: fixed code
Last edited on
Thanks. I'll fix it on my end.

Edit: Done. I guess now I need to draw the star-like pattern and the other 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
// Osman Zakir
// 3 / 29 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 12 Exercise 12
// Exercise Specifications:
/**
 * A superellipse is a two-dimensional shape defined by the equation
 *		pow(abs(x/a), m) + pow(abs(y/b), n) = 1, m,n > 0

 * Look up superellipse on the web to get a better idea of what such shapes
 * look like. Write a program that draws “starlike” patterns by connecting
 * points on a superellipse. Take a , b , m , n , and N as arguments. Select N
 * points on the superellipse defined by a , b , m , and n . Make the points
 * equally spaced for some definition of “equal.” Connect each of those N
 * points to one or more other points (if you like you can make the number
 * of points to which to connect a point another argument or just use N – 1 ,
 * i.e., all the other points).
 */

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

Graph_lib::Point superellipse(double deg, const double a, 
	const double b, const double m, const double n);

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

	Point tl{ 100, 100 };
	constexpr int win_width = 800;
	constexpr int win_height = 600;
	Simple_window win{ tl, win_width, win_height, "Super-ellipse" };

	try
	{
		constexpr double a = 100.0;
		constexpr double b = 100.0;
		constexpr double m = 3.0;
		constexpr double n = 3.0;
		constexpr int revolution = 360;
		Graph_lib::Point center{ win_width / 2, win_height / 2 };
		
		Open_polyline openpl;

		for (int d = 0; d < revolution; d++)
		{
			openpl.add(superellipse(d, a, b, m, n) += center);
		}
		openpl.set_color(Color::blue);
		openpl.set_style({ Line_style::solid, 2 });
		win.attach(openpl);
		win.wait_for_button();
	}
	catch (const runtime_error &e)
	{
		Text err_msg_start{ Point{300, 600}, "Runtime_error: " };
		Text err_msg{ Point{400, 600}, e.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::Point superellipse(double deg, const double a,
	const double b, const double m, const double n)
{
	constexpr double pi = 3.14159265359;
	deg *= pi / 180.0;
	return Graph_lib::Point{
		int(a * pow(abs(sin(deg)), 2 / m) * (sin(deg) > 0 ? 1 : -1)),
		int(b * pow(abs(cos(deg)), 2 / n) * (cos(deg) > 0 ? 1 : -1))
	};
}


Output screenshot: https://1drv.ms/i/s!As6LkLqTe7Ps73OrqBU6hXTypGhI
Last edited on
Looks like the last point isn't connected with the first point.
You can either:
- use a Closed_polyline
- change the for loop to be for (int d = 0; d <= revolution; d++)

Edit: Done. I guess now I need to draw the star-like pattern and the other lines. Here's the code:

Just generate N random angles [0, 360], pass that to superellipse() with the same arguments as you used to draw your superellipse and you'll get N points, which you can connect.
Do I make another loop and maybe use rand() with the max value set to N? Do I use a Point type? How do I set the angles' degree values?

Edit: I'm thinking of using N as the max value for the random number generator and using a loop to go from 0 to 360 degrees. Maybe the same loop I already have? Or should I make a nested loop? And I'll have to include some kind of calculation inside the superellipse() function to have it return N points to connect on the super-ellipse, right? I'd appreciate it if someone could tell me about that as well.
Last edited on
I'm not really sure of how to get an N-sided star, but here's one for N = 5.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
constexpr int N{ 5 }, spacing{ revolution / N };
int rand_theta{ randint( revolution ) };
Closed_polyline star{};
star.add(
    superellipse( rand_theta + 0 * spacing, a, b, m, n ) += center
);
star.add(
    superellipse( rand_theta + 2 * spacing, a, b, m, n ) += center
);
star.add(
    superellipse( rand_theta + 4 * spacing, a, b, m, n ) += center
);
star.add(
    superellipse( rand_theta + 1 * spacing, a, b, m, n ) += center
);
star.add(
    superellipse( rand_theta + 3 * spacing, a, b, m, n ) += center
);
win.attach( star );


You can convert the repetitive code to a for loop like so:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
for( int i{}; i < N; i += 2 ) {
    star.add(
        superellipse( rand_theta + i * spacing, a, b, m, n ) += center
    );
}
for( int i{ 1 }; i < N; i += 2 ) {
    star.add(
        superellipse( rand_theta + i * spacing, a, b, m, n ) += center
    );
}

// converted the above to a nested for loop
for( int i{}; i < 2; i++ ) {
    for( int j{ i }; j < N; j += 2 ) {
        star.add(
            superellipse( rand_theta + j * spacing, a, b, m, n ) += center
        );
    }
}
Last edited on
Should I put that loop inside the one I have in main?

Should I put that loop inside the one I have in main?

You could but it wouldn't really make sense to, as the loop you have in main draws the superellipse, while the above loop draws a star inside the superellipse. I think it would be more logical to separate these notions.
My compiler for some reason doesn't have the "experimental/random" header.

Instead, I'm trying to do it like this:
1
2
3
4
5
6
7
8
9
constexpr int N = 5;
constexpr int spacing = revolution / N;
default_random_engine generator;
uniform_int_distribution<int> distribution{ revolution };
Closed_polyline star;
for (int i = 0; i < N; i++)
{
	star.add(superellipse(distribution + i * spacing, a, b, m, n));
}


But I don't know how to make it work. I get the error

no operator "+" matches these operands
operand types are: std::uniform_int_distribution<int> + int


Of course I've #include d the "random" header.

I noticed that the distribution object has some member functions and stuff I can use, like a(). But I don't know anything about them.

Edit: In the meantime, I've tried doing this:
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
// Osman Zakir
// 3 / 29 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 12 Exercise 12
// Exercise Specifications:
/**
 * A superellipse is a two-dimensional shape defined by the equation
 *		pow(abs(x/a), m) + pow(abs(y/b), n) = 1, m,n > 0

 * Look up superellipse on the web to get a better idea of what such shapes
 * look like. Write a program that draws “starlike” patterns by connecting
 * points on a superellipse. Take a , b , m , n , and N as arguments. Select N
 * points on the superellipse defined by a , b , m , and n . Make the points
 * equally spaced for some definition of “equal.” Connect each of those N
 * points to one or more other points (if you like you can make the number
 * of points to which to connect a point another argument or just use N – 1 ,
 * i.e., all the other points).
 */

#include "../../Simple_window.h"
#include "../../Graph.h"
#include <random>
#include <stdexcept>
#include <cstdlib>
#include <cmath>

Graph_lib::Point superellipse(double deg, const double a, 
	const double b, const double m, const double n);

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

	Point tl{ 100, 100 };
	constexpr int win_width = 800;
	constexpr int win_height = 600;
	Simple_window win{ tl, win_width, win_height, "Super-ellipse" };

	try
	{
		constexpr double a = 100.0;
		constexpr double b = 100.0;
		constexpr double m = 3.0;
		constexpr double n = 3.0;
		constexpr int revolution = 360;
		Graph_lib::Point center{ win_width / 2, win_height / 2 };
		
		Open_polyline openpl;

		for (int d = 0; d <= revolution; d++)
		{
			openpl.add(superellipse(d, a, b, m, n) += center);
		}

		constexpr int N = 5;
		constexpr int spacing = revolution / N;
		default_random_engine generator;
		uniform_int_distribution<int> distribution{ revolution };
		Closed_polyline star;
		for (int i = 0; i < N; i++)
		{
			star.add(superellipse(distribution(generator) + i * spacing, a, b, m, n));
		}

		openpl.set_color(Color::blue);
		openpl.set_style({ Line_style::solid, 2 });
		star.set_color(Color::red);
		star.set_style({ Line_style::solid, 2 });
		win.attach(openpl);
		win.attach(star);
		win.wait_for_button();
	}
	catch (const runtime_error &e)
	{
		Text err_msg_start{ Point{300, 600}, "Runtime_error: " };
		Text err_msg{ Point{400, 600}, e.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::Point superellipse(double deg, const double a,
	const double b, const double m, const double n)
{
	constexpr double pi = 3.14159265359;
	deg *= pi / 180.0;
	return Graph_lib::Point{
		int(a * pow(abs(sin(deg)), 2 / m) * (sin(deg) > 0 ? 1 : -1)),
		int(b * pow(abs(cos(deg)), 2 / n) * (cos(deg) > 0 ? 1 : -1))
	};
}


But I don't get any star-like pattern drawn in there at all.
Last edited on
But I don't get any star-like pattern drawn in there at all.
1
2
3
4
for (int i = 0; i < N; i++)
{
    star.add(superellipse(distribution(generator) + i * spacing, a, b, m, n));
}

Because you're generating a random angle every iteration, which will just end up being some distorted shape with N sides.

Also, randint() is defined in the std_lib_facilities header, so no need to manually setup your own RNG.
I'll just copy-paste the definition of randint() from that header, then. I really don't want to use that header anymore. Thanks for the tip, by the way.

Edit: It gave me a regular 5-pointed star, rather than the star-like pattern that should be in a super-ellipse. https://1drv.ms/i/s!As6LkLqTe7Ps73nVfuTwebF-dAUf

How do I get those curved lines that I need to get on this?
Last edited on
Edit: It gave me a regular 5-pointed star

Isn't that what you're meant to do?
Last edited on
No. Look at the image on the link you posted with those equations. Do the curves inside the super-ellipse create anything like a regular 5-pointed star?

Look at Figure 1 on this page and the curves and stuff inside the super-ellipse here: http://fractional-calculus.com/super_ellipse.pdf
Last edited on

No. Look at the image on the link you posted with those equations. Do the curves inside the super-ellipse create anything like a regular 5-pointed star?

Look at Figure 1 on this page and the curves and stuff inside the super-ellipse here: http://fractional-calculus.com/super_ellipse.pdf

The green and cyan curves are created when n = 1 and n = 2/3, respectively. I don't think they are the star-like patterns Stroustrup refers to.


Write a program that draws “starlike” patterns by connecting
points on a superellipse. Take a , b , m , n , and N as arguments. Select N
points on the superellipse defined by a , b , m , and n . Make the points
equally spaced for some definition of “equal.”

The way I interpret this is, that you have to create a regular N-sided star within the superellipse. The code I provided creates a 5-sided star, but I'm not sure how to extend it to an N-sided star.
I think he wants us to make the patterns shown in the illustration. He didn't say to "make a star". He said to "make star-LIKE patterns". Isn't there a difference?
Isn't there someone who can help me with that? I want to know how to draw the shapes and lines that are inside the super-ellipse on that page. Anyone?
I'll ask there, then. Thanks.

Edit: Okay, thanks to a poster on Dream In Code, I figured out part of this exercise. Stroustrup is asking to draw the star-shaped super-ellipse, the one with the curves caving in towards the inside of the super-ellipse. The values of m and n have to both be 0.5 (each, individually). I just don't get what he means by choosing N points to connect on the super-ellipse, though. I mean, if the super-ellipse is already drawn, what points are there to connect?

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
// Osman Zakir
// 3 / 29 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 12 Exercise 12
// Exercise Specifications:
/**
 * A superellipse is a two-dimensional shape defined by the equation
 *		pow(abs(x/a), m) + pow(abs(y/b), n) = 1, m,n > 0

 * Look up superellipse on the web to get a better idea of what such shapes
 * look like. Write a program that draws “starlike” patterns by connecting
 * points on a superellipse. Take a , b , m , n , and N as arguments. Select N
 * points on the superellipse defined by a , b , m , and n . Make the points
 * equally spaced for some definition of “equal.” Connect each of those N
 * points to one or more other points (if you like you can make the number
 * of points to which to connect a point another argument or just use N – 1 ,
 * i.e., all the other points).
 */

#include "../../Simple_window.h"
#include "../../Graph.h"
#include <random>
#include <stdexcept>
#include <cstdlib>
#include <cmath>

Graph_lib::Point superellipse(double deg, const double a, 
	const double b, const double m, const double n);

inline int randint(int min, int max);

inline int randint(int max);

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

	Point tl{ 100, 100 };
	constexpr int win_width = 800;
	constexpr int win_height = 600;
	Simple_window win{ tl, win_width, win_height, "Super-ellipse" };

	try
	{
		constexpr double a = 100.0;
		constexpr double b = 100.0;
		constexpr double m = 0.5;
		constexpr double n = 0.5;
		constexpr int revolution = 360;
		Graph_lib::Point center{ win_width / 2, win_height / 2 };
		
		Open_polyline openpl;

		for (int d = 0; d <= revolution; d++)
		{
			openpl.add(superellipse(d, a, b, m, n) += center);
		}

		constexpr int N = 5;
		constexpr int spacing = revolution / N;
		int rand_theta = randint(revolution);
		Closed_polyline star;
		for (int i = 0; i < 2; i++) 
		{
			for (int j = i; j < N; j += 2) 
			{
				star.add(superellipse(rand_theta + j * spacing, a, b, m, n) += center);
			}
		}

		openpl.set_color(Color::blue);
		openpl.set_style({ Line_style::solid, 2 });
		star.set_color(Color::red);
		star.set_style({ Line_style::solid, 2 });
		win.attach(openpl);
		win.attach(star);
		win.wait_for_button();
	}
	catch (const runtime_error &e)
	{
		Text err_msg_start{ Point{300, 600}, "Runtime_error: " };
		Text err_msg{ Point{400, 600}, e.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::Point superellipse(double deg, const double a,
	const double b, const double m, const double n)
{
	constexpr double pi = 3.14159265359;
	deg *= pi / 180.0;
	return Graph_lib::Point{
		int(a * pow(abs(sin(deg)), 2 / m) * (sin(deg) > 0 ? 1 : -1)),
		int(b * pow(abs(cos(deg)), 2 / n) * (cos(deg) > 0 ? 1 : -1))
	};
}

inline int randint(int min, int max) 
{ 
	static std::default_random_engine ran; 
	return std::uniform_int_distribution<>{min, max}(ran); 
}

inline int randint(int max) 
{ 
	return randint(0, max); 
}


Output screenshot: https://1drv.ms/i/s!As6LkLqTe7Ps73vauLPxbnWtpCWT
Last edited on
Topic archived. No new replies allowed.
Pages: 123