simetri.geometry.circle
1from math import pi, cos, sqrt, acos, atan 2from dataclasses import dataclass 3import cmath 4 5import numpy as np 6 7from .geometry import distance, homogenize, side_len_to_radius, angle_between_lines2 8from ..graphics.affine import rotate, scale_matrix, rotation_matrix 9from ..graphics.shapes import Circle 10 11array = np.array 12dot = np.dot 13linalg = np.linalg 14 15@dataclass 16class Circle_: 17 """A simple circle class.""" 18 19 center: tuple 20 radius: float 21 22def circle_tangent_to_3_circles(c1, r1, c2, r2, c3, r3, s1=-1, s2=-1, s3=-1): 23 """Given the centers and radii of 3 circles, return the center and radius 24 of a circle that is tangent to all 3 circles. 25 26 Args: 27 c1 (tuple): Center of the first circle. 28 r1 (float): Radius of the first circle. 29 c2 (tuple): Center of the second circle. 30 r2 (float): Radius of the second circle. 31 c3 (tuple): Center of the third circle. 32 r3 (float): Radius of the third circle. 33 s1 (int, optional): Sign for the first circle. Defaults to -1. 34 s2 (int, optional): Sign for the second circle. Defaults to -1. 35 s3 (int, optional): Sign for the third circle. Defaults to -1. 36 37 Returns: 38 tuple: Center (x, y) and radius of the tangent circle. 39 """ 40 41 x1, y1 = c1 42 x2, y2 = c2 43 x3, y3 = c3 44 45 v11 = 2 * x2 - 2 * x1 46 v12 = 2 * y2 - 2 * y1 47 v13 = x1 * x1 - x2 * x2 + y1 * y1 - y2 * y2 - r1 * r1 + r2 * r2 48 v14 = 2 * s2 * r2 - 2 * s1 * r1 49 50 v21 = 2 * x3 - 2 * x2 51 v22 = 2 * y3 - 2 * y2 52 v23 = x2 * x2 - x3 * x3 + y2 * y2 - y3 * y3 - r2 * r2 + r3 * r3 53 v24 = 2 * s3 * r3 - 2 * s3 * r2 54 55 w12 = v12 / v11 56 w13 = v13 / v11 57 w14 = v14 / v11 58 59 w22 = v22 / v21 - w12 60 w23 = v23 / v21 - w13 61 w24 = v24 / v21 - w14 62 63 P = -w23 / w22 64 Q = w24 / w22 65 M = -w12 * P - w13 66 N = w14 - w12 * Q 67 68 a = N*N + Q*Q - 1 69 b = 2 * M * N - 2 * N * x1 + 2 * P * Q - 2 * Q * y1 + 2 * s1 * r1 70 c = x1 * x1 + M * M - 2 * M * x1 + P * P + y1 * y1 - 2 * P * y1 - r1 * r1 71 72 # Find a root of a quadratic equation. 73 # This requires the circle centers not to be collinear 74 D = b * b - 4 * a * c 75 rs = (-b - sqrt(D)) / (2 * a) 76 77 xs = M + N * rs 78 ys = P + Q * rs 79 80 return (xs, ys, rs) 81 82def apollonius(r1, r2, r3, z1, z2, z3, plus_minus=1): 83 """Solves the Problem of Apollonius using Descartes' Theorem. 84 85 Args: 86 r1 (float): Radius of the first circle. 87 r2 (float): Radius of the second circle. 88 r3 (float): Radius of the third circle. 89 z1 (complex): Center of the first circle. 90 z2 (complex): Center of the second circle. 91 z3 (complex): Center of the third circle. 92 plus_minus (int, optional): +1 for outer tangent circle, -1 for inner tangent circle. Defaults to 1. 93 94 Returns: 95 tuple: Radius and center coordinates (x, y) of the tangent circle, or None if no solution is found. 96 """ 97 k1, k2, k3 = 1/r1, 1/r2, 1/r3 98 99 # Applying Descartes' Theorem 100 k4_values = (k1 + k2 + k3) + plus_minus * 2 * sqrt(k1*k2 + k2*k3 + k3*k1) 101 102 # Handle cases where no solution exists (e.g., division by zero) 103 if k4_values == 0: 104 return None 105 106 r4 = 1/k4_values 107 z4 = ((k1*z1 + k2*z2 + k3*z3 + plus_minus * 2 * 108 cmath.sqrt(k1*k2*z1*z2 + k2*k3*z2*z3 + k3*k1*z3*z1)) / k4_values) 109 110 return r4, z4 111 112 113 114def circle_tangent_to_2_circles(c1, r1, c2, r2, r): 115 """Given the centers and radii of 2 circles, return the center 116 of a circle with radius r that is tangent to both circles. 117 118 Args: 119 c1 (tuple): Center of the first circle. 120 r1 (float): Radius of the first circle. 121 c2 (tuple): Center of the second circle. 122 r2 (float): Radius of the second circle. 123 r (float): Radius of the tangent circle. 124 125 Returns: 126 tuple: Centers (x1, y1) and (x2, y2) of the tangent circle. 127 """ 128 x1, y1 = c1 129 x2, y2 = c2 130 131 r12 = r1**2 132 r12y1 = r12 * y1 133 r12y2 = r12 * y2 134 r1r2 = r1 * r2 135 r22 = r2**2 136 r22y2 = r22 * y2 137 r_2 = r**2 138 rr1 = r * r1 139 rr1y1 = rr1 * y1 140 rr1y2 = rr1 * y2 141 rr2 = r * r2 142 rr2y1 = rr2 * y1 143 rr2y2 = rr2 * y2 144 x12 = x1**2 145 x12y1 = x12 * y1 146 x12y2 = x12 * y2 147 x1_x2 = x1 - x2 148 x1x2 = x1 * x2 149 x1x2y1 = x1x2 * y1 150 x1x2y2 = x1x2 * y2 151 x22 = x2**2 152 x22y1 = x22 * y1 153 x22y2 = x22 * y2 154 y12 = y1**2 155 y12y2 = y12 * y2 156 y13 = y1**3 157 y1y2 = y1 * y2 158 y22 = y2**2 159 y1y22 = y1 * y22 160 y23 = y2**3 161 162 x_1 = ( 163 -(y1 - y2) 164 * ( 165 -2 * rr1y1 166 + 2 * rr1y2 167 + 2 * rr2y1 168 - 2 * rr2y2 169 - r12y1 170 + r12y2 171 + r22 * y1 172 - r22y2 173 + x12y1 174 + x12y2 175 - 2 * x1x2y1 176 - 2 * x1x2y2 177 + x22y1 178 + x22y2 179 + y13 180 - y12y2 181 - y1y22 182 + y23 183 + sqrt( 184 (-r12 + 2 * r1r2 - r22 + x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 185 * ( 186 4 * r_2 187 + 4 * rr1 188 + 4 * rr2 189 + r12 190 + 2 * r1r2 191 + r22 192 - x12 193 + 2 * x1x2 194 - x22 195 - y12 196 + 2 * y1y2 197 - y22 198 ) 199 ) 200 * (-x1 + x2) 201 ) 202 - (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 203 * (2 * rr1 - 2 * rr2 + r12 - r22 - x12 + x22 - y12 + y22) 204 ) / (2 * (x1_x2) * (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22)) 205 206 y_1 = ( 207 -2 * rr1y1 208 + 2 * rr1y2 209 + 2 * rr2y1 210 - 2 * rr2y2 211 - r12y1 212 + r12y2 213 + r22 * y1 214 - r22y2 215 + x12y1 216 + x12y2 217 - 2 * x1x2y1 218 - 2 * x1x2y2 219 + x22y1 220 + x22y2 221 + y13 222 - y12y2 223 - y1y22 224 + y23 225 + sqrt( 226 (-r12 + 2 * r1r2 - r22 + x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 227 * ( 228 4 * r_2 229 + 4 * rr1 230 + 4 * rr2 231 + r12 232 + 2 * r1r2 233 + r22 234 - x12 235 + 2 * x1x2 236 - x22 237 - y12 238 + 2 * y1y2 239 - y22 240 ) 241 ) 242 * (-x1 + x2) 243 ) / (2 * (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22)) 244 245 x_2 = ( 246 -(y1 - y2) 247 * ( 248 -2 * rr1y1 249 + 2 * rr1y2 250 + 2 * rr2y1 251 - 2 * rr2y2 252 - r12y1 253 + r12y2 254 + r22 * y1 255 - r22y2 256 + x12y1 257 + x12y2 258 - 2 * x1x2y1 259 - 2 * x1x2y2 260 + x22y1 261 + x22y2 262 + y13 263 - y12y2 264 - y1y22 265 + y23 266 + sqrt( 267 (-r12 + 2 * r1r2 - r22 + x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 268 * ( 269 4 * r_2 270 + 4 * rr1 271 + 4 * rr2 272 + r12 273 + 2 * r1r2 274 + r22 275 - x12 276 + 2 * x1x2 277 - x22 278 - y12 279 + 2 * y1y2 280 - y22 281 ) 282 ) 283 * (x1_x2) 284 ) 285 - (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 286 * (2 * rr1 - 2 * rr2 + r12 - r22 - x12 + x22 - y12 + y22) 287 ) / (2 * (x1_x2) * (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22)) 288 289 y_2 = ( 290 -2 * rr1y1 291 + 2 * rr1y2 292 + 2 * rr2y1 293 - 2 * rr2y2 294 - r12y1 295 + r12y2 296 + r22 * y1 297 - r22y2 298 + x12y1 299 + x12y2 300 - 2 * x1x2y1 301 - 2 * x1x2y2 302 + x22y1 303 + x22y2 304 + y13 305 - y12y2 306 - y1y22 307 + y23 308 + sqrt( 309 (-r12 + 2 * r1r2 - r22 + x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 310 * ( 311 4 * r_2 312 + 4 * rr1 313 + 4 * rr2 314 + r12 315 + 2 * r1r2 316 + r22 317 - x12 318 + 2 * x1x2 319 - x22 320 - y12 321 + 2 * y1y2 322 - y22 323 ) 324 ) 325 * (x1_x2) 326 ) / (2 * (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22)) 327 328 return ((x_1, y_1), (x_2, y_2)) 329 330 331def tangent_points(center1, radius, center2, radius2, cross=False): 332 """Returns the tangent points (p1, p2, p3, p4) in world coordinates. 333 334 Args: 335 center1 (tuple): Center of the first circle. 336 radius (float): Radius of the first circle. 337 center2 (tuple): Center of the second circle. 338 radius2 (float): Radius of the second circle. 339 cross (bool, optional): Whether to calculate crossing tangents. Defaults to False. 340 341 Returns: 342 tuple: Tangent points (p1, p2, p3, p4) in world coordinates. 343 """ 344 c1 = Circle_(center1, radius) 345 c2 = Circle_(center2, radius2) 346 if radius < radius2: 347 c1, c2 = c2, c1 348 pos = c1.center 349 dist = distance(pos, c2.center) 350 r1 = c1.radius 351 r2 = c2.radius 352 353 if cross: 354 dr = r1 + r2 355 else: 356 dr = r1 - r2 357 358 x = sqrt(dist**2 - dr**2) 359 y = pos[1] + r1 360 p1 = [pos[0], y] 361 p2 = [pos[0] + x, y] 362 points = homogenize([p1, p2]) 363 alpha = angle_between_lines2((pos[0] + x, pos[1] + dr), pos, c2.center) 364 tp1w, tp2w = rotate(points, alpha, pos) 365 366 if x == 0: 367 beta = 0 368 else: 369 beta = pi / 2 - atan(dr / x) 370 tp3w = rotate([tp1w], -2 * beta, pos)[0] 371 tp4w = rotate([tp2w], -2 * beta, c2.center)[0] 372 373 return (tp1w, tp2w, tp3w, tp4w) 374 375 376def circle_area(rad): 377 """Given the radius of a circle, return the area of the circle. 378 379 Args: 380 rad (float): Radius of the circle. 381 382 Returns: 383 float: Area of the circle. 384 """ 385 return pi * rad**2 386 387 388def circle_circumference(rad): 389 """Given the radius of a circle, return the circumference of the circle. 390 391 Args: 392 rad (float): Radius of the circle. 393 394 Returns: 395 float: Circumference of the circle. 396 """ 397 return 2 * pi * rad 398 399 400def flower_angle(r1, r2, r3): 401 """Given the radii of 3 circles forming an interstice, return the angle between 402 the lines connecting circles' centers to center of the circle with r1 radius. 403 404 Args: 405 r1 (float): Radius of the first circle. 406 r2 (float): Radius of the second circle. 407 r3 (float): Radius of the third circle. 408 409 Returns: 410 float: Angle between the lines connecting circles' centers. 411 """ 412 angle = acos( 413 ((r1 + r2) ** 2 + (r1 + r3) ** 2 - (r2 + r3) ** 2) / (2 * (r1 + r2) * (r1 + r3)) 414 ) 415 416 return angle 417 418 419 420ratios = {8: 0.4974, 9: 0.5394, 10: 0.575, 11: 0.6056, 421 12: 0.6321, 13: 0.6553, 14: 0.6757, 15: 0.6939, 16: 0.7101, 422 17: 0.7248, 18: 0.738, 19: 0.75, 20: 0.7609} 423 424def circle_flower(n, radius=25, layers=6, ratio = None): 425 """Steiner chain. Return a list of circles that form a flower-like pattern. 426 427 Args: 428 n (int): Number of circles. 429 radius (float, optional): Radius of the circles. Defaults to 25. 430 layers (int, optional): Number of layers. Defaults to 6. 431 ratio (float, optional): Ratio for scaling. Defaults to None. 432 433 Returns: 434 list: List of circles forming a flower-like pattern. 435 436 Raises: 437 ValueError: If n is less than 8. 438 """ 439 if n<8: 440 raise ValueError('n must be greater than 7') 441 if ratio is None: 442 if n<21: 443 ratio = ratios[n] 444 else: 445 ratio = (-0.000000089767 * n**4 + 0.000015821834 * n**3 + 446 -0.001100867708 * n **2+ 0.038096046379 * n + 0.327363569038) 447 448 r1 = side_len_to_radius(n, 2*radius) 449 circles = Circle((r1, 0), radius).rotate(pi/(n/2), (0, 0), reps=n-1) 450 xform = scale_matrix(ratio) @ rotation_matrix(pi/n) 451 452 return circles.transform(xform_matrix=xform, reps=layers)
array(object, dtype=None, *, copy=True, order='K', subok=False, ndmin=0, like=None)
Create an array.
Parameters
object : array_like
An array, any object exposing the array interface, an object whose
__array__
method returns an array, or any (nested) sequence.
If object is a scalar, a 0-dimensional array containing object is
returned.
dtype : data-type, optional
The desired data-type for the array. If not given, NumPy will try to use
a default dtype
that can represent the values (by applying promotion
rules when necessary.)
copy : bool, optional
If True
(default), then the array data is copied. If None
,
a copy will only be made if __array__
returns a copy, if obj is
a nested sequence, or if a copy is needed to satisfy any of the other
requirements (dtype
, order
, etc.). Note that any copy of
the data is shallow, i.e., for arrays with object dtype, the new
array will point to the same objects. See Examples for ndarray.copy
.
For False
it raises a ValueError
if a copy cannot be avoided.
Default: True
.
order : {'K', 'A', 'C', 'F'}, optional
Specify the memory layout of the array. If object is not an array, the
newly created array will be in C order (row major) unless 'F' is
specified, in which case it will be in Fortran order (column major).
If object is an array the following holds.
===== ========= ===================================================
order no copy copy=True
===== ========= ===================================================
'K' unchanged F & C order preserved, otherwise most similar order
'A' unchanged F order if input is F and not C, otherwise C order
'C' C order C order
'F' F order F order
===== ========= ===================================================
When ``copy=None`` and a copy is made for other reasons, the result is
the same as if ``copy=True``, with some exceptions for 'A', see the
Notes section. The default order is 'K'.
subok : bool, optional
If True, then sub-classes will be passed-through, otherwise
the returned array will be forced to be a base-class array (default).
ndmin : int, optional
Specifies the minimum number of dimensions that the resulting
array should have. Ones will be prepended to the shape as
needed to meet this requirement.
like : array_like, optional
Reference object to allow the creation of arrays which are not
NumPy arrays. If an array-like passed in as like
supports
the __array_function__
protocol, the result will be defined
by it. In this case, it ensures the creation of an array object
compatible with that passed in via this argument.
*New in version 1.20.0.*
Returns
out : ndarray An array object satisfying the specified requirements.
See Also
empty_like : Return an empty array with shape and type of input. ones_like : Return an array of ones with shape and type of input. zeros_like : Return an array of zeros with shape and type of input. full_like : Return a new array with shape of input filled with value. empty : Return a new uninitialized array. ones : Return a new array setting values to one. zeros : Return a new array setting values to zero. full : Return a new array of given shape filled with value. copy: Return an array copy of the given object.
Notes
When order is 'A' and object
is an array in neither 'C' nor 'F' order,
and a copy is forced by a change in dtype, then the order of the result is
not necessarily 'C' as expected. This is likely a bug.
Examples
>>> np.array([1, 2, 3])
array([1, 2, 3])
Upcasting:
>>> np.array([1, 2, 3.0])
array([ 1., 2., 3.])
More than one dimension:
>>> np.array([[1, 2], [3, 4]])
array([[1, 2],
[3, 4]])
Minimum dimensions 2:
>>> np.array([1, 2, 3], ndmin=2)
array([[1, 2, 3]])
Type provided:
>>> np.array([1, 2, 3], dtype=complex)
array([ 1.+0.j, 2.+0.j, 3.+0.j])
Data-type consisting of more than one element:
>>> x = np.array([(1,2),(3,4)],dtype=[('a','<i4'),('b','<i4')])
>>> x['a']
array([1, 3])
Creating an array from sub-classes:
>>> np.array(np.asmatrix('1 2; 3 4'))
array([[1, 2],
[3, 4]])
>>> np.array(np.asmatrix('1 2; 3 4'), subok=True)
matrix([[1, 2],
[3, 4]])
dot(a, b, out=None)
Dot product of two arrays. Specifically,
If both
a
andb
are 1-D arrays, it is inner product of vectors (without complex conjugation).If both
a
andb
are 2-D arrays, it is matrix multiplication, but usingmatmul()
ora @ b
is preferred.If either
a
orb
is 0-D (scalar), it is equivalent tomultiply()
and usingnumpy.multiply(a, b)
ora * b
is preferred.If
a
is an N-D array andb
is a 1-D array, it is a sum product over the last axis ofa
andb
.If
a
is an N-D array andb
is an M-D array (whereM>=2
), it is a sum product over the last axis ofa
and the second-to-last axis ofb
::dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
It uses an optimized BLAS library when possible (see numpy.linalg
).
Parameters
a : array_like
First argument.
b : array_like
Second argument.
out : ndarray, optional
Output argument. This must have the exact kind that would be returned
if it was not used. In particular, it must have the right type, must be
C-contiguous, and its dtype must be the dtype that would be returned
for dot(a,b)
. This is a performance feature. Therefore, if these
conditions are not met, an exception is raised, instead of attempting
to be flexible.
Returns
output : ndarray
Returns the dot product of a
and b
. If a
and b
are both
scalars or both 1-D arrays then a scalar is returned; otherwise
an array is returned.
If out
is given, then it is returned.
Raises
ValueError
If the last dimension of a
is not the same size as
the second-to-last dimension of b
.
See Also
vdot : Complex-conjugating dot product. tensordot : Sum products over arbitrary axes. einsum : Einstein summation convention. matmul : '@' operator as method with out parameter. linalg.multi_dot : Chained dot product.
Examples
>>> np.dot(3, 4)
12
Neither argument is complex-conjugated:
>>> np.dot([2j, 3j], [2j, 3j])
(-13+0j)
For 2-D arrays it is the matrix product:
>>> a = [[1, 0], [0, 1]]
>>> b = [[4, 1], [2, 2]]
>>> np.dot(a, b)
array([[4, 1],
[2, 2]])
>>> a = np.arange(3*4*5*6).reshape((3,4,5,6))
>>> b = np.arange(3*4*5*6)[::-1].reshape((5,4,6,3))
>>> np.dot(a, b)[2,3,2,1,2,2]
499128
>>> sum(a[2,3,2,:] * b[1,2,:,2])
499128
A simple circle class.
23def circle_tangent_to_3_circles(c1, r1, c2, r2, c3, r3, s1=-1, s2=-1, s3=-1): 24 """Given the centers and radii of 3 circles, return the center and radius 25 of a circle that is tangent to all 3 circles. 26 27 Args: 28 c1 (tuple): Center of the first circle. 29 r1 (float): Radius of the first circle. 30 c2 (tuple): Center of the second circle. 31 r2 (float): Radius of the second circle. 32 c3 (tuple): Center of the third circle. 33 r3 (float): Radius of the third circle. 34 s1 (int, optional): Sign for the first circle. Defaults to -1. 35 s2 (int, optional): Sign for the second circle. Defaults to -1. 36 s3 (int, optional): Sign for the third circle. Defaults to -1. 37 38 Returns: 39 tuple: Center (x, y) and radius of the tangent circle. 40 """ 41 42 x1, y1 = c1 43 x2, y2 = c2 44 x3, y3 = c3 45 46 v11 = 2 * x2 - 2 * x1 47 v12 = 2 * y2 - 2 * y1 48 v13 = x1 * x1 - x2 * x2 + y1 * y1 - y2 * y2 - r1 * r1 + r2 * r2 49 v14 = 2 * s2 * r2 - 2 * s1 * r1 50 51 v21 = 2 * x3 - 2 * x2 52 v22 = 2 * y3 - 2 * y2 53 v23 = x2 * x2 - x3 * x3 + y2 * y2 - y3 * y3 - r2 * r2 + r3 * r3 54 v24 = 2 * s3 * r3 - 2 * s3 * r2 55 56 w12 = v12 / v11 57 w13 = v13 / v11 58 w14 = v14 / v11 59 60 w22 = v22 / v21 - w12 61 w23 = v23 / v21 - w13 62 w24 = v24 / v21 - w14 63 64 P = -w23 / w22 65 Q = w24 / w22 66 M = -w12 * P - w13 67 N = w14 - w12 * Q 68 69 a = N*N + Q*Q - 1 70 b = 2 * M * N - 2 * N * x1 + 2 * P * Q - 2 * Q * y1 + 2 * s1 * r1 71 c = x1 * x1 + M * M - 2 * M * x1 + P * P + y1 * y1 - 2 * P * y1 - r1 * r1 72 73 # Find a root of a quadratic equation. 74 # This requires the circle centers not to be collinear 75 D = b * b - 4 * a * c 76 rs = (-b - sqrt(D)) / (2 * a) 77 78 xs = M + N * rs 79 ys = P + Q * rs 80 81 return (xs, ys, rs)
Given the centers and radii of 3 circles, return the center and radius of a circle that is tangent to all 3 circles.
Arguments:
- c1 (tuple): Center of the first circle.
- r1 (float): Radius of the first circle.
- c2 (tuple): Center of the second circle.
- r2 (float): Radius of the second circle.
- c3 (tuple): Center of the third circle.
- r3 (float): Radius of the third circle.
- s1 (int, optional): Sign for the first circle. Defaults to -1.
- s2 (int, optional): Sign for the second circle. Defaults to -1.
- s3 (int, optional): Sign for the third circle. Defaults to -1.
Returns:
tuple: Center (x, y) and radius of the tangent circle.
83def apollonius(r1, r2, r3, z1, z2, z3, plus_minus=1): 84 """Solves the Problem of Apollonius using Descartes' Theorem. 85 86 Args: 87 r1 (float): Radius of the first circle. 88 r2 (float): Radius of the second circle. 89 r3 (float): Radius of the third circle. 90 z1 (complex): Center of the first circle. 91 z2 (complex): Center of the second circle. 92 z3 (complex): Center of the third circle. 93 plus_minus (int, optional): +1 for outer tangent circle, -1 for inner tangent circle. Defaults to 1. 94 95 Returns: 96 tuple: Radius and center coordinates (x, y) of the tangent circle, or None if no solution is found. 97 """ 98 k1, k2, k3 = 1/r1, 1/r2, 1/r3 99 100 # Applying Descartes' Theorem 101 k4_values = (k1 + k2 + k3) + plus_minus * 2 * sqrt(k1*k2 + k2*k3 + k3*k1) 102 103 # Handle cases where no solution exists (e.g., division by zero) 104 if k4_values == 0: 105 return None 106 107 r4 = 1/k4_values 108 z4 = ((k1*z1 + k2*z2 + k3*z3 + plus_minus * 2 * 109 cmath.sqrt(k1*k2*z1*z2 + k2*k3*z2*z3 + k3*k1*z3*z1)) / k4_values) 110 111 return r4, z4
Solves the Problem of Apollonius using Descartes' Theorem.
Arguments:
- r1 (float): Radius of the first circle.
- r2 (float): Radius of the second circle.
- r3 (float): Radius of the third circle.
- z1 (complex): Center of the first circle.
- z2 (complex): Center of the second circle.
- z3 (complex): Center of the third circle.
- plus_minus (int, optional): +1 for outer tangent circle, -1 for inner tangent circle. Defaults to 1.
Returns:
tuple: Radius and center coordinates (x, y) of the tangent circle, or None if no solution is found.
115def circle_tangent_to_2_circles(c1, r1, c2, r2, r): 116 """Given the centers and radii of 2 circles, return the center 117 of a circle with radius r that is tangent to both circles. 118 119 Args: 120 c1 (tuple): Center of the first circle. 121 r1 (float): Radius of the first circle. 122 c2 (tuple): Center of the second circle. 123 r2 (float): Radius of the second circle. 124 r (float): Radius of the tangent circle. 125 126 Returns: 127 tuple: Centers (x1, y1) and (x2, y2) of the tangent circle. 128 """ 129 x1, y1 = c1 130 x2, y2 = c2 131 132 r12 = r1**2 133 r12y1 = r12 * y1 134 r12y2 = r12 * y2 135 r1r2 = r1 * r2 136 r22 = r2**2 137 r22y2 = r22 * y2 138 r_2 = r**2 139 rr1 = r * r1 140 rr1y1 = rr1 * y1 141 rr1y2 = rr1 * y2 142 rr2 = r * r2 143 rr2y1 = rr2 * y1 144 rr2y2 = rr2 * y2 145 x12 = x1**2 146 x12y1 = x12 * y1 147 x12y2 = x12 * y2 148 x1_x2 = x1 - x2 149 x1x2 = x1 * x2 150 x1x2y1 = x1x2 * y1 151 x1x2y2 = x1x2 * y2 152 x22 = x2**2 153 x22y1 = x22 * y1 154 x22y2 = x22 * y2 155 y12 = y1**2 156 y12y2 = y12 * y2 157 y13 = y1**3 158 y1y2 = y1 * y2 159 y22 = y2**2 160 y1y22 = y1 * y22 161 y23 = y2**3 162 163 x_1 = ( 164 -(y1 - y2) 165 * ( 166 -2 * rr1y1 167 + 2 * rr1y2 168 + 2 * rr2y1 169 - 2 * rr2y2 170 - r12y1 171 + r12y2 172 + r22 * y1 173 - r22y2 174 + x12y1 175 + x12y2 176 - 2 * x1x2y1 177 - 2 * x1x2y2 178 + x22y1 179 + x22y2 180 + y13 181 - y12y2 182 - y1y22 183 + y23 184 + sqrt( 185 (-r12 + 2 * r1r2 - r22 + x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 186 * ( 187 4 * r_2 188 + 4 * rr1 189 + 4 * rr2 190 + r12 191 + 2 * r1r2 192 + r22 193 - x12 194 + 2 * x1x2 195 - x22 196 - y12 197 + 2 * y1y2 198 - y22 199 ) 200 ) 201 * (-x1 + x2) 202 ) 203 - (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 204 * (2 * rr1 - 2 * rr2 + r12 - r22 - x12 + x22 - y12 + y22) 205 ) / (2 * (x1_x2) * (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22)) 206 207 y_1 = ( 208 -2 * rr1y1 209 + 2 * rr1y2 210 + 2 * rr2y1 211 - 2 * rr2y2 212 - r12y1 213 + r12y2 214 + r22 * y1 215 - r22y2 216 + x12y1 217 + x12y2 218 - 2 * x1x2y1 219 - 2 * x1x2y2 220 + x22y1 221 + x22y2 222 + y13 223 - y12y2 224 - y1y22 225 + y23 226 + sqrt( 227 (-r12 + 2 * r1r2 - r22 + x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 228 * ( 229 4 * r_2 230 + 4 * rr1 231 + 4 * rr2 232 + r12 233 + 2 * r1r2 234 + r22 235 - x12 236 + 2 * x1x2 237 - x22 238 - y12 239 + 2 * y1y2 240 - y22 241 ) 242 ) 243 * (-x1 + x2) 244 ) / (2 * (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22)) 245 246 x_2 = ( 247 -(y1 - y2) 248 * ( 249 -2 * rr1y1 250 + 2 * rr1y2 251 + 2 * rr2y1 252 - 2 * rr2y2 253 - r12y1 254 + r12y2 255 + r22 * y1 256 - r22y2 257 + x12y1 258 + x12y2 259 - 2 * x1x2y1 260 - 2 * x1x2y2 261 + x22y1 262 + x22y2 263 + y13 264 - y12y2 265 - y1y22 266 + y23 267 + sqrt( 268 (-r12 + 2 * r1r2 - r22 + x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 269 * ( 270 4 * r_2 271 + 4 * rr1 272 + 4 * rr2 273 + r12 274 + 2 * r1r2 275 + r22 276 - x12 277 + 2 * x1x2 278 - x22 279 - y12 280 + 2 * y1y2 281 - y22 282 ) 283 ) 284 * (x1_x2) 285 ) 286 - (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 287 * (2 * rr1 - 2 * rr2 + r12 - r22 - x12 + x22 - y12 + y22) 288 ) / (2 * (x1_x2) * (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22)) 289 290 y_2 = ( 291 -2 * rr1y1 292 + 2 * rr1y2 293 + 2 * rr2y1 294 - 2 * rr2y2 295 - r12y1 296 + r12y2 297 + r22 * y1 298 - r22y2 299 + x12y1 300 + x12y2 301 - 2 * x1x2y1 302 - 2 * x1x2y2 303 + x22y1 304 + x22y2 305 + y13 306 - y12y2 307 - y1y22 308 + y23 309 + sqrt( 310 (-r12 + 2 * r1r2 - r22 + x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22) 311 * ( 312 4 * r_2 313 + 4 * rr1 314 + 4 * rr2 315 + r12 316 + 2 * r1r2 317 + r22 318 - x12 319 + 2 * x1x2 320 - x22 321 - y12 322 + 2 * y1y2 323 - y22 324 ) 325 ) 326 * (x1_x2) 327 ) / (2 * (x12 - 2 * x1x2 + x22 + y12 - 2 * y1y2 + y22)) 328 329 return ((x_1, y_1), (x_2, y_2))
Given the centers and radii of 2 circles, return the center of a circle with radius r that is tangent to both circles.
Arguments:
- c1 (tuple): Center of the first circle.
- r1 (float): Radius of the first circle.
- c2 (tuple): Center of the second circle.
- r2 (float): Radius of the second circle.
- r (float): Radius of the tangent circle.
Returns:
tuple: Centers (x1, y1) and (x2, y2) of the tangent circle.
332def tangent_points(center1, radius, center2, radius2, cross=False): 333 """Returns the tangent points (p1, p2, p3, p4) in world coordinates. 334 335 Args: 336 center1 (tuple): Center of the first circle. 337 radius (float): Radius of the first circle. 338 center2 (tuple): Center of the second circle. 339 radius2 (float): Radius of the second circle. 340 cross (bool, optional): Whether to calculate crossing tangents. Defaults to False. 341 342 Returns: 343 tuple: Tangent points (p1, p2, p3, p4) in world coordinates. 344 """ 345 c1 = Circle_(center1, radius) 346 c2 = Circle_(center2, radius2) 347 if radius < radius2: 348 c1, c2 = c2, c1 349 pos = c1.center 350 dist = distance(pos, c2.center) 351 r1 = c1.radius 352 r2 = c2.radius 353 354 if cross: 355 dr = r1 + r2 356 else: 357 dr = r1 - r2 358 359 x = sqrt(dist**2 - dr**2) 360 y = pos[1] + r1 361 p1 = [pos[0], y] 362 p2 = [pos[0] + x, y] 363 points = homogenize([p1, p2]) 364 alpha = angle_between_lines2((pos[0] + x, pos[1] + dr), pos, c2.center) 365 tp1w, tp2w = rotate(points, alpha, pos) 366 367 if x == 0: 368 beta = 0 369 else: 370 beta = pi / 2 - atan(dr / x) 371 tp3w = rotate([tp1w], -2 * beta, pos)[0] 372 tp4w = rotate([tp2w], -2 * beta, c2.center)[0] 373 374 return (tp1w, tp2w, tp3w, tp4w)
Returns the tangent points (p1, p2, p3, p4) in world coordinates.
Arguments:
- center1 (tuple): Center of the first circle.
- radius (float): Radius of the first circle.
- center2 (tuple): Center of the second circle.
- radius2 (float): Radius of the second circle.
- cross (bool, optional): Whether to calculate crossing tangents. Defaults to False.
Returns:
tuple: Tangent points (p1, p2, p3, p4) in world coordinates.
377def circle_area(rad): 378 """Given the radius of a circle, return the area of the circle. 379 380 Args: 381 rad (float): Radius of the circle. 382 383 Returns: 384 float: Area of the circle. 385 """ 386 return pi * rad**2
Given the radius of a circle, return the area of the circle.
Arguments:
- rad (float): Radius of the circle.
Returns:
float: Area of the circle.
389def circle_circumference(rad): 390 """Given the radius of a circle, return the circumference of the circle. 391 392 Args: 393 rad (float): Radius of the circle. 394 395 Returns: 396 float: Circumference of the circle. 397 """ 398 return 2 * pi * rad
Given the radius of a circle, return the circumference of the circle.
Arguments:
- rad (float): Radius of the circle.
Returns:
float: Circumference of the circle.
401def flower_angle(r1, r2, r3): 402 """Given the radii of 3 circles forming an interstice, return the angle between 403 the lines connecting circles' centers to center of the circle with r1 radius. 404 405 Args: 406 r1 (float): Radius of the first circle. 407 r2 (float): Radius of the second circle. 408 r3 (float): Radius of the third circle. 409 410 Returns: 411 float: Angle between the lines connecting circles' centers. 412 """ 413 angle = acos( 414 ((r1 + r2) ** 2 + (r1 + r3) ** 2 - (r2 + r3) ** 2) / (2 * (r1 + r2) * (r1 + r3)) 415 ) 416 417 return angle
Given the radii of 3 circles forming an interstice, return the angle between the lines connecting circles' centers to center of the circle with r1 radius.
Arguments:
- r1 (float): Radius of the first circle.
- r2 (float): Radius of the second circle.
- r3 (float): Radius of the third circle.
Returns:
float: Angle between the lines connecting circles' centers.
425def circle_flower(n, radius=25, layers=6, ratio = None): 426 """Steiner chain. Return a list of circles that form a flower-like pattern. 427 428 Args: 429 n (int): Number of circles. 430 radius (float, optional): Radius of the circles. Defaults to 25. 431 layers (int, optional): Number of layers. Defaults to 6. 432 ratio (float, optional): Ratio for scaling. Defaults to None. 433 434 Returns: 435 list: List of circles forming a flower-like pattern. 436 437 Raises: 438 ValueError: If n is less than 8. 439 """ 440 if n<8: 441 raise ValueError('n must be greater than 7') 442 if ratio is None: 443 if n<21: 444 ratio = ratios[n] 445 else: 446 ratio = (-0.000000089767 * n**4 + 0.000015821834 * n**3 + 447 -0.001100867708 * n **2+ 0.038096046379 * n + 0.327363569038) 448 449 r1 = side_len_to_radius(n, 2*radius) 450 circles = Circle((r1, 0), radius).rotate(pi/(n/2), (0, 0), reps=n-1) 451 xform = scale_matrix(ratio) @ rotation_matrix(pi/n) 452 453 return circles.transform(xform_matrix=xform, reps=layers)
Steiner chain. Return a list of circles that form a flower-like pattern.
Arguments:
- n (int): Number of circles.
- radius (float, optional): Radius of the circles. Defaults to 25.
- layers (int, optional): Number of layers. Defaults to 6.
- ratio (float, optional): Ratio for scaling. Defaults to None.
Returns:
list: List of circles forming a flower-like pattern.
Raises:
- ValueError: If n is less than 8.