Source code for conformalMaps.grids

import numpy as np
import plotly.graph_objs as go
import sympy as sym
from sympy import *
from collections import defaultdict

x, y = sym.symbols('x y', real=True)
I = i = sym.I
e = sym.exp

z = x + sym.I * y


[docs]class Rectangle: def __init__(self, left=-1, right=1, top=1, bottom=-1, fine=50, Hticks=10, Vticks=10, w=None): self.left = left self.right = right self.top = top self.bottom = bottom self.fine = fine self.Hticks = Hticks self.Vticks = Vticks self.z = x + sym.I * y self.x, self.y = sym.symbols('x y', real=True) if w is None: self.w = x + sym.I * y elif type(w) == str: self.w = self.evaluate(w) else: self.w = w self.w_sympy = self.w self.init_plotly(self.left, self.right, self.top, self.bottom, self.fine, self.Hticks, self.Vticks, self.w)
[docs] def init_plotly(self, left, right, top, bottom, fine, Hticks, Vticks, w): self.transformed = self.matrix_generator(w=w, left=left, right=right, top=top, bottom=bottom, fine=fine, Hticks=Hticks, Vticks=Vticks) self.traces = self.create_traces(self.transformed) self.fig = go.FigureWidget(data=self.traces) self.fig = self.fig.update_layout( template='plotly_dark', yaxis=dict(scaleanchor="x", scaleratio=1), autosize=False, width=900, height=500, showlegend=False, dragmode='pan')
[docs] def matrix_generator(self, left, right, top, bottom, fine, Hticks, Vticks, w): self.X, self.Y = self.gen_coordinate_grid(left, right, top, bottom, fine, Hticks, Vticks) self.w = self.w_numeric(w) return self.plugin(w=self.w, X=self.X, Y=self.Y)
[docs] def gen_coordinate_grid(self, left=None, right=None, top=None, bottom=None, fine=None, Hticks=None, Vticks=None): if left is None: left = self.left else: self.left = left if right is None: right = self.right else: self.right = right if top is None: top = self.top else: self.top = top if bottom is None: bottom = self.bottom else: self.bottom = bottom if fine is None: fine = self.fine else: self.fine = fine if Hticks is None: Hticks = self.Hticks else: self.Hticks = Hticks if Vticks is None: Vticks = self.Vticks else: self.Vticks = Vticks Hx, Hy = np.meshgrid(np.linspace(self.left, self.right, self.fine, dtype=complex), np.linspace(self.bottom, self.top, self.Hticks, dtype=complex)) Vx, Vy = np.meshgrid(np.linspace(self.left, self.right, self.Vticks, dtype=complex), np.linspace(self.bottom, self.top, self.fine, dtype=complex)) X = [] Y = [] X.extend(Hx) X.extend(np.transpose(Vx)) Y.extend(Hy) Y.extend(np.transpose(Vy)) return X, Y
[docs] def w_numeric(self, w): return sym.lambdify((x, y), w, "numpy")
[docs] def plugin(self, w, X, Y): matrix = X.copy() # same shape as X or Y for j in range(len(X)): for k in range(len(X[j])): matrix[j][k] = w(X[j][k], Y[j][k]) matrix[j] = np.array(matrix[j]) # returns list of numpy arrays return matrix
[docs] def create_traces(self, transformed_mat): traces = [] for count, arr in enumerate(transformed_mat): if count == 0: color = 'blue' elif 0 < count < self.Hticks - 1: color = 'grey' elif count == self.Hticks - 1: color = 'red' elif count == self.Hticks: color = 'orange' elif self.Hticks < count < self.Vticks + self.Hticks - 1: color = 'green' elif count == self.Hticks + self.Vticks - 1: color = 'magenta' traces.append(go.Scatter(x=arr.real, y=arr.imag, line_color=color, hoverinfo='none', line=dict(shape='spline'), mode='lines')) return traces
[docs] def over_write_traces(self, transformed_mat): with self.fig.batch_update(): for count, tmp in enumerate(transformed_mat): self.fig.data[count].x = tmp.real self.fig.data[count].y = tmp.imag
[docs] def add_new_data_to_traces(self, transformed_mat): with self.fig.batch_update(): self.fig.data = [] self.traces = self.create_traces(transformed_mat) self.fig.add_traces(self.traces)
[docs] def anim(self, w, left, right, top, bottom, fine, Hticks, Vticks, frame, scale=100): return self.matrix_generator(w=(w - z) * (frame / scale) + z, left=left, right=right, top=top, bottom=bottom, fine=fine, Hticks=Hticks, Vticks=Vticks)
[docs] def check_analytic(self, w = None, silent = False): if w is None: f = self.w_sympy elif type(w) == str: f = self.w_sympy = self.evaluate(w) else: f = self.w_sympy reps = defaultdict(lambda:sym.Dummy(real=True)) f = f.replace(lambda x: x.is_Float, lambda x: reps[x]) u = sym.re(f) v = sym.im(f) cond1 = sym.diff(u, x) - sym.diff(v, y) cond2 = sym.diff(u, y) + sym.diff(v, x) if sym.simplify(cond1) == 0 and sym.simplify(cond2) == 0: if silent is False: print('The function is conformal, angles are preserved :)') out = True else: if silent is False: print('The function is not conformal, angles are not preserved ...') out = False return out
[docs] def evaluate(self, w): try: evaluated = eval(w) self.w_sympy = evaluated return evaluated except: # tmp = exc print("CHECK FUNCTION w AGAIN, USING PREVIOUS ENTERED w") self.w_sympy = self.w_sympy return self.w
[docs] def updateFunc(self, w=None, left=None, right=None, top=None, bottom=None, fine=None, Hticks=None, Vticks=None, frame=None, scale=100): if w is None: w = self.w elif type(w) == str: w = self.evaluate(w) if left is None: left = self.left if right is None: right = self.right if top is None: top = self.top if bottom is None: bottom = self.bottom if fine is None: fine = self.fine if Hticks is None: Hticks = self.Hticks if Vticks is None: Vticks = self.Vticks if type(w) == str: w = self.evaluate(w) if frame is None: self.frame = frame = 1 self.same_args_for_w = True if self.right != right or self.left != left or self.top != top or self.bottom != bottom or self.fine != fine or self.Hticks != Hticks or self.Vticks != Vticks: self.same_args_for_w = False self.right = right self.left = left self.top = top self.bottom = bottom self.fine = fine self.Hticks = Hticks self.Vticks = Vticks if self.same_args_for_w: self.transformed = self.anim(w=w, left=left, right=right, top=top, bottom=bottom, fine=fine, Hticks=Hticks, Vticks=Vticks, frame=frame, scale=scale) self.over_write_traces(self.transformed) else: self.transformed = self.anim(w=w, left=left, right=right, top=top, bottom=bottom, fine=fine, Hticks=Hticks, Vticks=Vticks, frame=frame, scale=scale) self.add_new_data_to_traces(self.transformed)
[docs] def show(self): return self.fig
[docs]class Square(Rectangle): def __init__(self, side=1, fine=50, Hticks=10, Vticks=10, w=None): super().__init__(left=-side, right=side, top=side, bottom=-side, fine=fine, Hticks=Hticks, Vticks=Vticks, w=w)
[docs] def updateFunc(self, w=None, side=None, fine=None, Hticks=None, Vticks=None, frame=None, scale=100): if side is None: super().updateFunc(left=side, right=side, top=side, bottom=side, fine=fine, Hticks=Hticks, Vticks=Vticks, w=w, frame=frame, scale=scale) else: super().updateFunc(left=-side, right=side, top=side, bottom=-side, fine=fine, Hticks=Hticks, Vticks=Vticks, w=w, frame=frame, scale=scale)
[docs]class Donut(Rectangle): def __init__(self, w=None, rin=1, rout=2, fine=50, cticks=5, rticks=10, x0=0, y0=0): self.rin = rin self.rout = rout self.fine = fine self.cticks = cticks self.rticks = rticks self.x0 = x0 self.y0 = y0 self.x, self.y = sym.symbols('x y', real=True) if w is None: self.w = x + sym.I * y elif type(w) == str: self.w = self.evaluate(w) else: self.w = w self.w_sympy = self.w self.init_plotly(w=self.w, rin=self.rin, rout=self.rout, fine=self.fine, cticks=self.cticks, rticks=self.rticks, x0=self.x0, y0=self.y0)
[docs] def init_plotly(self, w, rin, rout, fine, cticks, rticks, x0, y0): self.transformed = self.matrix_generator(w=w, rin=rin, rout=rout, fine=fine, cticks=cticks, rticks=rticks, x0=x0, y0=y0) self.traces = self.create_traces(self.transformed) self.fig = go.FigureWidget(data=self.traces) self.fig = self.fig.update_layout( template='plotly_dark', yaxis=dict(scaleanchor="x", scaleratio=1), autosize=False, width=900, height=500, showlegend=False, dragmode='pan')
[docs] def matrix_generator(self, w, rin, rout, fine, cticks, rticks, x0, y0): self.X, self.Y = self.gen_coordinate_grid(rin=rin, rout=rout, fine=fine, cticks=cticks, rticks=rticks, x0=x0, y0=y0) self.w = self.w_numeric(w) return self.plugin(w=self.w, X=self.X, Y=self.Y)
[docs] def gen_coordinate_grid(self, rin=None, rout=None, fine=None, cticks=None, rticks=None, x0=None, y0=None): if rin is None: rin = self.rin else: self.rin = rin if rout is None: rout = self.rout else: self.rout = rout if fine is None: fine = self.fine else: self.fine = fine if cticks is None: cticks = self.cticks else: self.cticks = cticks if rticks is None: rticks = self.rticks else: self.rticks = rticks if x0 is None: x0 = self.x0 else: self.x0 = x0 if y0 is None: y0 = self.y0 else: self.y0 = y0 t = np.linspace(0, 2 * np.pi, fine, dtype=complex) r = np.linspace(rin, rout, cticks, dtype=complex) # circular X and Y coordinates Cx = [x0 + tmp * np.cos(t) for tmp in r] Cy = [y0 + tmp * np.sin(t) for tmp in r] # Radial lines from smaller circle to larger circle t = np.linspace(0, 2 * np.pi, rticks, dtype=complex, endpoint=False) cinx = x0 + rin * np.cos(t) ciny = y0 + rin * np.sin(t) coutx = x0 + rout * np.cos(t) couty = y0 + rout * np.sin(t) Rx = [np.linspace(tmp1, tmp2, fine, dtype=complex) for tmp1, tmp2 in zip(cinx, coutx)] Ry = [np.linspace(tmp1, tmp2, fine, dtype=complex) for tmp1, tmp2 in zip(ciny, couty)] X = [] Y = [] X.extend(Cx) X.extend(Rx) Y.extend(Cy) Y.extend(Ry) return X, Y
[docs] def plugin(self, w, X, Y): matrix = X.copy() # same shape as X or Y for j in range(len(X)): for k in range(len(X[j])): matrix[j][k] = w(X[j][k], Y[j][k]) matrix[j] = np.array(matrix[j]) return matrix
[docs] def create_traces(self, transformed_mat): traces = [] for count, arr in enumerate(transformed_mat): if count == 0: color = 'magenta' elif 0 < count < self.cticks - 1: color = 'green' elif count == self.cticks - 1: color = 'blue' elif count > self.cticks - 1: color = 'grey' traces.append(go.Scatter(x=arr.real, y=arr.imag, line_color=color, hoverinfo='none', line=dict(shape='spline'), mode='lines')) return traces
[docs] def anim(self, w, rin, rout, cticks, rticks, x0, y0, fine, frame, scale=100): return self.matrix_generator(w=(w - (x + I * y)) * (frame / scale) + z, x0=x0, y0=y0, rin=rin, rout=rout, fine=fine, cticks=cticks, rticks=rticks)
# return self.matrix_generator(w=(w - z) * (frame / scale) + z, x0=x0, y0=y0, rin=rin, rout=rout, fine=fine, # cticks=cticks, rticks=rticks)
[docs] def updateFunc(self, w=None, rin=None, rout=None, fine=None, cticks=None, rticks=None, x0=None, y0=None, frame=None, scale=100): if rin is None: rin = self.rin if rout is None: rout = self.rout if fine is None: fine = self.fine if cticks is None: cticks = self.cticks if rticks is None: rticks = self.rticks if x0 is None: x0 = self.x0 if y0 is None: y0 = self.y0 if w is None: w = self.w elif type(w) == str: w = self.evaluate(w) if frame is None: self.frame = frame = 1 self.same_args_for_w = True if self.x0 != x0 or self.y0 != y0 or self.rticks != rticks or self.cticks != cticks or self.rin != rin or self.rout != rout or self.fine != fine: self.same_args_for_w = False self.rin = rin self.rout = rout self.fine = fine self.cticks = cticks self.rticks = rticks self.x0 = x0 self.y0 = y0 if self.same_args_for_w: self.transformed = self.anim(w=w, rin=rin, rout=rout, cticks=cticks, rticks=rticks, x0=x0, y0=y0, fine=fine, frame=frame) self.over_write_traces(self.transformed) else: self.transformed = self.anim(w=w, rin=rin, rout=rout, cticks=cticks, rticks=rticks, x0=x0, y0=y0, fine=fine, frame=frame) self.add_new_data_to_traces(self.transformed)
[docs]class Circle(Donut): def __init__(self, w=None, r=1, fine=50, cticks=5, rticks=10, x0=0, y0=0): super().__init__(w=w, rin=0, rout=r, fine=fine, cticks=cticks + 1, rticks=rticks, x0=x0, y0=y0)
[docs] def updateFunc(self, w=None, r=None, fine=None, cticks=None, rticks=None, x0=None, y0=None, frame=None, scale=100): if cticks is None: super().updateFunc(w=w, rin=0., rout=r, fine=fine, cticks=cticks, rticks=rticks, x0=x0, y0=y0, frame=frame, scale=scale) else: super().updateFunc(w=w, rin=0., rout=r, fine=fine, cticks=cticks + 1, rticks=rticks, x0=x0, y0=y0, frame=frame, scale=scale)
[docs]class Single_circle(Circle): def __init__(self, w=None, r=1, fine=50, rticks=0, x0=0, y0=0): super().__init__(w=w, r=r, fine=fine, cticks=1, rticks=rticks, x0=x0, y0=y0)
[docs] def updateFunc(self, w=None, r=None, rticks=None, x0=None, y0=None, frame=None): super().updateFunc(w=w, r=r, rticks=rticks, x0=x0, y0=y0, frame=frame, scale=100)