#mobius.py import cmath, numpy class circle_involution: def __init__(self, center, radius): self.center = center self.radius = radius def transform(self): c = self.center r = self.radius return translation(conj(c))*self.scaling(r)*involution*self.inv_scaling(1.0/r)*self.inv_translation(-c) def of(self, z): "Apply to homogenous coordinate z." w = self.transform()*z return conj(w) def of_circle(self, center, radius): "Apply to circle involution." if radius == 0: return 0, 0 # We find the involution of center, radius c = self.center r = self.radius # We conjugate it with our own involution C = translation(conj(c))*scaling(r)*involution*scaling(1.0/r)*translation(-c) D = translation(center)*scaling(radius)*involution*scaling(1.0/radius)*translation(conj(-center)) E = translation(conj(c))*scaling(r)*involution*scaling(1.0/r)*translation(-c) image_transform = C*D*E # The result is itself an involution whose center/radius we return a = image_transform.item(0) b = image_transform.item(1) c = image_transform.item(2) d = image_transform.item(3) a /= c b /= c d /= c c = 1.0 new_center = -d new_radius = abs(b + (new_center*conj(new_center)))**.5 return new_center, new_radius def translation(t): return numpy.matrix([[1.0, t], [0.0, 1.0]]) def scaling(r): return numpy.matrix([[r, 0.0], [0.0, 1.0]]) def conj(z): return z - 2j*z.imag def normalize(z): "Normalize homogenous coordinates so last is 1.0" a = z.item(0) b = z.item(1) if b != 0.0 + 0j: return numpy.matrix([[a/b], [1.0 + 0j]]) else: return numpy.matrix([[1.0 + 0j], [0.0 + 0j]]) involution = numpy.matrix([[0.0, 1.0], [1.0, 0.0]])