Imane Bouziane
Imane Bouziane

Reputation: 11

matplotlib : How can I draw a circle with random x deformations?

I need to draw a circle that's not perfect, I mean at some points on the circle the radius needs to change (be greater or lower) in order to cause the desired deformation.

This image for instance shows a circle with 1 single deformation:

enter image description here

The number of the deformations is random and the positions also.

I am using the code below to draw a normal circle :

import numpy as np
import matplotlib.pyplot as plt
theta = np.linspace(0, 2*np.pi, 200)
radius = 0.4
a = radius * np.cos(theta)
b = radius * np.sin(theta)
figure, axes = plt.subplots(1)
axes.plot(a, b)
axes.set_aspect(1)
plt.show()

Do you have any ideas how can I achieve this?

Upvotes: 1

Views: 500

Answers (1)

JohanC
JohanC

Reputation: 80429

Making the radius depend on theta, we could have a function f such that:

  • When further than 2 steps away from an angle theta1: f(t) = 1
  • At 1 and 2 steps away f(1) = f(2) = 1
  • At theta1 there is a deformation such that f(0) = k, for some k
  • At 0 and 2, the tangent to the deformation should be zero
  • For negative t, we can use f on the absolute value

If f would be a polynomial, it could be of degree 4, so f = a*t**4 + b*t**3 + c*t**2 + d*t + e. The symbolic math library, sympy, can find suitable values for these parameters with the given constraints.

from sympy import Eq, solve
from sympy.abc import a, b, c, d, e, t, k

f = a * t ** 4 + b * t ** 3 + c * t ** 2 + d * t + e
eq1 = Eq(f.subs(t, 0), k)
eq2 = Eq(f.subs(t, 1), 1)
eq3 = Eq(f.subs(t, 2), 1)
eq4 = Eq(f.diff(t).subs(t, 0), 0)
eq5 = Eq(f.diff(t).subs(t, 2), 0)
sol = solve([eq1, eq2, eq3, eq4, eq5], (a, b, c, d, e))

This generates (after some rewriting), the following expression for f:

k + (2 * t ** 2 - 9 * t + 11) * t ** 2 * (1 - k) / 4

Now, use this function to draw the deformed circle:

import matplotlib.pyplot as plt
import numpy as np

theta = np.linspace(0, 2 * np.pi)
k = 0.8
theta1 = 80 * np.pi / 180  # a deformation at theta 80 degrees
alpha = 36 * np.pi / 180  # have a special point every 36 degrees (10 on the circle)
th = theta - theta1  # the difference between the angles, still needs to be careful to make this difference symmetrical to zero
t = np.abs(np.where(th < np.pi, th, th - 2 * np.pi)) / alpha  # use absolute value and let alpha represent a step of 1
r = np.where(t > 2, 1, k + (2 * t ** 2 - 9 * t + 11) * t ** 2 * (1 - k) / 4) # the deformed radius

plt.plot(np.cos(theta), np.sin(theta), ':r')
plt.plot(r * np.cos(theta), r * np.sin(theta), '-b')
plt.fill(r * np.cos(theta), r * np.sin(theta), color='blue', alpha=0.2)
for i in range(-5, 5):
    plt.plot(np.cos(theta1 + i * alpha), np.sin(theta1 + i * alpha), 'xk')
plt.axis('equal')
plt.show()

resulting plot

Upvotes: 4

Related Questions