Maurer Roses

(1 comment)

A Maurer rose is a shape formed by connecting the points on a rose defined in the polar coordinate system by $r = \sin(n\theta)$ for some positive integer, $n$. The points to join are, in polar coordinates using degrees:

$$ (r, \theta) = (\sin(nk), k)\;\mathrm{for}\;k=0, d, 2d, \ldots, 360d $$

For example, the Maurer rose defined by $n=4, d=120$ is:

Maurer rose for n=4, d=120

The following Python code plots a Maurer rose for parameters $n$ and $d$ provided on the command line, for example:

$ python maurerrose.py 4 120

the code requires NumPy and Matplotlib.

import sys
import numpy as np
import matplotlib.pyplot as plt

"""Plot a "Maurer Rose" with (integer) parameters n, d."""

def get_rose_xy(n, d):
    """Get the Cartesian coordinates for points on the rose."""

    # The rose is (r = sin(nk), k) in polar coordinates, for
    # k = d, 2d, 3d, ..., 360d.
    # Add a final point at 361d to close the curve when plotted.
    k = d * np.linspace(0, 361, 361)
    r = np.sin(np.radians(n * k))
    x = r * np.cos(np.radians(k))
    y = r * np.sin(np.radians(k))
    return x, y

def draw_rose(ax, n, d, c='r'):
    """Draw the Maurer rose defined by (n, d) in colour c."""

    x, y = get_rose_xy(n, d)
    ax.plot(x, y, c=c, lw=0.5)
    ax.axis('equal')
    ax.axis('off')

if __name__ == '__main__':
    n, d = int(sys.argv[1]), int(sys.argv[2])
    fig, ax = plt.subplots()
    draw_rose(ax, n, d, 'tab:orange')
    plt.show()

The following code uses the get_rose_xy function to create an animation showing how the geometry of a Maurer Rose changes with the parameter, $d$:

import sys
from functools import partial
import matplotlib.pyplot as plt
from matplotlib import animation
from maurerrose import get_rose_xy

"""Animate a Maurer rose with a given n for increasing d."""

# Colours will change according to this colormap as d advances.
cmap = plt.get_cmap('hsv')

# Read n from the command line, initialize d to 1.
n, d = int(sys.argv[1]), 1

# New Figure with a single Axes and a black background.
fig, ax = plt.subplots(facecolor='k')

# Initial plot.
x, y = get_rose_xy(n, d)
line, = ax.plot(x, y, c='r', lw=0.5)
# Annotate with the values of n and d.
ax.text(0.8, 0.9, f'n = {n}', ha='left', c='w')
text = ax.text(0.8, 0.8, f'd = {d}', ha='left', c='w')
# Make the Axes square and turn off tick marks, labels and spines.
ax.axis('equal')
ax.axis('off')

def init_animation():
    """Initialize animation: line and text are handed around at each frame."""
    return line, text

def animate(i):
    """Advance the animation by one frame, increasing d by 1."""

    global d
    d += 1
    x, y = get_rose_xy(n, d)
    line.set_data(x, y)
    # Also advance the colour, mapping i to [0,1]:
    c = cmap(i / 360)
    line.set_color(c)
    # Update the text label indicating the value of d.
    text.set_text(f'd = {d}')
    return line, text

frames = 360
interval = 50
#
anim = animation.FuncAnimation(fig, animate,
                    init_func=init_animation, frames=frames,
                    interval=interval)

# If we're saving the animation as a video, uncomment these two lines.
#writer = animation.FFMpegWriter(fps=20)
#anim.save('maurer-rose.mp4', writer=writer)

# If we're just viewing the animation locally, uncomment this line.
plt.show()
Current rating: 5

Comments

Comments are pre-moderated. Please be patient and your comment will appear soon.

djeffal khaled 1 month, 3 weeks ago

thanks

Link | Reply
Currently unrated

New Comment

required

required (not published)

optional

required