How to rotate 1D piecewise function around Y axis in Python - python

I have a 1D piecewise shape which looks like a rectangular with smooth edges (quarter circles):
import numpy as np
import matplotlib.pyplot as plt
def piecewise(x, r, d, h):
if -d/2 <= x <= - d/2 + r or d/2- r <= x <= d/2:
return np.sqrt(r**2-(x-np.sign(x)*d/2+np.sign(x)*r)**2)+r+(h-2*r)
elif -d/2 - r <= x <= -d/2 or d/2 <= x <= d/2 + r:
return -np.sqrt(r**2-(x- np.sign(x)*d/2-np.sign(x)*r)**2)+r
elif - d / 2 + r < x < d / 2 - r:
return 2*r+(h-2*r)
return 0.0
vpiecewise = np.vectorize(piecewise)
x = np.linspace(-20, 20, 10000)
z = vpiecewise(x, 4, 20, 10)
plt.plot(x, z, '-')
plt.show()
I would like to rotate it around Y-axis which is its centre to get a cyllinder with smoothed out edges. Is there a way to do it in Python?

Related

Parabolas in middle of a line segment

I am writing a code to plot roller contours. The radius of the roller may differ and the contour that I am trying to get is a concave parabola. The requirement is that I should be able to generate random roller contours with different radiuses and different parabola-like structures in between them. Below is the sample code I wrote but want it to be more generic and also want to use the random function in python for generating random contours.
Could someone please help? I need to generate n number of rollers with different radiuses and different parabolic shapes in between the line segment. Also, the parabola should not always have to be lying on the y = 0. It should be able to move left and right.
import matplotlib.pyplot as plt
import numpy as np
import random
def rec():
x = list(np.arange(-15, 16, 1))
y = [-35, 35]
y0 = []
for xv in x:
p = (xv, y[0])
y0.append(p)
y1 = []
for xv in x:
q = (xv, y[-1])
y1.append(q)
# plt.plot(x_val, y_val)
x_range = [elx[0] for elx in y0]
y_nve = [ely[1] for ely in y0]
# plt.plot(x_val, y_val)
y_pve = [ely[1] for ely in y1]
return x, x_range, y_nve, y_pve
def parabolic_ins():
x, x_range, y_nve, y_pve = rec()
nl = []
xr = []
for xp, yp in zip(x, y_pve):
if -7 <= xp <= 7:
a = 1
b = 0
c = 2
yc = yp
yp = a * (xp ** 2) # yp value should not cross 35 so that parabola doesn't shift with corner edge
if yp <= yc:
nl.append(yp)
xr.append(xp)
print(nl, xr)
else:
nl.append(yp)
xr.append(xp)
nk = [-x for x in nl]
aa = xr + xr
bb = nl + nk
plt.plot(aa, bb)
# plt.axis([x_range, nl])
plt.show()
parabolic_ins()

plot in python Piecewise function substitution

I want to plot a graph in python by substituting piecewise function.
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
#set up constants
d0=0.2
d1=0.2
L0=0.2
L=5
delta1=0.02
Dp=1
def fun(x):
out = np.zeros_like(x)
out[(x >= 0) & (x <= d1)] = d0
mask = (x >= d1) & (x <= d1 + L0)
out[mask] = d0-0.5*delta1*(1+np.cos(2*np.pi/L0)*(x[mask]-d1-0.5*L0))
out[(x >= d1 + L0) & (x <= L)] = L0
return out
# Define 100 points between 0 to 5 evenly spaced
y = np.linspace(0, 5, 100) # Change
g = Dp*(y*y-(fun(y))**2) # Change
plt.plot(y, g) # Change
plt.show() # Change
dear fun(y)is not a function of y its a function of x. i am attaching the sample graph

Blank areas while drawing Heighway Dragon in Python

So I've been doing some exercises from matura exam and there was one to draw a heighway dragon.
The program should focus on two pairs of variables:
x' = -0,4*x - 1
y' = -0,4*y + 0,1
and
x' = 0,76*x -0,4*y
y' = 0,4*x + 0,76*y
At the start x = 1 and y = 1, then, count a new x and y using the formula of randomly taken pair shown above (50/50 chance) and mark the point (x,y) on the chart. Everything repeat 5000 times.
So I tried it using python but the problem is that when I've finally drawn a dragon, at the chart I could see it was not one constant drawing but it had blank areas as in the photo below. Is it still acceptable or I made any mistake? Is there any way to make it look like the correct one?
My chart
The correct one to compare
My code:
import matplotlib.pyplot as plt
import random
x = 1
y = 1
sumx = 0
sumy = 0
max_x = 0
max_y = 0
for i in range(5000):
rand = random.randint(0, 1)
if rand == 0:
x = (-0.4 * x) - 1
y = (-0.4 * y) + 0.1
else:
x = (0.76 * x) - (0.4 * y)
y = (0.4 * x) + (0.76 * y)
if i >= 100:
sumx += x
sumy += y
plt.plot(x, y, c='black', marker='P', markersize=6)
if x > max_x:
max_x = x
if y > max_y:
max_y = y
plt.show()
avg_x = sumx / 5000
avg_y = sumy / 5000
print(round(avg_x, 1), round(avg_y, 1))
print('maximum x: ' + str(max_x) + ', maximum y: ' + str(max_y))
If the coordinates x' and y' are definitely calculated in the way you have written them above then your code is OK (although I'm not sure why you only start plotting once you've iterated 100 times).
However, calling pyplot's plot function is computationally expensive so I would suggest the following:
calculate all your x and y values and store them in lists
make one call to plt.scatter outside your for loop
this way the execution time for your code is drastically improved. I've done that in the following code, and also removed the condition that i >= 100. I also changed the way the random number was generated to see if this had an effect but it still produces a very similar output to the original code (see image below).
import matplotlib.pyplot as plt
import random
import sys
x = 1
y = 1
sumx = 0
sumy = 0
max_x = 0
max_y = 0
x_values = []
y_values = []
for i in range(5000):
rand = random.uniform(0,1)
if rand <= 0.5:
x = ((-0.4 * x) - 1)
y = ((-0.4 * y) + 0.1)
x_values.append(x)
y_values.append(y)
else:
x = ((0.76 * x) - (0.4 * y))
y = ((0.4 * x) + (0.76 * y))
x_values.append(x)
y_values.append(y)
sumx += x
sumy += y
if x > max_x:
max_x = x
if y > max_y:
max_y = y
plt.scatter(x_values, y_values, c='black', marker='P')
plt.ylim([-1, 0.4])
plt.xlim([-1.5, 0.5])
plt.show()
avg_x = sumx / 5000
avg_y = sumy / 5000
print(round(avg_x, 1), round(avg_y, 1))
print('maximum x: ' + str(max_x) + ', maximum y: ' + str(max_y))

plotting semitransparent lines / 16bit alpha values

I would like to draw a large number (about 1 million) of overlapping semitransparent lines with very small alpha values.
I tried matplotlib, but apperently it is not possible to set the alpha values below a certain value (1/256) (see https://github.com/matplotlib/matplotlib/issues/2287)
Switching to OpenGL did not help, I have the same issues (see e.g. http://www.opengl.org/discussion_boards/showthread.php/170674-Alpha-Buffer-Alpha-Bits).
Is there an easy way to draw lines with very small alpha values?
You need a line accumulator, here is the code modified from scikit-image:
import cython
import numpy as np
#cython.wraparound(False)
#cython.boundscheck(False)
def acc_lines(Py_ssize_t[:, ::1] lines, int w, int h):
cdef int[:, ::] count
cdef char steep = 0
cdef Py_ssize_t sx, sy, d, i, idx, r, c, dx, dy, x, y, x2, y2
count = np.zeros((h, w), int)
for idx in range(lines.shape[0]):
steep = 0
x = lines[idx, 0]
y = lines[idx, 1]
x2 = lines[idx, 2]
y2 = lines[idx, 3]
dx = x2 - x
dy = y2 - y
if dx < 0:
dx = -dx
if dy < 0:
dy = -dy
sx = 1 if (x2 - x) > 0 else -1
sy = 1 if (y2 - y) > 0 else -1
if dy > dx:
steep = 1
x, y = y, x
dx, dy = dy, dx
sx, sy = sy, sx
d = (2 * dy) - dx
for i in range(dx):
if steep:
r = x
c = y
else:
r = y
c = x
if 0 <= r < h and 0 <= c < w:
count[r, c] += 1
while d >= 0:
y = y + sy
d = d - (2 * dx)
x = x + sx
d = d + (2 * dy)
r = y2
c = x2
if 0 <= r < h and 0 <= c < w:
count[r, c] += 1
return count.base
Here is a demo that shows how to use it:
from matplotlib.collections import LineCollection
w = h = 512
lines = np.random.randint(0, 512, size=(10000, 4))
count = acc_lines(lines, w, h)
fig, axes = pl.subplots(1, 2, figsize=(10, 5))
lc = LineCollection(lines.reshape(-1, 2, 2), alpha=0.015, color="black")
axes[0].imshow(count, cmap="gray_r")
axes[1].add_collection(lc)
axes[1].axis((0, w, h, 0))
axes[0].set_aspect("equal")
axes[1].set_aspect("equal")
here is the output, left is the image calculated by acc_lines, right is drawed by LineCollection

Python: heat density plot in a disk

My goal is to make a density heat map plot of sphere in 2D. The plotting code below the line works when I use rectangular domains. However, I am trying to use the code for a circular domain. The radius of sphere is 1. The code I have so far is:
from pylab import *
import numpy as np
from matplotlib.colors import LightSource
from numpy.polynomial.legendre import leggauss, legval
xi = 0.0
xf = 1.0
numx = 500
yi = 0.0
yf = 1.0
numy = 500
def f(x):
if 0 <= x <= 1:
return 100
if -1 <= x <= 0:
return 0
deg = 1000
xx, w = leggauss(deg)
L = np.polynomial.legendre.legval(xx, np.identity(deg))
integral = (L * (f(x) * w)[None,:]).sum(axis = 1)
c = (np.arange(1, 500) + 0.5) * integral[1:500]
def r(x, y):
return np.sqrt(x ** 2 + y ** 2)
theta = np.arctan2(y, x)
x, y = np.linspace(0, 1, 500000)
def T(x, y):
return (sum(r(x, y) ** l * c[:,None] *
np.polynomial.legendre.legval(xx, identity(deg)) for l in range(1, 500)))
T(x, y) should equal the sum of c the coefficients times the radius as a function of x and y to the l power times the legendre polynomial where the argument is of the legendre polynomial is cos(theta).
In python: integrating a piecewise function, I learned how to use the Legendre polynomials in a summation but that method is slightly different, and for the plotting, I need a function T(x, y).
This is the plotting code.
densityinterpolation = 'bilinear'
densitycolormap = cm.jet
densityshadedflag = False
densitybarflag = True
gridflag = True
plotfilename = 'laplacesphere.eps'
x = arange(xi, xf, (xf - xi) / (numx - 1))
y = arange(yi, yf, (yf - yi) / (numy - 1))
X, Y = meshgrid(x, y)
z = T(X, Y)
if densityshadedflag:
ls = LightSource(azdeg = 120, altdeg = 65)
rgb = ls.shade(z, densitycolormap)
im = imshow(rgb, extent = [xi, xf, yi, yf], cmap = densitycolormap)
else:
im = imshow(z, extent = [xi, xf, yi, yf], cmap = densitycolormap)
im.set_interpolation(densityinterpolation)
if densitybarflag:
colorbar(im)
grid(gridflag)
show()
I made the plot in Mathematica for reference of what my end goal is
If you set the values outside of the disk domain (or whichever domain you want) to float('nan'), those points will be ignored when plotting (leaving them in white color).

Categories

Resources