Module specrel.graphics.basegraph
Basic graphics and base classes for spacetime plots and animations in special relativity.
Source code
"""Basic graphics and base classes for spacetime plots and animations in
special relativity."""
from abc import ABC, abstractmethod
import math
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from specrel.graphics import graphrc
class FigureCreator:
"""Something that can create and own a figure.
Attributes:
fig (matplotlib.figure.Figure): Figure handle.
"""
def __init__(self):
self.fig = None
self._created_own_fig = False
def close(self):
"""Close the figure window if it was created by the class."""
if self._created_own_fig:
plt.close(self.fig)
class SingleAxisFigureCreator(FigureCreator):
"""Something that creates and owns a figure with a single axis
(i.e. not a subplot).
Args:
fig (matplotlib.figure.Figure, optional): Figure window. If `None` and
`ax` is not `None`, will be set to `ax.figure`. If both are `None`,
a new figure will be created.
ax (matplotlib.axes.Axes, optional): Axes to draw on. If `None` and
`fig` is not `None`, will be set to `fig.gca()`. If both are `None`,
a new set of axes will be created.
Attributes:
ax (matplotlib.axes.Axes): Axes to draw on.
fig (matplotlib.figure.Figure): Figure window.
"""
def __init__(self, fig=graphrc['fig'], ax=graphrc['ax']):
super().__init__()
if fig is None and ax is None:
self.fig, self.ax = plt.subplots()
self._created_own_fig = True
elif fig is not None and ax is None:
self.fig = fig
self.ax = fig.gca()
elif fig is None and ax is not None:
self.fig = ax.figure
self.ax = ax
else:
self.fig = fig
self.ax = ax
class STPlotter(SingleAxisFigureCreator, ABC):
"""Base-level plotting interface."""
@abstractmethod
def draw_point(self, point, tag, **kwargs):
"""Draws a single spacetime point.
Args:
point (specrel.geom.STVector): Point to draw.
tag (str): Tag to draw with the point.
**kwargs: Matplotlib plot keyword arguments.
Raises:
NotImplementedError:
Abstract method.
"""
raise NotImplementedError
@abstractmethod
def draw_line_segment(self, point1, point2, tag, **kwargs):
"""Draws a line segment between two spacetime points.
Args:
point1 (specrel.geom.STVector): One line segment endpoint.
point2 (specrel.geom.STVector): Other line segment endpoint.
tag (str): Tag to draw with the point.
**kwargs: Matplotlib plot keyword arguments.
Raises:
NotImplementedError:
Abstract method.
"""
raise NotImplementedError
@abstractmethod
def draw_shaded_polygon(self, vertices, tag, **kwargs):
"""Draws a shaded polygon in spacetime.
Args:
vertices (list): List of `specrel.geom.STVector` objects defining
the polygon vertices.
tag (str): Tag to draw with the point.
**kwargs: Matplotlib plot keyword arguments.
Raises:
NotImplementedError:
Abstract method.
"""
raise NotImplementedError
@staticmethod
def _decouple_stvectors(stvectors):
"""Separate time and space values in a list of STVectors."""
if stvectors:
return tuple(zip(*stvectors))
return tuple(), tuple()
@abstractmethod
def set_lims(self, tlim, xlim):
"""Sets the plotting limits for the plotter.
Args:
tlim (tuple): Minimum and maximum time values of the plot. `None`
values are filled automatically.
xlim (tuple): Minimum and maximum position values of the plot.
`None` values are filled automatically.
Raises:
NotImplementedError:
Abstract method.
"""
raise NotImplementedError
@abstractmethod
def show(self):
"""Shows the plot in interactive mode.
Raises:
NotImplementedError:
Abstract method.
"""
raise NotImplementedError
def save(self, filename, **kwargs):
"""Saves the plot to a file.
Args:
filename (str): Output file name.
**kwargs: Keyword arguments to `matplotlib.figure.Figure.savefig`.
"""
self.fig.savefig(filename, **kwargs)
class BaseAnimator(ABC):
"""Base class for animators that animate over some changing control
variable, like time or velocity.
Args:
frame_lim (tuple, optional): Minimum and maximum frame index of the
animation. See `BaseAnimator.calc_frame_idx`.
others: See attributes.
Attributes:
display_current (bool): Flag for displaying the control variable value
of the current animation frame.
display_current_decimals (int): Number of decimals to display the
current control variable to.
fig (matplotlib.figure.Figure): Matplotlib figure window.
fps (float): Animation frames per second.
stepsize (float): Change in control variable value over one frame.
title (str): Animation title; static throughout all frames.
"""
def __init__(self,
fig=graphrc['fig'],
stepsize=None,
fps=graphrc['anim.fps'],
display_current=graphrc['anim.display_current'],
display_current_decimals=graphrc['anim.display_current_decimals'],
title=graphrc['title'],
frame_lim=(0, 0)):
self.fig = fig
self.display_current = display_current
self.display_current_decimals = display_current_decimals
self.stepsize = stepsize
self.fps = fps
self.title = title
self._frame_lim = frame_lim
# Cached animation if already created previously
self._cached_anim = None
def clear(self):
"""Clear persistent data from the last animation."""
self._frame_lim = (0, 0)
self._cached_anim = None
def calc_frame_idx(self, val):
"""Snap a value to the closest corresponding frame. Frames are indexed
as multiples of the stepsize. Note that frame indexes can be either
positive or negative.
Args:
val (float): Value of the frame.
Returns:
int:
Closest frame index corresponding to `val`.
"""
return round(val / self.stepsize)
def calc_frame_val(self, idx):
"""Get the corresponding value of a frame at a specified index, rounded
to the same precision as the stepsize, but with one extra decimal place.
Args:
idx (int): Frame index.
Returns:
float:
Value of the frame.
"""
# Round to the same order of magnitude (one smaller for safety) as the
# stepsize to ensure precision, but to mitigate floating-point error
def order_of_mag(x):
return int(math.floor(math.log10(abs(x))))
return round(idx * self.stepsize, 1-order_of_mag(self.stepsize))
@abstractmethod
def init_func(self):
"""Initialization function for `matplotlib.animation.FuncAnimation`.
Returns:
matplotlib.artist.Artist
Raises:
NotImplementedError:
Abstract method.
"""
raise NotImplementedError
@abstractmethod
def update(self, frame):
"""Update the animation frame.
Args:
frame (int): Index of the frame to draw.
Returns:
matplotlib.artist.Artist
Raises:
NotImplementedError:
Abstract method.
"""
raise NotImplementedError
def get_frame_lim(self):
"""
Returns:
tuple:
The animation frame index limits.
"""
return self._frame_lim
def _get_frame_list(self):
"""Get a full list of all frame values to be played in the animation."""
frame_lim = self.get_frame_lim()
return list(range(frame_lim[0], frame_lim[1]+1))
def animate(self):
"""Synthesize an animation. If an animation was already synthesized and
cached, return the cached animation.
Returns:
matplotlib.animation.FuncAnimation
"""
# Only create a new animation if the cache is empty
if self._cached_anim is None:
self._cached_anim = FuncAnimation(self.fig, self.update,
init_func=self.init_func,
frames=self._get_frame_list(),
interval=1e3/self.fps,
repeat=False)
return self._cached_anim
def show(self):
"""Play the animation in interactive mode."""
plt.show(self.animate())
def save(self, filename, **kwargs):
"""Save the animation to a file.
Kwargs:
fps: Fixed to the animator's `fps` attribute.
"""
kwargs.pop('fps', None) # FPS is fixed
self.animate().save(filename, fps=self.fps, **kwargs)
class WorldlinePlotter(STPlotter):
"""Plotter for creating static spacetime diagrams.
Args:
fig (matplotlib.figure.Figure, optional): See
`specrel.graphics.basegraph.SingleAxisFigureCreator`.
ax (matplotlib.axes.Axes, optional): See
`specrel.graphics.basegraph.SingleAxisFigureCreator`.
others: See attributes.
Attributes:
equal_lim_expand (float): If the limits on an axis are specified to be
equal, they will be expanded symmetrically until the axis size is
this value.
grid (bool): Flag for whether or not to plot background grid lines.
legend (bool): Flag for whether or not to plot a legend.
legend_loc (str): Legend location according to the Matplotlib
`loc` parameter. If `legend` is `False`, this attribute is ignored.
lim_padding (float): Extra padding on spacetime diagram axis limits,
relative to the axis sizes.
"""
def __init__(self,
fig=graphrc['fig'],
ax=graphrc['ax'],
grid=graphrc['grid'],
legend=graphrc['legend'],
legend_loc=graphrc['legend_loc'],
lim_padding=graphrc['worldline.lim_padding'],
equal_lim_expand=graphrc['worldline.equal_lim_expand']):
super().__init__(fig, ax)
self.grid = grid
self.legend = legend
self.legend_loc = legend_loc
self.lim_padding = lim_padding
self.equal_lim_expand = equal_lim_expand
def _prepare_ax(self):
"""Do any axis preparation before drawing stuff."""
self.ax.grid(self.grid)
# Ensure grid lines are behind other objects
self.ax.set_axisbelow(True)
def _set_legend(self):
"""Turn on the legend, or do nothing, depending on self.legend"""
if self.legend:
self.ax.legend(loc=self.legend_loc)
def draw_point(self, point, tag=None, **kwargs):
"""See `specrel.graphics.basegraph.STPlotter.draw_point`.
Kwargs:
linestyle: Forced to be `'None'`
marker: Default is `'.'`.
"""
self._prepare_ax()
marker = kwargs.pop('marker', '.')
# Force linestyle to be none for a point
kwargs['linestyle'] = 'None'
self.ax.plot(point[1], point[0], marker=marker, **kwargs)
if tag:
self.ax.text(point[1], point[0], tag)
self._set_legend()
def draw_line_segment(self, point1, point2, tag=None, **kwargs):
"""See `specrel.graphics.basegraph.STPlotter.draw_line_segment`.
Kwargs:
marker: Forced to be `None`.
"""
self._prepare_ax()
# Ignore the "marker" parameter
kwargs.pop('marker', None)
self.ax.plot((point1[1], point2[1]), (point1[0], point2[0]), **kwargs)
# Put the tag at the midpoint
if tag:
self.ax.text((point1[1] + point2[1])/2, (point1[0] + point2[0])/2,
tag, ha='center', va='center', bbox={'boxstyle': 'round',
'ec': 'black', 'fc': (1, 1, 1, 0.5)})
self._set_legend()
def draw_shaded_polygon(self, vertices, tag=None, **kwargs):
"""See `specrel.graphics.basegraph.STPlotter.draw_shaded_polygon`.
Kwargs:
linewidth: Forced to be `None`.
"""
self._prepare_ax()
tvals, xvals = self._decouple_stvectors(vertices)
# Ignore the "linewidth" parameter
kwargs.pop('linewidth', None)
self.ax.fill(xvals, tvals, **kwargs)
# Put tag at the centroid
if vertices and tag:
self.ax.text(sum(xvals)/len(xvals), sum(tvals)/len(tvals), tag,
ha='center', va='center', bbox={'boxstyle': 'round',
'ec': 'black', 'fc': (1, 1, 1, 0.5)})
self._set_legend()
def set_lims(self, tlim, xlim):
# Pad the limits
tpad = self.lim_padding/2 * (tlim[1] - tlim[0])
xpad = self.lim_padding/2 * (xlim[1] - xlim[0])
# If the limits are identical, expand them
if tlim[0] == tlim[1]:
tpad = self.equal_lim_expand/2
if xlim[0] == xlim[1]:
xpad = self.equal_lim_expand/2
padded_tlim = (tlim[0] - tpad, tlim[1] + tpad)
padded_xlim = (xlim[0] - xpad, xlim[1] + xpad)
# Set the actual limits
self.ax.set_xlim(padded_xlim)
self.ax.set_ylim(padded_tlim)
# Clip lines and polygons objects to the actual limits
clipbox = self.ax.fill([xlim[0], xlim[0], xlim[1], xlim[1]],
[tlim[0], tlim[1], tlim[1], tlim[0]], color='None')[0]
# Only clip lines, not points
for artist in [ln for ln in self.ax.lines if len(ln.get_xdata()) > 1] \
+ self.ax.patches[:-1]:
artist.set_clip_path(clipbox)
self.ax.patches.pop()
def show(self):
plt.show()
def set_labels(self):
"""Set labels for the time and space axes. See
`specrel.graphics.graphrc`.
"""
self.ax.set_xlabel(graphrc['xlabel'])
self.ax.set_ylabel(graphrc['tlabel'])
Classes
class BaseAnimator (fig=None, stepsize=None, fps=50, display_current=True, display_current_decimals=3, title=None, frame_lim=(0, 0))-
Base class for animators that animate over some changing control variable, like time or velocity.
Args
frame_lim:tuple, optional- Minimum and maximum frame index of the
animation. See
BaseAnimator.calc_frame_idx(). others- See attributes.
Attributes
display_current:bool- Flag for displaying the control variable value of the current animation frame.
display_current_decimals:int- Number of decimals to display the current control variable to.
fig:matplotlib.figure.Figure- Matplotlib figure window.
fps:float- Animation frames per second.
stepsize:float- Change in control variable value over one frame.
title:str- Animation title; static throughout all frames.
Source code
class BaseAnimator(ABC): """Base class for animators that animate over some changing control variable, like time or velocity. Args: frame_lim (tuple, optional): Minimum and maximum frame index of the animation. See `BaseAnimator.calc_frame_idx`. others: See attributes. Attributes: display_current (bool): Flag for displaying the control variable value of the current animation frame. display_current_decimals (int): Number of decimals to display the current control variable to. fig (matplotlib.figure.Figure): Matplotlib figure window. fps (float): Animation frames per second. stepsize (float): Change in control variable value over one frame. title (str): Animation title; static throughout all frames. """ def __init__(self, fig=graphrc['fig'], stepsize=None, fps=graphrc['anim.fps'], display_current=graphrc['anim.display_current'], display_current_decimals=graphrc['anim.display_current_decimals'], title=graphrc['title'], frame_lim=(0, 0)): self.fig = fig self.display_current = display_current self.display_current_decimals = display_current_decimals self.stepsize = stepsize self.fps = fps self.title = title self._frame_lim = frame_lim # Cached animation if already created previously self._cached_anim = None def clear(self): """Clear persistent data from the last animation.""" self._frame_lim = (0, 0) self._cached_anim = None def calc_frame_idx(self, val): """Snap a value to the closest corresponding frame. Frames are indexed as multiples of the stepsize. Note that frame indexes can be either positive or negative. Args: val (float): Value of the frame. Returns: int: Closest frame index corresponding to `val`. """ return round(val / self.stepsize) def calc_frame_val(self, idx): """Get the corresponding value of a frame at a specified index, rounded to the same precision as the stepsize, but with one extra decimal place. Args: idx (int): Frame index. Returns: float: Value of the frame. """ # Round to the same order of magnitude (one smaller for safety) as the # stepsize to ensure precision, but to mitigate floating-point error def order_of_mag(x): return int(math.floor(math.log10(abs(x)))) return round(idx * self.stepsize, 1-order_of_mag(self.stepsize)) @abstractmethod def init_func(self): """Initialization function for `matplotlib.animation.FuncAnimation`. Returns: matplotlib.artist.Artist Raises: NotImplementedError: Abstract method. """ raise NotImplementedError @abstractmethod def update(self, frame): """Update the animation frame. Args: frame (int): Index of the frame to draw. Returns: matplotlib.artist.Artist Raises: NotImplementedError: Abstract method. """ raise NotImplementedError def get_frame_lim(self): """ Returns: tuple: The animation frame index limits. """ return self._frame_lim def _get_frame_list(self): """Get a full list of all frame values to be played in the animation.""" frame_lim = self.get_frame_lim() return list(range(frame_lim[0], frame_lim[1]+1)) def animate(self): """Synthesize an animation. If an animation was already synthesized and cached, return the cached animation. Returns: matplotlib.animation.FuncAnimation """ # Only create a new animation if the cache is empty if self._cached_anim is None: self._cached_anim = FuncAnimation(self.fig, self.update, init_func=self.init_func, frames=self._get_frame_list(), interval=1e3/self.fps, repeat=False) return self._cached_anim def show(self): """Play the animation in interactive mode.""" plt.show(self.animate()) def save(self, filename, **kwargs): """Save the animation to a file. Kwargs: fps: Fixed to the animator's `fps` attribute. """ kwargs.pop('fps', None) # FPS is fixed self.animate().save(filename, fps=self.fps, **kwargs)Ancestors
- abc.ABC
Subclasses
Methods
def animate(self)-
Synthesize an animation. If an animation was already synthesized and cached, return the cached animation.
Returns
matplotlib.animation.FuncAnimation
Source code
def animate(self): """Synthesize an animation. If an animation was already synthesized and cached, return the cached animation. Returns: matplotlib.animation.FuncAnimation """ # Only create a new animation if the cache is empty if self._cached_anim is None: self._cached_anim = FuncAnimation(self.fig, self.update, init_func=self.init_func, frames=self._get_frame_list(), interval=1e3/self.fps, repeat=False) return self._cached_anim def calc_frame_idx(self, val)-
Snap a value to the closest corresponding frame. Frames are indexed as multiples of the stepsize. Note that frame indexes can be either positive or negative.
Args
val:float- Value of the frame.
Returns
int:- Closest frame index corresponding to
val.
Source code
def calc_frame_idx(self, val): """Snap a value to the closest corresponding frame. Frames are indexed as multiples of the stepsize. Note that frame indexes can be either positive or negative. Args: val (float): Value of the frame. Returns: int: Closest frame index corresponding to `val`. """ return round(val / self.stepsize) def calc_frame_val(self, idx)-
Get the corresponding value of a frame at a specified index, rounded to the same precision as the stepsize, but with one extra decimal place.
Args
idx:int- Frame index.
Returns
float:- Value of the frame.
Source code
def calc_frame_val(self, idx): """Get the corresponding value of a frame at a specified index, rounded to the same precision as the stepsize, but with one extra decimal place. Args: idx (int): Frame index. Returns: float: Value of the frame. """ # Round to the same order of magnitude (one smaller for safety) as the # stepsize to ensure precision, but to mitigate floating-point error def order_of_mag(x): return int(math.floor(math.log10(abs(x)))) return round(idx * self.stepsize, 1-order_of_mag(self.stepsize)) def clear(self)-
Clear persistent data from the last animation.
Source code
def clear(self): """Clear persistent data from the last animation.""" self._frame_lim = (0, 0) self._cached_anim = None def get_frame_lim(self)-
Returns
tuple:- The animation frame index limits.
Source code
def get_frame_lim(self): """ Returns: tuple: The animation frame index limits. """ return self._frame_lim def init_func(self)-
Initialization function for
matplotlib.animation.FuncAnimation.Returns
matplotlib.artist.Artist
Raises
NotImplementedError:- Abstract method.
Source code
@abstractmethod def init_func(self): """Initialization function for `matplotlib.animation.FuncAnimation`. Returns: matplotlib.artist.Artist Raises: NotImplementedError: Abstract method. """ raise NotImplementedError def save(self, filename, **kwargs)-
Save the animation to a file.
Kwargs
fps- Fixed to the animator's
fpsattribute.
Source code
def save(self, filename, **kwargs): """Save the animation to a file. Kwargs: fps: Fixed to the animator's `fps` attribute. """ kwargs.pop('fps', None) # FPS is fixed self.animate().save(filename, fps=self.fps, **kwargs) def show(self)-
Play the animation in interactive mode.
Source code
def show(self): """Play the animation in interactive mode.""" plt.show(self.animate()) def update(self, frame)-
Update the animation frame.
Args
frame:int- Index of the frame to draw.
Returns
matplotlib.artist.Artist
Raises
NotImplementedError:- Abstract method.
Source code
@abstractmethod def update(self, frame): """Update the animation frame. Args: frame (int): Index of the frame to draw. Returns: matplotlib.artist.Artist Raises: NotImplementedError: Abstract method. """ raise NotImplementedError
class FigureCreator-
Something that can create and own a figure.
Attributes
fig:matplotlib.figure.Figure- Figure handle.
Source code
class FigureCreator: """Something that can create and own a figure. Attributes: fig (matplotlib.figure.Figure): Figure handle. """ def __init__(self): self.fig = None self._created_own_fig = False def close(self): """Close the figure window if it was created by the class.""" if self._created_own_fig: plt.close(self.fig)Subclasses
Methods
def close(self)-
Close the figure window if it was created by the class.
Source code
def close(self): """Close the figure window if it was created by the class.""" if self._created_own_fig: plt.close(self.fig)
class STPlotter (fig=None, ax=None)-
Base-level plotting interface.
Source code
class STPlotter(SingleAxisFigureCreator, ABC): """Base-level plotting interface.""" @abstractmethod def draw_point(self, point, tag, **kwargs): """Draws a single spacetime point. Args: point (specrel.geom.STVector): Point to draw. tag (str): Tag to draw with the point. **kwargs: Matplotlib plot keyword arguments. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError @abstractmethod def draw_line_segment(self, point1, point2, tag, **kwargs): """Draws a line segment between two spacetime points. Args: point1 (specrel.geom.STVector): One line segment endpoint. point2 (specrel.geom.STVector): Other line segment endpoint. tag (str): Tag to draw with the point. **kwargs: Matplotlib plot keyword arguments. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError @abstractmethod def draw_shaded_polygon(self, vertices, tag, **kwargs): """Draws a shaded polygon in spacetime. Args: vertices (list): List of `specrel.geom.STVector` objects defining the polygon vertices. tag (str): Tag to draw with the point. **kwargs: Matplotlib plot keyword arguments. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError @staticmethod def _decouple_stvectors(stvectors): """Separate time and space values in a list of STVectors.""" if stvectors: return tuple(zip(*stvectors)) return tuple(), tuple() @abstractmethod def set_lims(self, tlim, xlim): """Sets the plotting limits for the plotter. Args: tlim (tuple): Minimum and maximum time values of the plot. `None` values are filled automatically. xlim (tuple): Minimum and maximum position values of the plot. `None` values are filled automatically. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError @abstractmethod def show(self): """Shows the plot in interactive mode. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError def save(self, filename, **kwargs): """Saves the plot to a file. Args: filename (str): Output file name. **kwargs: Keyword arguments to `matplotlib.figure.Figure.savefig`. """ self.fig.savefig(filename, **kwargs)Ancestors
- SingleAxisFigureCreator
- FigureCreator
- abc.ABC
Subclasses
Methods
def draw_line_segment(self, point1, point2, tag, **kwargs)-
Draws a line segment between two spacetime points.
Args
point1:STVector- One line segment endpoint.
point2:STVector- Other line segment endpoint.
tag:str- Tag to draw with the point.
**kwargs- Matplotlib plot keyword arguments.
Raises
NotImplementedError:- Abstract method.
Source code
@abstractmethod def draw_line_segment(self, point1, point2, tag, **kwargs): """Draws a line segment between two spacetime points. Args: point1 (specrel.geom.STVector): One line segment endpoint. point2 (specrel.geom.STVector): Other line segment endpoint. tag (str): Tag to draw with the point. **kwargs: Matplotlib plot keyword arguments. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError def draw_point(self, point, tag, **kwargs)-
Draws a single spacetime point.
Args
point:STVector- Point to draw.
tag:str- Tag to draw with the point.
**kwargs- Matplotlib plot keyword arguments.
Raises
NotImplementedError:- Abstract method.
Source code
@abstractmethod def draw_point(self, point, tag, **kwargs): """Draws a single spacetime point. Args: point (specrel.geom.STVector): Point to draw. tag (str): Tag to draw with the point. **kwargs: Matplotlib plot keyword arguments. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError def draw_shaded_polygon(self, vertices, tag, **kwargs)-
Draws a shaded polygon in spacetime.
Args
vertices:list- List of
STVectorobjects defining the polygon vertices. tag:str- Tag to draw with the point.
**kwargs- Matplotlib plot keyword arguments.
Raises
NotImplementedError:- Abstract method.
Source code
@abstractmethod def draw_shaded_polygon(self, vertices, tag, **kwargs): """Draws a shaded polygon in spacetime. Args: vertices (list): List of `specrel.geom.STVector` objects defining the polygon vertices. tag (str): Tag to draw with the point. **kwargs: Matplotlib plot keyword arguments. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError def save(self, filename, **kwargs)-
Saves the plot to a file.
Args
filename:str- Output file name.
**kwargs- Keyword arguments to
matplotlib.figure.Figure.savefig.
Source code
def save(self, filename, **kwargs): """Saves the plot to a file. Args: filename (str): Output file name. **kwargs: Keyword arguments to `matplotlib.figure.Figure.savefig`. """ self.fig.savefig(filename, **kwargs) def set_lims(self, tlim, xlim)-
Sets the plotting limits for the plotter.
Args
tlim:tuple- Minimum and maximum time values of the plot.
Nonevalues are filled automatically. xlim:tuple- Minimum and maximum position values of the plot.
Nonevalues are filled automatically.
Raises
NotImplementedError:- Abstract method.
Source code
@abstractmethod def set_lims(self, tlim, xlim): """Sets the plotting limits for the plotter. Args: tlim (tuple): Minimum and maximum time values of the plot. `None` values are filled automatically. xlim (tuple): Minimum and maximum position values of the plot. `None` values are filled automatically. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError def show(self)-
Shows the plot in interactive mode.
Raises
NotImplementedError:- Abstract method.
Source code
@abstractmethod def show(self): """Shows the plot in interactive mode. Raises: NotImplementedError: Abstract method. """ raise NotImplementedError
Inherited members
class SingleAxisFigureCreator (fig=None, ax=None)-
Something that creates and owns a figure with a single axis (i.e. not a subplot).
Args
fig:matplotlib.figure.Figure, optional- Figure window. If
Noneandaxis notNone, will be set toax.figure. If both areNone, a new figure will be created. ax:matplotlib.axes.Axes, optional- Axes to draw on. If
Noneandfigis notNone, will be set tofig.gca(). If both areNone, a new set of axes will be created.
Attributes
ax:matplotlib.axes.Axes- Axes to draw on.
fig:matplotlib.figure.Figure- Figure window.
Source code
class SingleAxisFigureCreator(FigureCreator): """Something that creates and owns a figure with a single axis (i.e. not a subplot). Args: fig (matplotlib.figure.Figure, optional): Figure window. If `None` and `ax` is not `None`, will be set to `ax.figure`. If both are `None`, a new figure will be created. ax (matplotlib.axes.Axes, optional): Axes to draw on. If `None` and `fig` is not `None`, will be set to `fig.gca()`. If both are `None`, a new set of axes will be created. Attributes: ax (matplotlib.axes.Axes): Axes to draw on. fig (matplotlib.figure.Figure): Figure window. """ def __init__(self, fig=graphrc['fig'], ax=graphrc['ax']): super().__init__() if fig is None and ax is None: self.fig, self.ax = plt.subplots() self._created_own_fig = True elif fig is not None and ax is None: self.fig = fig self.ax = fig.gca() elif fig is None and ax is not None: self.fig = ax.figure self.ax = ax else: self.fig = fig self.ax = axAncestors
Subclasses
Inherited members
class WorldlinePlotter (fig=None, ax=None, grid=False, legend=False, legend_loc='best', lim_padding=0.1, equal_lim_expand=1)-
Plotter for creating static spacetime diagrams.
Args
fig:matplotlib.figure.Figure, optional- See
SingleAxisFigureCreator. ax:matplotlib.axes.Axes, optional- See
SingleAxisFigureCreator. others- See attributes.
Attributes
equal_lim_expand:float- If the limits on an axis are specified to be equal, they will be expanded symmetrically until the axis size is this value.
grid:bool- Flag for whether or not to plot background grid lines.
legend:bool- Flag for whether or not to plot a legend.
legend_loc:str- Legend location according to the Matplotlib
locparameter. IflegendisFalse, this attribute is ignored. lim_padding:float- Extra padding on spacetime diagram axis limits, relative to the axis sizes.
Source code
class WorldlinePlotter(STPlotter): """Plotter for creating static spacetime diagrams. Args: fig (matplotlib.figure.Figure, optional): See `specrel.graphics.basegraph.SingleAxisFigureCreator`. ax (matplotlib.axes.Axes, optional): See `specrel.graphics.basegraph.SingleAxisFigureCreator`. others: See attributes. Attributes: equal_lim_expand (float): If the limits on an axis are specified to be equal, they will be expanded symmetrically until the axis size is this value. grid (bool): Flag for whether or not to plot background grid lines. legend (bool): Flag for whether or not to plot a legend. legend_loc (str): Legend location according to the Matplotlib `loc` parameter. If `legend` is `False`, this attribute is ignored. lim_padding (float): Extra padding on spacetime diagram axis limits, relative to the axis sizes. """ def __init__(self, fig=graphrc['fig'], ax=graphrc['ax'], grid=graphrc['grid'], legend=graphrc['legend'], legend_loc=graphrc['legend_loc'], lim_padding=graphrc['worldline.lim_padding'], equal_lim_expand=graphrc['worldline.equal_lim_expand']): super().__init__(fig, ax) self.grid = grid self.legend = legend self.legend_loc = legend_loc self.lim_padding = lim_padding self.equal_lim_expand = equal_lim_expand def _prepare_ax(self): """Do any axis preparation before drawing stuff.""" self.ax.grid(self.grid) # Ensure grid lines are behind other objects self.ax.set_axisbelow(True) def _set_legend(self): """Turn on the legend, or do nothing, depending on self.legend""" if self.legend: self.ax.legend(loc=self.legend_loc) def draw_point(self, point, tag=None, **kwargs): """See `specrel.graphics.basegraph.STPlotter.draw_point`. Kwargs: linestyle: Forced to be `'None'` marker: Default is `'.'`. """ self._prepare_ax() marker = kwargs.pop('marker', '.') # Force linestyle to be none for a point kwargs['linestyle'] = 'None' self.ax.plot(point[1], point[0], marker=marker, **kwargs) if tag: self.ax.text(point[1], point[0], tag) self._set_legend() def draw_line_segment(self, point1, point2, tag=None, **kwargs): """See `specrel.graphics.basegraph.STPlotter.draw_line_segment`. Kwargs: marker: Forced to be `None`. """ self._prepare_ax() # Ignore the "marker" parameter kwargs.pop('marker', None) self.ax.plot((point1[1], point2[1]), (point1[0], point2[0]), **kwargs) # Put the tag at the midpoint if tag: self.ax.text((point1[1] + point2[1])/2, (point1[0] + point2[0])/2, tag, ha='center', va='center', bbox={'boxstyle': 'round', 'ec': 'black', 'fc': (1, 1, 1, 0.5)}) self._set_legend() def draw_shaded_polygon(self, vertices, tag=None, **kwargs): """See `specrel.graphics.basegraph.STPlotter.draw_shaded_polygon`. Kwargs: linewidth: Forced to be `None`. """ self._prepare_ax() tvals, xvals = self._decouple_stvectors(vertices) # Ignore the "linewidth" parameter kwargs.pop('linewidth', None) self.ax.fill(xvals, tvals, **kwargs) # Put tag at the centroid if vertices and tag: self.ax.text(sum(xvals)/len(xvals), sum(tvals)/len(tvals), tag, ha='center', va='center', bbox={'boxstyle': 'round', 'ec': 'black', 'fc': (1, 1, 1, 0.5)}) self._set_legend() def set_lims(self, tlim, xlim): # Pad the limits tpad = self.lim_padding/2 * (tlim[1] - tlim[0]) xpad = self.lim_padding/2 * (xlim[1] - xlim[0]) # If the limits are identical, expand them if tlim[0] == tlim[1]: tpad = self.equal_lim_expand/2 if xlim[0] == xlim[1]: xpad = self.equal_lim_expand/2 padded_tlim = (tlim[0] - tpad, tlim[1] + tpad) padded_xlim = (xlim[0] - xpad, xlim[1] + xpad) # Set the actual limits self.ax.set_xlim(padded_xlim) self.ax.set_ylim(padded_tlim) # Clip lines and polygons objects to the actual limits clipbox = self.ax.fill([xlim[0], xlim[0], xlim[1], xlim[1]], [tlim[0], tlim[1], tlim[1], tlim[0]], color='None')[0] # Only clip lines, not points for artist in [ln for ln in self.ax.lines if len(ln.get_xdata()) > 1] \ + self.ax.patches[:-1]: artist.set_clip_path(clipbox) self.ax.patches.pop() def show(self): plt.show() def set_labels(self): """Set labels for the time and space axes. See `specrel.graphics.graphrc`. """ self.ax.set_xlabel(graphrc['xlabel']) self.ax.set_ylabel(graphrc['tlabel'])Ancestors
Methods
def draw_line_segment(self, point1, point2, tag=None, **kwargs)-
See
STPlotter.draw_line_segment().Kwargs
marker- Forced to be
None.
Source code
def draw_line_segment(self, point1, point2, tag=None, **kwargs): """See `specrel.graphics.basegraph.STPlotter.draw_line_segment`. Kwargs: marker: Forced to be `None`. """ self._prepare_ax() # Ignore the "marker" parameter kwargs.pop('marker', None) self.ax.plot((point1[1], point2[1]), (point1[0], point2[0]), **kwargs) # Put the tag at the midpoint if tag: self.ax.text((point1[1] + point2[1])/2, (point1[0] + point2[0])/2, tag, ha='center', va='center', bbox={'boxstyle': 'round', 'ec': 'black', 'fc': (1, 1, 1, 0.5)}) self._set_legend() def draw_point(self, point, tag=None, **kwargs)-
Kwargs
linestyle- Forced to be
'None' marker- Default is
'.'.
Source code
def draw_point(self, point, tag=None, **kwargs): """See `specrel.graphics.basegraph.STPlotter.draw_point`. Kwargs: linestyle: Forced to be `'None'` marker: Default is `'.'`. """ self._prepare_ax() marker = kwargs.pop('marker', '.') # Force linestyle to be none for a point kwargs['linestyle'] = 'None' self.ax.plot(point[1], point[0], marker=marker, **kwargs) if tag: self.ax.text(point[1], point[0], tag) self._set_legend() def draw_shaded_polygon(self, vertices, tag=None, **kwargs)-
See
STPlotter.draw_shaded_polygon().Kwargs
linewidth- Forced to be
None.
Source code
def draw_shaded_polygon(self, vertices, tag=None, **kwargs): """See `specrel.graphics.basegraph.STPlotter.draw_shaded_polygon`. Kwargs: linewidth: Forced to be `None`. """ self._prepare_ax() tvals, xvals = self._decouple_stvectors(vertices) # Ignore the "linewidth" parameter kwargs.pop('linewidth', None) self.ax.fill(xvals, tvals, **kwargs) # Put tag at the centroid if vertices and tag: self.ax.text(sum(xvals)/len(xvals), sum(tvals)/len(tvals), tag, ha='center', va='center', bbox={'boxstyle': 'round', 'ec': 'black', 'fc': (1, 1, 1, 0.5)}) self._set_legend() def set_labels(self)-
Set labels for the time and space axes. See
graphrc.Source code
def set_labels(self): """Set labels for the time and space axes. See `specrel.graphics.graphrc`. """ self.ax.set_xlabel(graphrc['xlabel']) self.ax.set_ylabel(graphrc['tlabel'])
Inherited members