Skip to content

Commit

Permalink
Added presets and updated examples.
Browse files Browse the repository at this point in the history
  • Loading branch information
Yatoom committed Mar 21, 2021
1 parent fa58b9e commit 04dc4fe
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 30 deletions.
63 changes: 63 additions & 0 deletions examples/observers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import os

from voronoi import Polygon, Voronoi, VoronoiObserver, TreeObserver, DebugObserver
from voronoi.visualization import Presets

# Define some points (a.k.a sites or cell points)
points = [
(2.5, 2.5), (4, 7.5), (7.5, 2.5), (6, 7.5), (4, 4), (3, 3), (6, 3)
]

# Define a bounding box / polygon
polygon = Polygon([
(2.5, 10), (5, 10), (10, 5), (10, 2.5), (5, 0), (2.5, 0), (0, 2.5), (0, 5)
])

# Initialize the algorithm
v = Voronoi(polygon)

# Attach a Voronoi observer that visualizes the Voronoi diagram every step
v.attach_observer(
VoronoiObserver(

# Settings to pass into the visualizer's plot_all() method.
# - By default, the observer uses a set of minimalistic presets that are useful for visualizing during
# construction, clipping and the final result. Have a look at Presets.construction, Presets.clipping and
# Presets.final.
# - These settings below will update the default presets used by the observer. For example, by default,
# the arc_labels are not shown, but below we can enable the arc labels. Other parameters can be found in
# the visualizer's plot_all() method.
settings=dict(arc_labels=True, site_labels=True),

# Callback that saves the figure every step
# If no callback is provided, it will simply display the figure in a matplotlib window
callback=lambda observer, figure: figure.savefig(f"output/voronoi/{observer.n_messages:02d}.png")
)
)

# Attach observer that visualizes the tree every step. This is a binary tree data structure that keeps track of where
# the arcs and the breakpoints between the arcs are going.
v.attach_observer(
TreeObserver(
# Callback that saves the figure every step
# If no callback is provided, it will render the figure in a window
callback=lambda observer, dot: dot.render(f"output/tree/{observer.n_messages:02d}")
)
)

# Attach a listener that listens to debug messages.
# If no callback is provided, it will print the messages.
v.attach_observer(DebugObserver(callback=lambda _: print(_)))

# Create the output directory if it doesn't exist
if not os.path.exists("output"):
os.mkdir("output")

if not os.path.exists("output/tree/"):
os.mkdir("output/tree/")

if not os.path.exists("output/voronoi/"):
os.mkdir("output/voronoi/")

# Create the Voronoi diagram
v.create_diagram(points=points)
36 changes: 21 additions & 15 deletions examples/quickstart.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import List
from voronoi import Voronoi, Polygon, Visualizer, Point
from voronoi import Voronoi, Polygon, Visualizer, Point, VoronoiObserver
from voronoi.graph import HalfEdge, Vertex

# Define some points (a.k.a sites or cell points)
Expand All @@ -15,12 +15,18 @@
# Initialize the algorithm
v = Voronoi(polygon)

# Optional: visualize the voronoi diagram at every step.
# You can find more information in the observers.py example file
# v.attach_observer(
# VoronoiObserver()
# )

# Create the Voronoi diagram
v.create_diagram(points=points)

# Visualize the Voronoi diagram
Visualizer(v) \
.plot_sites(show_labels=True) \
.plot_sites(show_labels=False) \
.plot_edges(show_labels=False) \
.plot_vertices() \
.show()
Expand All @@ -33,23 +39,23 @@
edge, vertex, site = edges[0], vertices[0], sites[0]

# Edge operations
origin: Vertex = edge.origin # The vertex in which the edge originates
target: Vertex = edge.twin.origin # The twin is the edge that goes in the other direction
target_alt: Vertex = edge.target # Same as above, but more convenient
twin: HalfEdge = edge.twin # Get the twin of this edge
next: HalfEdge = edge.next # Get the next edge
prev: HalfEdge = edge.twin.next # Get the previous edge
prev_alt: HalfEdge = edge.prev # Same as above, but more convenient
origin: Vertex = edge.origin # The vertex in which the edge originates
target: Vertex = edge.twin.origin # The twin is the edge that goes in the other direction
target_alt: Vertex = edge.target # Same as above, but more convenient
twin: HalfEdge = edge.twin # Get the twin of this edge
next_edge: HalfEdge = edge.next # Get the next edge
prev_edge: HalfEdge = edge.twin.next # Get the previous edge
prev_alt: HalfEdge = edge.prev # Same as above, but more convenient

# Site operations
size: float = site.area() # The area of the cell
borders: List[HalfEdge] = site.borders() # A list of all the borders that surround this cell point
vertices: List[Vertex] = site.vertices() # A list of all the vertices around this cell point
site_x: float = site.x # X-coordinate of the site
site_xy: [float, float] = site.xy # (x, y)-coordinates of the site
size: float = site.area() # The area of the cell
borders: List[HalfEdge] = site.borders() # A list of all the borders that surround this cell point
vertices: List[Vertex] = site.vertices() # A list of all the vertices around this cell point
site_x: float = site.x # X-coordinate of the site
site_xy: [float, float] = site.xy # (x, y)-coordinates of the site
first_edge: HalfEdge = site.first_edge # Points to the first edge that is part of the border around the site

# Vertex operations
connected_edges: List[HalfEdge] = vertex.connected_edges # A list of all edges that are connected to this vertex
vertex_x: float = vertex.x # x-coordinate of the vertex
vertex_xy: [float, float] = vertex.xy # (x, y)-coordinates of the vertex
print()
2 changes: 1 addition & 1 deletion voronoi/graph/half_edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __repr__(self):

def set_next(self, next):
if next:
next.prev = self
next.prev_edge = self
self.next = next

def get_origin(self, y=None, max_y=None):
Expand Down
10 changes: 5 additions & 5 deletions voronoi/observers/voronoi_observer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
from voronoi.observers.observer import Observer
import matplotlib.pyplot as plt

from voronoi.visualization.visualizer import Visualizer
from voronoi.visualization.visualizer import Visualizer, Presets


class VoronoiObserver(Observer, ABC):
def __init__(self, visualize_steps=True, visualize_before_clipping=False, visualize_result=True, callback=None,
figsize=(8, 8), canvas_offset=5, settings=None):
figsize=(8, 8), canvas_offset=1, settings=None):
self.canvas_offset = canvas_offset
self.figsize = figsize
self.visualize_steps = visualize_steps
Expand All @@ -28,20 +28,20 @@ def update(self, subject: Algorithm, message: Message, **kwargs):

if message == Message.STEP_FINISHED and self.visualize_steps:
vis = Visualizer(subject, canvas_offset=self.canvas_offset)
settings = dict(outgoing_edges=False)
settings = Presets.construction
settings.update(self.settings)
assert subject.sweep_line == subject.event.yd
result = vis.plot_all(**settings)
plt.title(str(subject.event) + "\n")
elif message == Message.SWEEP_FINISHED and self.visualize_before_clipping:
vis = Visualizer(subject, canvas_offset=self.canvas_offset)
settings = dict(events=False, beach_line=False, outgoing_edges=False)
settings = Presets.clipping
settings.update(self.settings)
result = vis.plot_all(**settings)
plt.title("Sweep finished\n")
elif message == Message.VORONOI_FINISHED and self.visualize_result:
vis = Visualizer(subject, canvas_offset=self.canvas_offset)
settings = dict(events=False, outgoing_edges=False, arcs=False, beach_line=False, sweep_line=False)
settings = Presets.final
settings.update(self.settings)
result = vis.plot_all(**settings)
plt.title("Voronoi completed\n")
Expand Down
3 changes: 2 additions & 1 deletion voronoi/visualization/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from voronoi.visualization.visualizer import Visualizer
from voronoi.visualization.visualizer import Visualizer
from voronoi.visualization.visualizer import Presets
31 changes: 23 additions & 8 deletions voronoi/visualization/visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ class Colors:
FIRST_EDGE = "#2ecc71"


class Presets:
# A minimalistic preset that is useful during construction
construction = dict(polygon=True, events=True, beach_line=True, sweep_line=True)

# A minimalistic preset that is useful during clipping
clipping = dict(polygon=True)

# A minimalistic preset that is useful to show the final result
final = dict()


class Visualizer:

def __init__(self, voronoi, canvas_offset=1, figsize=(8, 8)):
Expand All @@ -52,9 +63,10 @@ def show(self, block=True, **kwargs):
plt.show(block=block, **kwargs)
return self

def plot_all(self, polygon=True, edges=True, vertices=True, sites=True,
outgoing_edges=False, events=True, beach_line=True, arcs=True, border_to_site=False, scale=1,
edge_labels=True, site_labels=True, triangles=False, sweep_line=True, arc_labels=True):
def plot_all(self, polygon=False, edges=True, vertices=True, sites=True,
outgoing_edges=False, border_to_site=False, scale=1,
edge_labels=False, site_labels=False, triangles=False, arcs=False, sweep_line=False, events=False,
arc_labels=False, beach_line=False):

self.plot_sweep_line() if sweep_line else False
self.plot_polygon() if polygon else False
Expand All @@ -72,7 +84,8 @@ def plot_polygon(self):
if hasattr(self.voronoi.bounding_poly, 'radius'):
# Draw bounding box
self.canvas.add_patch(
patches.Circle((self.voronoi.bounding_poly.xd, self.voronoi.bounding_poly.xd), self.voronoi.bounding_poly.radius,
patches.Circle((self.voronoi.bounding_poly.xd, self.voronoi.bounding_poly.xd),
self.voronoi.bounding_poly.radius,
fill=False,
edgecolor=Colors.BOUNDING_BOX)
)
Expand All @@ -95,8 +108,9 @@ def plot_vertices(self, vertices=None, **kwargs):

return self

def plot_outgoing_edges(self, vertices=None, scale=1, **kwargs):
def plot_outgoing_edges(self, vertices=None, scale=0.5, **kwargs):
vertices = vertices or self.voronoi.vertices
scale = Decimal(str(scale))

for vertex in vertices:
for edge in vertex.connected_edges:
Expand All @@ -116,8 +130,9 @@ def plot_outgoing_edges(self, vertices=None, scale=1, **kwargs):
direction = (x_diff / length, y_diff / length)
new_end = Coordinate(start.xd + direction[0] * scale, start.yd + direction[1] * scale)

props = dict(arrowstyle="->", color=Colors.EDGE_DIRECTION, linewidth=1, **kwargs)
self.canvas.annotate(text='', xy=(new_end.xd, new_end.yd), xytext=(start.xd, start.yd), arrowprops=props)
props = dict(arrowstyle="->", color=Colors.EDGE_DIRECTION, linewidth=3, **kwargs)
self.canvas.annotate(text='', xy=(new_end.xd, new_end.yd), xytext=(start.xd, start.yd),
arrowprops=props)

return self

Expand All @@ -133,7 +148,7 @@ def plot_sites(self, points=None, show_labels=True, color=Colors.CELL_POINTS, zo
# Add descriptions
if show_labels:
for point in points:
self.canvas.text(point.xd, point.yd, s=f"{point} (A={point.area(digits=2)})", zorder=15)
self.canvas.text(point.xd, point.yd, s=f"P{point.name if point.name is not None else ''}", zorder=15)

return self

Expand Down

0 comments on commit 04dc4fe

Please sign in to comment.