turn some text into a mobius surfaceยค
a good reference was: https://algebra-fun.gitee.io/blog/2020/06/16/Joy-%E7%BB%98%E5%88%B6Mobius/
if __import__("sys").platform == "emscripten":
await __import__("micropip").install("pandas matplotlib ipympl".split())
import pandas, matplotlib, io, numpy
from mpl_toolkits.mplot3d import Axes3D
%matplotlib agg
get_text
transforms some text into a dataframe that will allow us to plot a mobius strip.
def get_text(text="deathbeds", repeat=2):
return get_text_array(
get_text_figure(text, repeat)
).pipe(get_parameterized_text).pipe(get_xyz)
get_text_figure
transforms text to pixels.
def get_text_figure(text="deathbeds", repeat=1):
matplotlib.pyplot.gca().text(0, 0, text*repeat, size=1000)
matplotlib.pyplot.gca().axis("off")
fig = matplotlib.pyplot.gcf()
return fig
get_text_array
structure those pixels as dataframe
def get_text_array(fig):
data= io.BytesIO()
fig.savefig(data, bbox_inches="tight")
data.seek(0)
where = numpy.where(matplotlib.pyplot.imread(data).sum(2)[::-4, ::20] < 2)
return pandas.concat(
[pandas.Series(where[0], name="x"), pandas.Series(where[1], name="y")], axis=1
).set_index("x")
get_parameterized_text
parameterizes x onto 0..1 and s one 0..2ฯ
def get_parameterized_text(df):
extent = df.index.max() - df.index.min()
df["t"] = df.index.to_series()
df.t = df.t.sub(df.t.min()).div(extent).sub(.5)
extent = df.y.max() - df.y.min()
df["s"] = df.y.add(df.y.min()).div(extent).mul(2*numpy.pi)
return df
get_xyz
moves the parameterization in xyz space.
def get_xyz(df, R=100, W=50):
return df.assign(
x=(R+W*df.t*df.s.div(2).apply(numpy.cos))*df.s.apply(numpy.cos),
y=(R+W*df.t*df.s.div(2).apply(numpy.cos))*df.s.apply(numpy.sin),
z=W*df.t.mul(df.s.div(2).apply(numpy.sin)),
)
get_plot
takes our structured data and plots in 3d.
def get_plot(df):
fig = matplotlib.pyplot.gcf()
ax = Axes3D(fig, auto_add_to_figure=False)
fig.add_axes(ax)
ax.scatter3D(df.x, df.y, df.z, c=[(.1, .1, .1, .015)], marker="v", s=200, edgecolor=None)
return fig
use the interactive 3-d dispklay
%matplotlib ipympl
plot it all
matplotlib.pyplot.gcf().set_size_inches((6, 6))
get_text("deathbeds").pipe(get_plot)