This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.

#### 195 lines 6.3 KiB Python Raw Permalink Normal View History

 2021-07-23 02:36:56 +02:00 `#!/usr/bin/env python3` `# Generate Apollonian Gaskets -- the math part.` ``` ``` `# Copyright (c) 2014 Ludger Sandig` `# This file is part of apollon.` ``` ``` `# Apollon is free software: you can redistribute it and/or modify` `# it under the terms of the GNU General Public License as published by` `# the Free Software Foundation, either version 3 of the License, or` `# (at your option) any later version.` ``` ``` `# Apollon is distributed in the hope that it will be useful,` `# but WITHOUT ANY WARRANTY; without even the implied warranty of` `# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the` `# GNU General Public License for more details.` ``` ``` `# You should have received a copy of the GNU General Public License` `# along with Apollon. If not, see .` ``` ``` `from cmath import *` `import random` ``` ``` `class Circle(object):` ``` """ ``` ` A circle represented by center point as complex number and radius.` ``` """ ``` ` def __init__ ( self, mx, my, r ):` ``` """ ``` ` @param mx: x center coordinate` ` @type mx: int or float` ` @param my: y center coordinate` ` @type my: int or float` ` @param r: radius` ` @type r: int or float` ``` """ ``` ` self.r = r` ` self.m = (mx +my*1j)` ``` ``` ` def __repr__ ( self ):` ``` """ ``` ` Pretty printing` ``` """ ``` ` return "Circle( self, %s, %s, %s )" % (self.m.real, self.m.imag, self.r)` ``` ``` ` def __str__ ( self ):` ``` """ ``` ` Pretty printing` ``` """ ``` ` return "Circle x:%.3f y:%.3f r:%.3f [cur:%.3f]" % (self.m.real, self.m.imag, self.r.real, self.curvature().real)` ``` ``` ` def curvature (self):` ``` """ ``` ` Get circle's curvature.` ` @rtype: float` ` @return: Curvature of the circle.` ``` """ ``` ` return 1/self.r` ``` ``` `def outerTangentCircle( circle1, circle2, circle3 ):` ``` """ ``` ` Takes three externally tangent circles and calculates the fourth one enclosing them.` ` @param circle1: first circle` ` @param circle2: second circle` ` @param circle3: third circle` ` @type circle1: L{Circle}` ` @type circle2: L{Circle}` ` @type circle3: L{Circle}` ` @return: The enclosing circle` ` @rtype: L{Circle}` ``` """ ``` ` cur1 = circle1.curvature()` ` cur2 = circle2.curvature()` ` cur3 = circle3.curvature()` ` m1 = circle1.m` ` m2 = circle2.m` ` m3 = circle3.m` ` cur4 = -2 * sqrt( cur1*cur2 + cur2*cur3 + cur1 * cur3 ) + cur1 + cur2 + cur3` ` m4 = ( -2 * sqrt( cur1*m1*cur2*m2 + cur2*m2*cur3*m3 + cur1*m1*cur3*m3 ) + cur1*m1 + cur2*m2 + cur3*m3 ) / cur4` ` circle4 = Circle( m4.real, m4.imag, 1/cur4 )` ` ` ` return circle4` ` ` ``` ``` `def tangentCirclesFromRadii( r2, r3, r4 ):` ``` """ ``` ` Takes three radii and calculates the corresponding externally` ` tangent circles as well as a fourth one enclosing them. The enclosing` ` circle is the first one.` ``` ``` ` @param r2, r3, r4: Radii of the circles to calculate` ` @type r2: int or float` ` @type r3: int or float` ` @type r4: int or float` ` @return: The four circles, where the first one is the enclosing one.` ` @rtype: (L{Circle}, L{Circle}, L{Circle}, L{Circle})` ``` """ ``` ` circle2 = Circle( 0, 0, r2 )` ` circle3 = Circle( r2 + r3, 0, r3 )` ` m4x = (r2*r2 + r2*r4 + r2*r3 - r3*r4) / (r2 + r3)` ` m4y = sqrt( (r2 + r4) * (r2 + r4) - m4x*m4x )` ` circle4 = Circle( m4x, m4y, r4 )` ` circle1 = outerTangentCircle( circle2, circle3, circle4 )` ` return ( circle1, circle2, circle3, circle4 )` ``` ``` `def secondSolution( fixed, c1, c2, c3 ):` ``` """ ``` ` If given four tangent circles, calculate the other one that is tangent` ` to the last three.` ` ` ` @param fixed: The fixed circle touches the other three, but not` ` the one to be calculated.` ` ` ` @param c1, c2, c3: Three circles to which the other tangent circle` ` is to be calculated.` ``` ``` ` @type fixed: L{Circle}` ` @type c1: L{Circle}` ` @type c2: L{Circle}` ` @type c3: L{Circle}` ` @return: The circle.` ` @rtype: L{Circle}` ``` """ ``` ` ` ` curf = fixed.curvature()` ` cur1 = c1.curvature()` ` cur2 = c2.curvature()` ` cur3 = c3.curvature()` ``` ``` ` curn = 2 * (cur1 + cur2 + cur3) - curf` ` mn = (2 * (cur1*c1.m + cur2*c2.m + cur3*c3.m) - curf*fixed.m ) / curn` ` return Circle( mn.real, mn.imag, 1/curn )` ``` ``` `class ApollonianGasket(object):` ``` """ ``` ` Container for an Apollonian Gasket.` ``` """ ``` ` def __init__(self, c1, c2, c3):` ``` """ ``` ` Creates a basic apollonian Gasket with four circles. ` ``` ``` ` @param c1, c2, c3: The curvatures of the three inner circles of the` ` starting set (i.e. depth 0 of the recursion). The fourth,` ` enclosing circle will be calculated from them.` ` @type c1: int or float` ` @type c2: int or float` ` @type c3: int or float` ``` """ ``` ` self.start = tangentCirclesFromRadii( 1/c1, 1/c2, 1/c3 )` ` self.genCircles = list(self.start)` ``` ``` ` def recurse(self, circles, depth, maxDepth):` ``` """Recursively calculate the smaller circles of the AG up to the ``` ` given depth. Note that for depth n we get 2*3^{n+1} circles.` ``` ``` ` @param maxDepth: Maximal depth of the recursion.` ` @type maxDepth: int` ``` ``` ` @param circles: 4-Tuple of circles for which the second` ` solutions are calculated` ` @type circles: (L{Circle}, L{Circle}, L{Circle}, L{Circle})` ``` ``` ` @param depth: Current depth` ` @type depth: int` ``` """ ``` ` if( depth == maxDepth ):` ` return` ` (c1, c2, c3, c4) = circles` ` if( depth == 0 ):` ` # First recursive step, this is the only time we need to` ` # calculate 4 new circles.` ` del self.genCircles[4:]` ` cspecial = secondSolution( c1, c2, c3, c4 )` ` self.genCircles.append( cspecial )` ` self.recurse( (cspecial, c2, c3, c4), 1, maxDepth )` ``` ``` ` cn2 = secondSolution( c2, c1, c3, c4 )` ` self.genCircles.append( cn2 )` ` cn3 = secondSolution( c3, c1, c2, c4 )` ` self.genCircles.append( cn3 )` ` cn4 = secondSolution( c4, c1, c2, c3 )` ` self.genCircles.append( cn4 ) ` ``` ``` ` self.recurse( (cn2, c1, c3, c4), depth+1, maxDepth )` ` self.recurse( (cn3, c1, c2, c4), depth+1, maxDepth )` ` self.recurse( (cn4, c1, c2, c3), depth+1, maxDepth )` ``` ``` ` def generate(self, depth):` ``` """ ``` ` Wrapper for the recurse function. Generate the AG,` ` @param depth: Recursion depth of the Gasket` ` @type depth: int` ``` """ ``` ` self.recurse(self.start, 0, depth)` ``` ```