58 lines
1.8 KiB
Python
58 lines
1.8 KiB
Python
"""Module for performing tutte embeddings"""
|
|
from typing import Iterable, cast, Any
|
|
from sage.all import vector, Graph, cos, pi, sin # type: ignore
|
|
|
|
|
|
def create_tutte_embedding(
|
|
graph: Graph,
|
|
outer_face: list[Any],
|
|
max_iter: int=50,
|
|
set_pos: bool = True) -> dict[Any, Any]:
|
|
"""
|
|
Performs a tutte force embedding with a prescribed outer face on
|
|
a given sage graph.
|
|
|
|
For a description on tutte embeddings, see the video [here](\
|
|
https://www.youtube.com/watch?v=mEzPPMhR8XE).
|
|
"""
|
|
radius = 1000
|
|
pos: dict[Any, Any] = {}
|
|
num_outer_points = len(outer_face)
|
|
for i, v in enumerate(outer_face):
|
|
pos[v] = (
|
|
radius * cos((i / num_outer_points) * 2 * pi),
|
|
radius * sin((i / num_outer_points) * 2 * pi)
|
|
)
|
|
|
|
# start off setting all other points to (0, 0)
|
|
num_inner_points = cast(int, graph.order()) - num_outer_points
|
|
|
|
for i, v in enumerate(cast(Iterable[Any], graph.vertices())): # type: ignore
|
|
if v in pos:
|
|
continue
|
|
pos[v] = (
|
|
radius/3 * cos((i / num_inner_points) * 2 * pi),
|
|
radius/3 * sin((i / num_inner_points) * 2 * pi)
|
|
)
|
|
|
|
i = 0
|
|
while i < max_iter:
|
|
for v in cast(Iterable[Any], graph.vertices()): # type: ignore
|
|
if v in outer_face:
|
|
continue
|
|
neighbors = cast(list[Any], graph.neighbors(v)) # type: ignore
|
|
deg = len(neighbors)
|
|
x_desired = sum(map(lambda n: pos[n][0], neighbors)) / deg
|
|
y_desired = sum(map(lambda n: pos[n][1], neighbors)) / deg
|
|
pos[v] = tuple(
|
|
vector(pos[v]) - vector([ # type: ignore
|
|
pos[v][0] - x_desired, pos[v][1] - y_desired
|
|
])
|
|
)
|
|
i += 1
|
|
|
|
if set_pos:
|
|
graph.set_pos(pos) # type: ignore
|
|
|
|
return pos
|