1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
from __future__ import division
import numpy as np
from math import gcd
class TorusKnot(object):
"""Representation of a torus knot or link.
A torus knot is one that can be drawn on the surface of a
torus. It is parameterised by two integers p and q as below; in
fact this returns a single knot (a single curve) only if p and q
are coprime, otherwise it describes multiple linked curves.
Parameters
----------
p : int
The number of times the knot winds around the outside of the
torus. Defaults to 2.
q : int
The number of times the knot passes through the hole in the
centre of the torus. Defaults to 3.
num_points : int
The number of points in the returned piecewise linear
curve. If there are multiple curves (i.e. a torus link), this
is the number of points in *each* curve. Defaults to 100.
major_radius : float
Distance from the center of the torus tube to the center of the torus.
Defaults to 10.
minor_radius : float
The radius of the torus tube. Defaults to 5.
"""
def __init__(self, p=3, q=2, num_points=100, major_radius=10.,
minor_radius=5.):
self._p = p
self._q = q
self._num_points = num_points
self._major_radius = major_radius
self._minor_radius = minor_radius
self._calculate_vertices()
def _calculate_vertices(self):
angles = np.linspace(0, 2*np.pi, self._num_points)
num_components = self.num_components
divisions = (np.max([self._q, self._p]) *
np.min([self._q, self._p]) // self.num_components)
starting_angles = np.linspace(
0, 2*np.pi, divisions + 1)[
:num_components]
q = self._q / num_components
p = self._p / num_components
components = []
for starting_angle in starting_angles:
vertices = np.zeros((self._num_points, 3))
local_angles = angles + starting_angle
radii = (self._minor_radius * np.cos(q * angles) +
self._major_radius)
vertices[:, 0] = radii * np.cos(p * local_angles)
vertices[:, 1] = radii * np.sin(p * local_angles)
vertices[:, 2] = (self._minor_radius * -1 *
np.sin(q * angles))
components.append(vertices)
self._components = components
@property
def first_component(self):
"""The vertices of the first component line of the torus knot or link."""
return self._components[0]
@property
def components(self):
"""A list of the vertices in each line of the torus knot or link.
Even if p and q are coprime, this is a list with just one
entry.
"""
return self._components
@property
def num_components(self):
"""The number of component lines in the torus link. This is equal
to the greatest common divisor of p and q.
"""
return gcd(self._p, self._q)
@property
def q(self):
"""The q parameter of the torus knot or link."""
return self._q
@q.setter
def q(self, q):
self._q = q
self._calculate_vertices()
@property
def p(self):
"""The p parameter of the torus knot or link."""
return self._p
@p.setter
def p(self, p):
self._p = p
self._calculate_vertices()
@property
def minor_radius(self):
"""The minor radius of the torus."""
return self._minor_radius
@minor_radius.setter
def minor_radius(self, r):
self._minor_radius = r
self._calculate_vertices()
@property
def major_radius(self):
"""The major radius of the torus."""
return self._major_radius
@major_radius.setter
def major_radius(self, r):
self._major_radius = r
self._calculate_vertices()
@property
def num_points(self):
"""The number of points in the vertices returned for each knot/link
component
"""
return self._num_points
@num_points.setter
def num_points(self, r):
self._num_points = r
self._calculate_vertices()
|