def plot_cell_mayavi(lines, boundary=None, clf=True, smooth=True, min_seg_length=5, **kwargs): import mayavi.mlab as may may.clf() hues = n.linspace(0, 1, len(lines) + 1)[:-1] colours = [hsv_to_rgb(hue, 1, 1) for hue in hues] random.shuffle(colours) i = 0 for (line, colour) in zip(lines, colours): vprint('Plotting line {} / {}\r'.format(i, len(lines) - 1), False) i += 1 for segment in line: if len(segment) < min_seg_length: continue plot_line(segment, mode='mayavi', clf=False, color=colour, **kwargs) if boundary is not None: draw_bounding_box_mayavi(boundary, **kwargs)
def octree_simplify(self, runs=1, plot=False, rotate=True, obey_knotting=False, **kwargs): ''' Simplifies the curves via the octree reduction of :module:`pyknotid.simplify.octree`. Parameters ---------- runs : int The number of times to run the octree simplification. Defaults to 1. plot : bool Whether to plot the curve after each run. Defaults to False. rotate : bool Whether to rotate the space curve before each run. Defaults to True as this can make things much faster. obey_knotting : bool Whether to not let the line pass through itself. Defaults to False - knotting of individual components will be ignored! This is *much* faster than the alternative. kwargs are passed to the :class:`pyknotid.simplify.octree.OctreeCell` constructor. ''' from ..simplify.octree import OctreeCell, remove_nearby_points for line in self.lines: line.points = remove_nearby_points(line.points) for i in range(runs): if n.sum([len(knot.points) for knot in self.lines]) > 30: vprint( '\rRun {} of {}, {} points remain'.format( i, runs, len(self)), False, self.verbose) if rotate: rot_mat = get_rotation_matrix(n.random.random(3)) for line in self.lines: line._apply_matrix(rot_mat) oc = OctreeCell.from_lines([line.points for line in self.lines], **kwargs) oc.simplify(obey_knotting) self._recent_octree = oc self.lines = [Knot(line) for line in oc.get_lines()] for line in self.lines: line.points = remove_nearby_points(line.points) if rotate: for line in self.lines: line._apply_matrix(rot_mat.T) if plot: self.plot() vprint('\nReduced to {} points'.format(len(self)))
def plot_cell_vispy(lines, boundary=None, clf=True, colours=None, randomise_colours=True, tube_radius=1., **kwargs): if clf: clear_vispy_canvas() if colours is None: hues = n.linspace(0, 1, len(lines) + 1)[:-1] colours = [hsv_to_rgb(hue, 1, 1) for hue in hues] if randomise_colours: random.shuffle(colours) elif not isinstance(colours, (list, tuple)): colours = [colours for _ in lines] i = 0 segments = [] segment_colours = [] segment_radii = [] if not isinstance(tube_radius, list): tube_radius = [tube_radius for _ in range(len(lines))] for (line, colour, radius) in zip(lines, colours, tube_radius): vprint('Plotting line {} / {}\r'.format(i, len(lines) - 1), False) i += 1 if len(colour) == 4 and colour[-1] == 0: continue for segment in line: if len(segment) < 4: continue segments.append(segment) segment_colours.append(colour) segment_radii.append(radius) # plot_line_vispy(segment, # clf=False, colour=colour, **kwargs) plot_lines_vispy(segments, colours=segment_colours, tube_radius=segment_radii, clf=clf, **kwargs) if boundary is not None: draw_bounding_box_vispy(boundary, tube_radius=tube_radius[0])
def writhing_numbers(gc, diagrams, based=False): '''Returns the signed sum of representations of the given Arrow diagrams in the given representation. Parameters ---------- gc : A :class:`~pyknotid.representations.gausscode.GaussCode` or equivalent representation. The knot for which to find the writhes. diagrams : str or list or tuple A list of strings, or single string, representing Arrow diagrams, e.g. '1-2+1+2-' for Vassiliev 2. based : bool Whether the diagrams have basepoints (if True, assumed to be just before the first entry). ''' if not isinstance(diagrams, (list, tuple)): diagrams = [diagrams] multipliers = defaultdict(lambda: {}) use_multipliers = False if any(['d' in diagram for diagram in diagrams]): use_multipliers = True if use_multipliers: for di, diagram in enumerate(diagrams): terms = diagram.split(',') for term in terms: if term[-1] == 'd': multiplier = 2 else: multiplier = 1 term = int(term[:-2]) if 'd' in term else int(term[:-1]) multipliers[diagram.replace('d', '')][term] = multiplier diagrams[di] = diagram.replace('d', '') for d in diagrams: validate_diagram(d) level = 0 code = gc._gauss_code code = code[0] gc_len = len(gc) code_len = len(code) from pyknotid.invariants import _crossing_arrows_and_signs arrows, signs = _crossing_arrows_and_signs(code, gc.crossing_numbers) crossing_numbers = list(gc.crossing_numbers) # degrees = [len(diagram.split(',')) for diagram in diagrams] degrees = defaultdict(lambda: []) for diagram in diagrams: degrees[len(diagram.split(',')) // 2].append(diagram) relations = {diagram: [] for diagram in diagrams} for diagram in diagrams: degree = len(diagram.split(',')) // 2 num_relations = factorial(degree - 1) * 4 terms = diagram.split(',') numbers = [term[:-1] for term in terms] number_strs = list(sorted(set(numbers), key=lambda j: int(j))) for i, number in enumerate(number_strs): for oi, other_number in enumerate(number_strs[i + 1:]): oi += i + 1 if i != 0: if terms.index(number + '-') < terms.index(other_number + '-'): relations[diagram].append( lambda l, i=i, oi=oi: l[i][0] < l[oi][0]) else: relations[diagram].append( lambda l, i=i, oi=oi: l[i][0] > l[oi][0]) if terms.index(number + '-') < terms.index(other_number + '+'): relations[diagram].append( lambda l, i=i, oi=oi: l[i][0] < l[oi][1]) else: relations[diagram].append( lambda l, i=i, oi=oi: l[i][0] > l[oi][1]) if terms.index(number + '+') < terms.index(other_number + '-'): relations[diagram].append( lambda l, i=i, oi=oi: l[i][1] < l[oi][0]) else: relations[diagram].append( lambda l, i=i, oi=oi: l[i][1] > l[oi][0]) # This one seems to not be necessary for diagrams where all arrows cross? if terms.index(number + '+') < terms.index(other_number + '+'): relations[diagram].append( lambda l, i=i, oi=oi: l[i][1] < l[oi][1]) else: relations[diagram].append( lambda l, i=i, oi=oi: l[i][1] > l[oi][1]) if i == 0: continue if terms.index(number + '+') < terms.index(number + '-'): relations[diagram].append( lambda l, i=i, oi=oi: l[i][1] < l[i][0]) else: relations[diagram].append( lambda l, i=i, oi=oi: l[i][1] > l[i][0]) max_degree = max(degrees.keys()) representations_sums = {d: 0 for d in diagrams} used_sets = {d: set() for d in diagrams} combs = combinations(crossing_numbers, max_degree) try: num_combs = (factorial(len(crossing_numbers)) // factorial(max_degree) // factorial(len(crossing_numbers) - max_degree)) except ValueError: num_combs = 0 strs = [None for _ in range(max_degree)] * 2 order = [None for _ in range(max_degree)] * 2 for ci, comb in enumerate(combs): if ci % 10000 == 0: vprint('\rCombination {} of {} '.format(ci + 1, num_combs), newline=False, condition=(ci % 10000) == 0) if based: perms = [comb] else: perms = permutations(comb) ordered_indices = tuple(sorted(comb)) for diagram in diagrams: if ordered_indices not in used_sets[diagram]: break else: continue for perm in perms: cur_arrows = [list(arrows[i]) for i in perm] a1s = cur_arrows[0][0] if based: a1s = 0 for i, arrow in enumerate(cur_arrows): arrow[0] = (arrow[0] - a1s) % code_len arrow[1] = (arrow[1] - a1s) % code_len for diagram in diagrams: if ordered_indices in used_sets[diagram]: continue for relation in relations[diagram]: if not relation(cur_arrows): break else: if use_multipliers: representations_sums[diagram] += (reduce( lambda x, y: x * y, [ signs[arrow_i]**multipliers[diagram][num + 1] for num, arrow_i in enumerate(perm) ])) else: representations_sums[diagram] += (reduce( lambda x, y: x * y, [signs[arrow_i] for arrow_i in perm])) used_sets[diagram].add(ordered_indices) vprint() return representations_sums
def _vprint(self, s, newline=True): '''Prints s, with optional newline. Intended for internal use in displaying progress.''' if self.verbose: vprint(s, newline)
def plot_sphere_mollweide_vispy(func, circle_points=50, depth=2, edge_color=None, cmap='hsv', smooth=0, mesh='circles', **kwargs): '''func must be a function of sphere angles theta, phi''' # from vispy.scene import Sphere, ArcballCamera from vispy.scene import TurntableCamera, Mesh if mesh == 'circles': vertices, indices = circles_ellipse_mesh(circle_points, depth) else: vertices, indices = recursive_ellipse_mesh(circle_points, depth) vertices[:, 0] *= 2 * n.sqrt(2) vertices[:, 1] *= n.sqrt(2) mesh = Mesh(vertices, indices) md = mesh._meshdata vertices = md.get_vertices() values = n.zeros(len(vertices)) print('pre') thetas = [] phis = [] for i, vertex in enumerate(vertices): if i % 10 == 0: vprint('\ri = {} / {}'.format(i, len(vertices)), newline=False) intermediate = n.arcsin(vertex[1] / n.sqrt(2)) theta = n.arcsin((2 * intermediate + n.sin(2 * intermediate)) / n.pi) phi = n.pi * vertex[0] / (2 * n.sqrt(2) * n.cos(intermediate)) # theta = n.arccos(vertex[2]) # phi = n.arctan2(vertex[1], vertex[0]) if n.isnan(theta): theta = 0.0 print('theta', vertex) if n.isnan(phi): phi = 0.0 print('phi', vertex) thetas.append(theta) phis.append(phi) values[i] = func(theta + n.pi / 2, phi + n.pi) vprint() print('thetas', n.min(thetas), n.max(thetas)) print('phis', n.min(phis), n.max(phis)) colours = n.zeros((len(values), 4)) max_val = n.max(values) min_val = n.min(values) unique_values = n.unique(colours) max_val += (1. + 1. / len(unique_values)) * (max_val - min_val) diff = (max_val - min_val) import matplotlib.pyplot as plt cm = plt.get_cmap(cmap) for i in range(len(colours)): colours[i] = cm(((values[i] - min_val) / diff)) colours[:, -1] = 1. faces = md.get_faces() for si in range(smooth): new_colours = [[n.array(row) for _ in range(3)] for row in colours] for i, face in enumerate(faces): new_colours[face[0]].append(colours[face[1]]) new_colours[face[0]].append(colours[face[2]]) new_colours[face[1]].append(colours[face[0]]) new_colours[face[1]].append(colours[face[2]]) new_colours[face[2]].append(colours[face[0]]) new_colours[face[2]].append(colours[face[1]]) new_colours = n.array([n.average(cs, axis=0) for cs in new_colours]) colours = new_colours md.set_vertex_colors(colours) ensure_vispy_canvas() clear_vispy_canvas() vispy_canvas.view.camera = TurntableCamera(fov=0, elevation=90.) vispy_canvas.view.add(mesh) vispy_canvas.show()
def plot_sphere_shell_vispy(func, rows=100, cols=100, radius=1., opacity=1., translation=(0., 0., 0.), method='latitude', edge_color=None, cmap='hsv', smooth=0, cutoff=0.4, cutoff_max=0.8, transparent_side=True, **kwargs): '''func must be a function of sphere angles theta, phi''' from vispy.scene import Sphere, ArcballCamera s = Sphere(rows=rows, cols=cols, method=method, edge_color=edge_color, radius=radius) mesh = s.mesh md = mesh._meshdata vertices = md.get_vertices() md.set_vertices(vertices + n.array(translation)) values = n.zeros(len(vertices)) opacities = n.ones(len(vertices)) print('pre') for i, vertex in enumerate(vertices): if i % 10 == 0: vprint('\ri = {} / {}'.format(i, len(vertices)), newline=False) vertex = vertex / n.sqrt(n.sum(vertex * vertex)) theta = n.arccos(vertex[2]) phi = n.arctan2(vertex[1], vertex[0]) if n.isnan(theta): theta = 0.0 values[i] = func(theta, phi) distance = vertex[1] - cutoff distance_frac = distance / (cutoff_max - cutoff) opacity = max(0, 1. - distance_frac) opacities[i] = 1. if vertex[1] < cutoff else opacity vprint() colours = n.zeros((len(values), 4)) max_val = n.max(values) min_val = n.min(values) unique_values = n.unique(colours) # max_val += (1. + 1./len(unique_values))*(max_val - min_val) diff = (max_val - min_val) import matplotlib.pyplot as plt cm = plt.get_cmap(cmap) for i in range(len(colours)): colours[i] = cm(((values[i] - min_val) / diff)) colours[:, -1] = opacity faces = md.get_faces() for si in range(smooth): new_colours = [[n.array(row) for _ in range(3)] for row in colours] for i, face in enumerate(faces): new_colours[face[0]].append(colours[face[1]]) new_colours[face[0]].append(colours[face[2]]) new_colours[face[1]].append(colours[face[0]]) new_colours[face[1]].append(colours[face[2]]) new_colours[face[2]].append(colours[face[0]]) new_colours[face[2]].append(colours[face[1]]) new_colours = n.array([n.average(cs, axis=0) for cs in new_colours]) colours = new_colours colours = [(c[0], c[1], c[2], o) for c, o in zip(colours, opacities)] md.set_vertex_colors(colours) ensure_vispy_canvas() vispy_canvas.view.camera = ArcballCamera(fov=30) vispy_canvas.view.add(s) vispy_canvas.show() return colours
def plot_sphere_lambert_sharp_vispy(func, circle_points=50, depth=2, output_size=500, edge_color=None, cmap='brg', smooth=0, mesh='circles', **kwargs): '''func must be a function of sphere angles theta, phi''' from vispy.scene import TurntableCamera, Mesh if mesh == 'circles': vertices, indices = circles_ellipse_mesh(circle_points, depth) else: vertices, indices = recursive_ellipse_mesh(circle_points, depth) vertices[:, 0] *= 2 vertices[:, 1] *= 2 mesh = Mesh(vertices, indices) md = mesh._meshdata vertices = md.get_vertices() values = n.zeros(len(vertices)) print('pre') thetas = [] phis = [] for i, vertex in enumerate(vertices): if i % 10 == 0: vprint('\ri = {} / {}'.format(i, len(vertices)), newline=False) theta = 2 * np.arccos(np.sqrt(vertex[0]**2 + vertex[1]**2) / 2.) phi = np.arctan2(vertex[1], vertex[0]) if n.isnan(theta): theta = 0.0 print('theta', vertex) if n.isnan(phi): phi = 0.0 print('phi', vertex) thetas.append(theta) phis.append(phi) values[i] = func(theta + n.pi / 2, phi + n.pi) vprint() return thetas, phis, values import svgwrite as svg d = svg.Drawing() import matplotlib.pyplot as plt cmap = plt.get_cmap(cmap) max_value = n.max(values) min_value = n.min(values) def normalise(v): return (v - min_value) / (max_value - min_value) for tri_i, triangle in enumerate(indices): v1 = vertices[triangle[0]] v2 = vertices[triangle[1]] v3 = vertices[triangle[2]] c1 = values[triangle[0]] c2 = values[triangle[1]] c3 = values[triangle[2]] offset_x = 2.001412 offset_y = 2.0214 v1_x = (v1[0] + offset_x) / 4. * output_size v1_y = (v1[1] + offset_y) / 4. * output_size v2_x = (v2[0] + offset_x) / 4. * output_size v2_y = (v2[1] + offset_y) / 4. * output_size v3_x = (v3[0] + offset_x) / 4. * output_size v3_y = (v3[1] + offset_y) / 4. * output_size c_x = n.average([v1_x, v2_x, v3_x]) c_y = n.average([v1_y, v2_y, v3_y]) v12_x = (v1_x + v2_x) / 2. v12_y = (v1_y + v2_y) / 2. v13_x = (v1_x + v3_x) / 2. v13_y = (v1_y + v3_y) / 2. v23_x = (v2_x + v3_x) / 2. v23_y = (v2_y + v3_y) / 2. cmap_1 = cmap(normalise(c1)) rgb_cmap1 = [int(c * 255) for c in cmap_1[:3]] d.add( d.polygon( [[v1_x, v1_y], [v12_x, v12_y], [c_x, c_y], [v13_x, v13_y]], fill='rgb({},{},{})'.format(*rgb_cmap1), stroke='rgb({},{},{})'.format(*rgb_cmap1), stroke_width='0.5')) cmap_2 = cmap(normalise(c2)) rgb_cmap2 = [int(c * 255) for c in cmap_2[:3]] d.add( d.polygon( [[v2_x, v2_y], [v12_x, v12_y], [c_x, c_y], [v23_x, v23_y]], fill='rgb({},{},{})'.format(*rgb_cmap2), stroke='rgb({},{},{})'.format(*rgb_cmap2), stroke_width='0.5')) cmap_3 = cmap(normalise(c3)) rgb_cmap3 = [int(c * 255) for c in cmap_3[:3]] d.add( d.polygon( [[v3_x, v3_y], [v13_x, v13_y], [c_x, c_y], [v23_x, v23_y]], fill='rgb({},{},{})'.format(*rgb_cmap3), stroke='rgb({},{},{})'.format(*rgb_cmap3), stroke_width='0.5')) # print('cols', c1, cmap_1, c2, cmap_2, c3, cmap_3) return d